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

17,451

Solution 1

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.)

Solution 2

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();

Share:
17,451
Alain
Author by

Alain

Updated on June 09, 2022

Comments

  • Alain
    Alain almost 2 years

    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

  • Alain
    Alain over 13 years
    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.