3

I have a hibernate entity class

public class MyComplexClass implements  Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;

    String name;
    int number;
    @ElementCollection
    Map<String,String> myMap;

    @ManyToOne
    Simple simple;

    public MyComplexClass(String name, int number, Map<String,String> myMap) {
        this.name = name;
        this.port = number;
        this.myMap = myMap;
    }

    public MyComplexClass() {
        // TODO Auto-generated constructor stub
    }

    public void setName(String name) {
        this.name = name;

    }

    public String getName() {
        return name;
    }

    public void setNumber(int number) {
        this.port = number;
    }

    public int getPort() {
        return port;
    }

    public void setMyMap(Map<String,String> myMap) {
        this.myMap = myMap;
    }

    public Map<String,String> getMyMap() {
        return this.myMap;
    }


    public Simple getSimple() {
        return this.simple;
    }

And in the class simple I have a mapping of the form

    @Entity
    @Table(name = "Simple")


    public class Simple  implements Comparable<Simple>, Serializable {
    @JsonProperty
    @OneToMany(mappedBy="simple",fetch = FetchType.EAGER,cascade = CascadeType.ALL)
    Set<MyComplexClass> myComplexClass;

    public void setMyComplexClass(List<MyComplexClass> myComplexClass) {
        this.myComplexClass = myComplexClass;
    }

    public List<MyComplexClass> getMyComplexClass() {
        return this.myComplexClass;
    }

Somewhere in the system I set the values as

Map<String, String> myMap = new HashMap<String,String>();
myMap.put("value","value");
MyComplexClass myComplexClass = new MyComplexclass("a", 123, myMap)
Set<MyComplexClass> myComplexClassList = new HashSet<MyComplexClass>();
myComplexClassList.add(myComplexClassList)
simple.setMyComplexClass(myComplexClassList);
myComplexClass.setSimple(simple);
// save to the database
dao.save(simple);

This gets persisted in the database with multiple rows for my complex class with the same foreign key for the simple classs

 Table: MyComplexClass
    id  name number simple_id
     1  abc   234    1
     2  abc   234    1
     3  abc   234    1
     4  abc   234    1
     5  abc   234    1
     6  xyz   432    2
     7  xyz   432    2
     8  xyz   432    2

What have I missed? The id for all these rows is different which makes me think that they have been initialized multiple times in the code. But they aren't. Any reason they have different entries? I am using AKKA actors, could that be a reason?

Based on various similar problems. I have changed the Collection to be a Set and also added a compareTo method as follows

public int compareTo(MyComplexClass o) {
        if (o == null) {
            return 1;
        }

        if (this.getName().equals(o.getName()) && this.getNumber() == o.getPort()) {
            return 0;
        }
        return (int) (id - o.id);
    }
user_mda
  • 18,148
  • 27
  • 82
  • 145
  • The `ElementCollection` should be persisted into a separate (join) table, with multiple rows ... one per key/value pair. I don't see it in the `MyComplexClass` table, so no idea what you mean – Neil Stockton Jun 09 '17 at 17:07
  • Yes you are right, it is persisted as a seperate table. But my question is about the List which is an element collection on the Simple class. These creates various duplicate rows. Not sure why – user_mda Jun 09 '17 at 17:15
  • A `@OneToMany` is NOT an `@ElementCollection`! `@ElementCollection` is for containers of basic types, like the Map mentioned before – Neil Stockton Jun 09 '17 at 17:16
  • Ok Thanks, removed it but the problem still persists about duplicate rows – user_mda Jun 09 '17 at 17:20
  • also changed the question to reflect the problem – user_mda Jun 09 '17 at 18:31
  • @user_mda why You did this ? `MyComplexClass myComplexClass = new MyComplexclass("a", 123, myMap); Set myComplexClassList = new HashSet(); myComplexClassList.add(myComplexClassList);` – Taras Shpulyar Jun 12 '17 at 16:38
  • There is nothing akka-ish in the code you have posted, though. Why do you think Akka is the issue? – Diego Martinoia Jun 13 '17 at 08:27

2 Answers2

0

I'd vote to close this example that doesn't compile and isn't complete if I could, but since it has a bounty I can't. You reference a dao object that you don't include source for. You call doa.save, is this a Spring question? Otherwise, there is nothing wrong with your entities, it all works just fine for me.

public class JPAExample {    
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistence");
        EntityManager em = emf.createEntityManager();
        try {
            EntityTransaction tx = em.getTransaction();
            tx.begin();
            Simple simple = new Simple();
            Map<String, String> myMap = new HashMap<String,String>();
            myMap.put("value","value");
            MyComplexClass myComplexClass = new MyComplexClass("a", 123, myMap);
            Set<MyComplexClass> myComplexClassList = new HashSet<MyComplexClass>();
            myComplexClassList.add(myComplexClass);
            simple.setMyComplexClass(myComplexClassList);
            myComplexClass.setSimple(simple);
            // save to the database
            em.persist(simple);
            tx.commit();
        } finally {
            emf.close();
        }
    }
}

Simple.class

@Entity
@Table(name = "Simple")
public class Simple implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Long id;
    //    @JsonProperty
    @OneToMany(mappedBy = "simple", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    Set<MyComplexClass> myComplexClass;
    public void setMyComplexClass(Set<MyComplexClass> myComplexClass) {
        this.myComplexClass = myComplexClass;
    }
    public Set<MyComplexClass> getMyComplexClass() {
        return this.myComplexClass;
    }
}

MyComplexClass

@Entity
@Table(name="MyComplexClass")
public class MyComplexClass implements  Serializable {
    private static final long serialVersionUID = 1L;
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Long id;
    String name;
    int number;
    @ElementCollection
    Map<String,String> myMap;
    @ManyToOne
    Simple simple;
    public MyComplexClass(String name, int number, Map<String,String> myMap) {
        this.name = name;
        this.myMap = myMap;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setMyMap(Map<String,String> myMap) {
        this.myMap = myMap;
    }
    public Map<String,String> getMyMap() {
        return this.myMap;
    }
    public Simple getSimple() {
        return this.simple;
    }
    public void setSimple(Simple simple ) {
        this.simple = simple;
    }
}

Produces the following output...

Hibernate: create table MyComplexClass (id bigint generated by default as identity (start with 1), name varchar(255), number integer not null, simple_id bigint, primary key (id))
Hibernate: create table MyComplexClass_myMap (MyComplexClass_id bigint not null, myMap varchar(255), myMap_KEY varchar(255), primary key (MyComplexClass_id, myMap_KEY))
Hibernate: create table Simple (id bigint generated by default as identity (start with 1), primary key (id))
Hibernate: alter table MyComplexClass add constraint FK_qlkwvh7xyrn3udhnrn40fl6f8 foreign key (simple_id) references Simple
Hibernate: alter table MyComplexClass_myMap add constraint FK_qp5xa90y7pk94lb64kvt62589 foreign key (MyComplexClass_id) references MyComplexClass
Jun 12, 2017 10:06:56 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: HHH000230: Schema export complete
Hibernate: insert into Simple (id) values (default)
Hibernate: insert into MyComplexClass (id, name, number, simple_id) values (default, ?, ?, ?)
Hibernate: insert into MyComplexClass_myMap (MyComplexClass_id, myMap_KEY, myMap) values (?, ?, ?)
Jun 12, 2017 10:06:56 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:hsqldb:mem:standalone]

Which shows only one row inserted into the MyComplexClass table.

You can copy this code and use it to figure out what is wrong with your code.

K.Nicholas
  • 10,956
  • 4
  • 46
  • 66
0

Since you are using a Hash based collection Set, you must override equals and hashcode in your MyComplexClass entity.

From the docs

You have to override the equals() and hashCode() methods if you: intend to put instances of persistent classes in a Set (the recommended way to represent many-valued associations); and intend to use reattachment of detached instances

Overriding equals and hashcode should fix your problem.

Abdullah Khan
  • 12,010
  • 6
  • 65
  • 78