Results 1 to 10 of 10

Thread: OneToMany bi-directional relationship - OneToMany set is always empty

  1. #1
    Join Date
    Jan 2009
    Posts
    29

    Default OneToMany bi-directional relationship - OneToMany set is always empty

    I have a OneToMany, bi-directional relationship set up with Roo/JPA. I am using the following:

    Spring Roo 1.1.5.RELEASE
    EclipseLink
    MySQL 5.5.14
    Java 1.6.0_23
    Windows 7

    My relationship is defined as follows:

    Code:
    @RooJavaBean
    @RooToString
    public class Baby {
    
        @NotNull
        @ManyToOne
        private Mother mother;
    }
    The bi-directional relationship is defined by:

    Code:
    @RooJavaBean
    @RooToString
    public class Mother{
    
        @OneToMany(cascade = {CascadeType.REMOVE }, mappedBy = "mother", fetch =  FetchType.EAGER)
        @OrderBy("sequence")
        Set<Baby> babies= new TreeSet<Baby>();
    }
    Any time I use a Roo finder method to fetch a Mother from the database, the set of baby objects is always null. However, fetching a Baby from the database always has the Mother member variable set correctly. I have tried FetchType.EAGER and FetchType.LAZY. For example, the following log statement always outputs zero:

    Code:
    Mother m = Mother.findMother(id);
    
    logger.info("mother has {} children", m.getBabies().size());
    Thanks, any help is appreciated.

  2. #2
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    668

    Lightbulb

    Try using PersistenceUnitUtil to check whether the Set has actually been initialised, as explained here.

    Some non-Roo-standard things I noticed about your code (might be irrelevant):

    • no @RooEntity annotations (presumably you have these in your real code)
    • the Set is not private
    • the Set is a TreeSet, not a HashSet (might be relevant)

    Also try turning on SQL debug mode to see what SQL queries EclipseLink is actually sending to the database.

  3. #3
    Join Date
    Jan 2009
    Posts
    29

    Default

    Hi Andrew, thanks for your reply. Following your suggestions:

    • Used PersistenceUnitUtil in my integration test, but to no avail
    • @RooEntity is definitely set in my real code
    • Made Set private like my other fields
    • Changed Set from TreeSet to HashSet


    I still was not getting the Set populated in my integration test, so I enabled SQL debugging as you suggested. This shed some light on things, since I was not seeing the SELECT statement when calling the Roo-generated Mother.findMother(long id) method to fetch the Mother object that I had previously persisted in the test. This has led me to believe that some caching mechanism is in effect with EclipseLink, so I disabled caching for my entity as follows:

    Code:
    @Cache (
    	type=CacheType.WEAK,
    	isolation=CacheIsolationType.SHARED,
    	expiry=60000,
    	alwaysRefresh=true,
    	disableHits=true,
    	coordinationType=CacheCoordinationType.INVALIDATE_CHANGED_OBJECTS
    )
    public class Mother { ... }
    I have also added the following option to persistence.xml:

    Code:
    <property name="eclipselink.query-results-cache" value="false"/>
    So, either I still am not configuring my EclipseLink cache correctly, or there is some other reason why Mother.findMother(long id) is not returning the set of children in the returned Mother object. Here is the code from my integration test:

    Code:
    Mother mom = new Mother();
    mom.persist();
    mom.flush()
    
    // mom is correctly inserted
    Assert.assertNotNull("mom id should not be null", mom.getId());
    
    Baby child = new Baby();
    child.setMother(mom);
    
    // child is correctly inserted, referencing mom
    child = child.merge();
    
    Assert.assertNotNull("child id should not be null", child.getId());
    
    Mother freshMom = Mother.findMother(mom.getId());
    Assert.assertNotNull("freshMom should not be null", freshMom);
    Assert.assertEquals(mom, freshMom);  // this passes... I would think it should not
    
    Assert.assertTrue("freshMom should have at least one child", freshMom.getBabies().size() > 0); // this fails, I would expect it to pass since I just added it
    Is there still an issue my caching, or is there another reason why the Mother.babies Set is not being populated?

    Thanks.

  4. #4
    Join Date
    Jul 2006
    Location
    Houston, TX
    Posts
    80

    Default

    I am having a similar issue. When I list the the parent objects in the default Roo-generated entity listing, it does not show values for the child entities.
    When the relationship is many-to-many, this works just fine, and the parent listing will show the child objects. I have not confirmed if the child objects are actually there in memory and just not showing in the UI, but I suspect they aren't. I suspect the Set is null on the parent side.

  5. #5
    Join Date
    Jul 2006
    Location
    Houston, TX
    Posts
    80

    Default

    My bad. The child data is there in memory, but its just not appearing in the list.

  6. #6
    Join Date
    Jul 2006
    Location
    Houston, TX
    Posts
    80

    Question OneToMany set IS empty after all

    After running the following Roo script:


    project --topLevelPackage com.sandbox.roo --projectName roo-sandbox --java 6
    persistence setup --database H2_IN_MEMORY --provider HIBERNATE
    web mvc setup
    entity --class ~.domain.Department --testAutomatically
    entity --class com.sandbox.roo.domain.Employee --testAutomatically
    field reference --fieldName department --type com.sandbox.roo.domain.Department --cardinality MANY_TO_ONE --fetch EAGER
    focus --class ~.domain.Department
    field set --fieldName employees --type com.sandbox.roo.domain.Employee --cardinality ONE_TO_MANY
    field string --fieldName name --notNull
    focus --class com.sandbox.roo.domain.Employee
    field string --fieldName name --notNull
    web mvc all --package ~.web
    perform eclipse


    You get a basic project consisting of Departments and Employees.
    There is a One-To-Many relationship between Department and Employee and the fetch type is eager.

    When I list the Departments, the Employees do not show up in the listing.

    If I set a breakpoint in the controller, I can see that the Employee Set is not populated in the Department object after a call to entityManager.find(). The fetch type is EAGER, so why aren't the employees populated when the Department object is returned?

    See attached image for an example of what is displayed.
    Attached Images Attached Images

  7. #7
    Join Date
    Jul 2006
    Location
    Houston, TX
    Posts
    80

    Wink Problem Solved

    The solution was to use mappedBy on the Department object. I haven't used JPA in a while.


    package com.sandbox.roo.domain;

    import org.springframework.roo.addon.entity.RooEntity;
    import org.springframework.roo.addon.javabean.RooJavaBean ;
    import org.springframework.roo.addon.tostring.RooToString ;
    import java.util.Set;
    import com.sandbox.roo.domain.Employee;
    import java.util.HashSet;

    import javax.persistence.FetchType;
    import javax.persistence.OneToMany;
    import javax.persistence.CascadeType;
    import javax.validation.constraints.NotNull;

    @RooJavaBean
    @RooToString
    @RooEntity
    public class Department {

    @OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER,mappedBy="department")
    private Set<Employee> employees = new HashSet<Employee>();

    @NotNull
    private String name;
    }

  8. #8
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    668

    Default

    Glad you solved your problem, however jporche already has the "mappedBy" attribute, and it doesn't work for them. Anyone still having this issue should log a JIRA ticket with a minimal example project that demonstrates the problem.

  9. #9
    Join Date
    Sep 2011
    Location
    United Kingdom
    Posts
    2

    Default Both sides of the relation count

    Hope this is not my misunderstanding of the Active Record pattern, but traditionally one would have to satisfy both sides of the JPA relation.

    JPorche, in their test, was only setting one side.

    Here is a complete example in which the tests pass:

    project --topLevelPackage mvm.mums --projectName mums --java 6
    persistence setup --database HYPERSONIC_PERSISTENT --provider HIBERNATE
    entity --class ~.model.Baby --testAutomatically
    entity --class ~.model.Mother --testAutomatically
    field set --fieldName babies --type ~.model.Baby --cardinality ONE_TO_MANY --mappedBy mother
    focus --class ~.model.Baby
    field reference --fieldName mother --type ~.model.Mother --cardinality MANY_TO_ONE

    Code:
        @Transactional
        @Test
        public void tryMe() {
        	Mother mum = new Mother();
        	Baby babs = new Baby();
        	
        	// We must satisfy both sides of bi-directional relation
        	babs.setMother(mum);
        	mum.getBabies().add(babs);
        	
        	mum.persist();
        	
        	Mother newMumBefore = Mother.findMother(mum.getId());
        	Assert.isTrue(newMumBefore.getBabies().size() == 1);
        	
        	mum.flush();
        	mum.clear();
        	
        	Mother newMumAfter = Mother.findMother(mum.getId());
        	
        	Assert.isTrue(newMumAfter.getBabies().size() == 1);
        }
    You could add convenience methods to your entities that take care of this for you, for example the Mother entity:

    Code:
    @RooJavaBean
    @RooToString
    @RooEntity
    public class Mother {
    
        @OneToMany(cascade = CascadeType.ALL, mappedBy = "mother")
        private Set<Baby> babies = new HashSet<Baby>();
        
        public void addBaby(Baby babs) {
        	babs.setMother(this);
        	getBabies().add(babs);
        }
    
    }
    Hope this helped, found myself scratching my scalp over same problems.

    M.

  10. #10
    Join Date
    Nov 2012
    Posts
    2

    Default Related Question: Field set @NotEmpty

    (yet another newbie using roo)

    I want to add just a validations rule (@NotEmpty or @NotNull or whatever says that this collections cannot be empty) to a field set of the entity Tagging, but nothing works...

    Code:
    @RooJavaBean
    @RooToString
    @RooJpaActiveRecord(table = "taggings")
    @RooJson
    @RooSolrSearchable
    public class Tagging extends Event {
    
        @NotNull
        @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "tagging")
        private Set<Tag> tags = new HashSet<Tag>();
    Also to mention that Tagging extends Event as you can see...

    Any help/comment more than welcome!!
    Thanks,
    GK

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •