How would I map a parent/child relation on same object with JPA
Asked Answered
C

3

8

After reading this post JPA map relation entity parentID I tried applying this to my code but this didn't work for me.

This is the code I have in my Object

@Entity
public class Category extends Model {

public static final int EASY = 1;
public static final int MEDIUM = 2;
public static final int HARD = 3;
public static final int VERRY_HARD = 4;

public String name;
public String fullName;
public boolean active;
public Date createdOn;
public int difficulty;

@ManyToOne
@JoinColumn(name = "FK_PARENT_CATEGORY")
public Category parentCategory;

@OneToMany(mappedBy="parentCategory", cascade = CascadeType.ALL)
public List<Category> subCategories;

public Category(Category parentCategory, String name, boolean active) {
    this.name = name;
    this.active = active;
    this.parentCategory = parentCategory;
    this.subCategories = new ArrayList<Category>();
    this.createdOn = new Date();
    this.difficulty = Category.EASY;
    this.fullName = name;
    if (parentCategory != null)
        this.fullName = parentCategory.fullName + "/" + this.fullName;

}

Now this is the test I run

@Test
public void testParentAndSubCategories() {

    //Create the parent category
    new Category(null, "Sport", true).save();
    Category sportCat = Category.find("byName", "Sport").first();

    //Test the newly created parent category state
    assertNotNull(sportCat);
    assertEquals("Sport", sportCat.name);
    assertEquals(true, sportCat.active);
    assertEquals("Sport", sportCat.fullName);
    assertNull(sportCat.parentCategory);
    assertEquals(0, sportCat.subCategories.size());

    //Create the subCategory 
    new Category(sportCat, "Hockey", false).save();
    Category hockeyCat = Category.find("byName", "Hockey").first();

    // Test the newly created sub category
    assertNotNull(hockeyCat);
    assertEquals("Hockey", hockeyCat.name);
    assertEquals(false, hockeyCat.active);
    assertEquals("Sport/Hockey", hockeyCat.fullName);
    assertNotNull(hockeyCat.parentCategory);
    assertEquals("Sport", hockeyCat.parentCategory.name);
    assertEquals(0, sportCat.subCategories.size());

    //Fetch new values for parent category
    sportCat = Category.find("byName", "Sport").first();

    // Test updated parent category
    assertEquals(1, sportCat.subCategories.size());
    assertEquals("Hockey", sportCat.subCategories.get(0).name);

}

This line of the test always fail.

// Test updated parent category
    assertEquals(1, sportCat.subCategories.size());

Based on the setup I have for my relations, Hibernate can't retrieve the subcategory and I don't know why. Now I really really hope this is not something stupid on my part because I'm going to have shoot myself (Even if it's late and I'm tired). By the way don't mind the public variables in the code, I'm using play!(playframework) and it takes care of encapsulation. Thanks in advance for any help

Cilium answered 9/12, 2010 at 5:11 Comment(0)
P
6

It is not a problem of mapping parent and child to the same class. - The problem is that you need to maintain both ends of the bi-directional-relationship by hand.

child.setParent(parent)
parent.addChild(child)

BTW: Setting it only on one side (the one which is responsible to store the relationship in the database), store and reload the entity will work in some cases too. (And you will find this dirty trick in many old tutorials). But in my opinion it is bad practice. (In your test case, it would require to clean the cache before you reload the parent after the child is saved.)

Poulter answered 9/12, 2010 at 11:23 Comment(1)
This is exactly what I did. I added a method to add a subcategory to a parent category. I modified my test accordingly and it works exactly as I want now. Thank you very much Ralph. And thanks to everyone else for there answers.Cilium
G
3

The code that is shown in your question doesn't add anything to subCategories. It only initialize it with an empty list. I think you need something like

if (parentCategory != null) {
    parentCategory.subCategories.add(this);
}

So, as I understand (and read) your code correctly, Hibernate doesn't (cannot) retrieve the subcategories because it's stays empty after calling new Category(sportCat, "Hockey", false).save();

Galateah answered 9/12, 2010 at 5:27 Comment(0)
D
0

You were probably expecting to be enough to add the parent entity to the child, but from what I've seen, Hibernate requires you to maintain both ends of a one-to-many relationship.

In other words, you have to add the child Category to the parent's list of categories.

It's described here: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/example-parentchild.html#example-parentchild-bidir

And also here (with an extra caching issue): Hibernate @OneToMany with mappedBy (parent-child) relationship and cache problem

Downwash answered 9/12, 2010 at 8:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.