How to debug "Found two representations of same collection"?

16,584

Solution 1

As far as I've been able to tell, the error is caused by any combination of:

  • Lacking / missing mappedBy parameter on @OneToMany annotations. This parameter should receive the name of the field in the target model that refers back to this model.
  • Old hibernate - Play 1.2.4 ships with hibernate 3.6.1 ... upgrading to 3.6.8 seems to resolve another such issue (just add the following to dependencies.yml, and play deps)

- org.hibernate -> hibernate-core 3.6.8.Final:

force: true

For me, the above steps solved the issue.

It is in fact a bug in hibernate, because it is thrown when persisting objects, while it actually implies a "design time" problem that should be detected when creating the schema.

Steps I used to debug:

  • Wrote a test that reproduced the problem
  • Added the associations module - I'm not sure if it resolved a part of the issue, or made it worse.
  • Debugged through hibernate code, and realized this probably indicates a hibernate problem, not a user / configuration error.
  • Noticed that hibernate has quite a few bugfix versions after 3.6.1, and decided to try my luck and upgrade.
  • Also important, cleaning the tmp folder can't hurt - Play caches compiled jars there, and after a major change like upgrading hibernate version, it might be worthwhile to clean it.

Solution 2

  Try

        @OneToMany(mappedBy="position")
        public List<Project> projects;

Solution 3

First I think you miss a line one before the last:

joe.positions.add(joeAtTwitter);

Second: I think that you should not do

joe.positions = new ArrayList<Position>();

instead change Person to:

@Entity
public class Person extends Model {
    public String name;

    @OneToMany(cascade = CascadeType.ALL)
    public List<Position> positions = new ArrayList<Position>();
}

It will solve your problem, plus it's a best practice, using empty collection instead of null value (see Effective Java) at general and specifically for working with Hibernate managed objects. Read first paragraph here for explanation why you better initialize with empty collections.

Now what I think is happened is: when you call joe.save() you have made the object managed (by Hibernate) then you overwritten a property with a new collection, I can't understand why the error you got is about model.Position.projects, but I think that's the case.

Share:
16,584
ripper234
Author by

ripper234

See blog or LinkedIn Profile

Updated on June 07, 2022

Comments

  • ripper234
    ripper234 almost 2 years

    I have found several questions about this, but none with a complete explaintation of the problem, and how to debug it - the answers are all anecdotal.

    The problem is that in a Play 1.2.4 JPA test, I'm getting this exception when I save() a model:

    org.hibernate.HibernateException: Found two representations of same collection: models.Position.projects

    I would like to know:

    1. Is there a documentation of this problem in general, unrelated to Play? The issue is in hibernate, yet a lot of the Google results on this are within Play apps.
    2. What are some basic best practices to avoid this problem?
    3. Is it caused by Play? Or something I'm doing wrong?
    4. How to resolve in my specific case?

    Here is a reproduction of the problem on github. I have four entities:

    @Entity
    public class Person extends Model {
        public String name;
    
        @OneToMany(cascade = CascadeType.ALL)
        public List<Position> positions;
    }
    
    
    @Entity
    public class Position extends Model {
        public Position(){}
        public Position(Company companies) {
            this.companies = companies;
            this.projects = new ArrayList<Project>();
        }
    
        @OneToOne
        public Company companies;
    
        @ManyToOne
        public Person person;
    
        @OneToMany
        public List<Project> projects;
    }
    
    @Entity
    public class Company extends Model {
        public String name;
    }
    
    @Entity
    public class Project extends Model {
        public Project(){}
        public Project(String field, String status){
            this.theField = field;
            this.status = status;
        }
    
        @ManyToOne
        public Position position;
    
        public String theField;
        public String status;
    }
    

    And my persistence code:

    Company facebook = new Company();
    facebook.name = "Facebook";
    facebook.save();
    Company twitter = new Company();
    twitter.name = "Twitter";
    twitter.save();
    
    Person joe = new Person();
    joe.name = "Joe";
    joe.save();
    
    joe.positions = new ArrayList<Position>();
    
    Position joeAtFacebook = new Position(facebook);
    joeAtFacebook.projects.add(new Project("Stream", "Architect"));
    joeAtFacebook.projects.add(new Project("Messages", "Lead QA"));
    joe.positions.add(joeAtFacebook);
    
    Position joeAtTwitter = new Position(twitter);
    joeAtTwitter.projects.add(new Project("Steal stuff from Facebook", "CEO"));
    joe.positions.add(joeAtTwitter);
    joe.save();
    

    BTW, I've tried adding the Play associations module as one person suggested, and it does't seem to help.

    I see that indeed that tables that are created are duplicate in a sense:

    I have both a person_position table and a position table, where both contain similar fields: person_position contains a Person_id and positions_id, while the position table contain id (meaning position id), person_id, and companies_id. So I understand some kind of unintended redundancy is created by my model definition, but I don't really understand how to solve it.

    I thought this might be related to bi-directional mappings, but here is a branch where the model is uni-directional (I removed some back-references) - and the problem still occurs.

    • Heartless
      Heartless over 12 years
      my guess - cascading problem. you add a Position, and the position adds two projects, then joe.save() might cause adding the position twice. Play with Cascade options and report back :)
    • ripper234
      ripper234 over 12 years
      @KenEgozi - do cascade options effect how my tables are generated? Because I have a duplication in the table structure itself... (I put the details in the question)
    • ripper234
      ripper234 over 12 years
      @KenEgozi - resolved by upgrading hibernate version - see my answer. Thanks.
  • ripper234
    ripper234 over 12 years
    Thanks for the help. Perhaps I didn't make myself clear in my answer above, but specifying the mappedBy property and upgrading from hibernate 3.6.1 to 3.6.8 resolved the issue for me. Anyway, I fixed my question according to your first suggestion.
  • Itay Levin
    Itay Levin over 12 years
    I don't understand why you indicated ManyToOne annotations at all...it seems weird that in a definition for a single entity you are specifying that Many of this entities will be related to a single other entity. (I understand the other way around.) but i think that it should be implicitly understood by the framework. (don't have much hibrnate experience , I admit)
  • ripper234
    ripper234 over 12 years
    @ItayLevin - I'm not sure why exactly it's required, but i think it's there for a good reason. I'll read the hibernate tutorial and see if I'll be marter after that.
  • izilotti
    izilotti over 10 years
    Also, if you are using persist(entity) and all your @OneToMany's already have a "mappedBy" property, you could try using merge(entity) instead.