2

I am using following example

@Entity
public class Employer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @OneToMany(mappedBy = "employer")
    private List<EmployerDeliveryAgent> deliveryAgentAssoc;

    // other properties and getters and setters
}

@Entity
public class DeliveryAgent {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @OneToMany(mappedBy = "deliveryAgent")
    private List<EmployerDeliveryAgent> employerAssoc;

    // other properties and getters and setters
}

The association class

@Entity
@Table(name = "employer_delivery_agent")
@IdClass(EmployerDeliveryAgentId.class)
public class EmployerDeliveryAgent {

    @Id
    @ManyToOne
    @JoinColumn(name = "employer_id", referencedColumnName = "id")
    private Employer employer;

    @Id
    @ManyToOne
    @JoinColumn(name = "delivery_agent_id", referencedColumnName = "id")
    private DeliveryAgent deliveryAgent;

    @Column(name = "is_project_lead")
    private boolean isProjectLead;
}

Association PK class:

public class EmployerDeliveryAgentId implements Serializable {

    private int employer;
    private int deliveryAgent;

    // getters/setters and most importantly equals() and hashCode()
}

How do update the List<EmployerDeliveryAgent> deliveryAgentAssoc;?

If I get the Employer entity and do a simple setDeliveryAgentAssoc() and set it to a new list and save the Employer entity I end up with the old list and the new in my DB.

I have also tried following code but again it does not remove the old collection for some reason:

employer.getDeliveryAgentAssoc().forEach(employerDeliveryAgentRepository::delete);
employer.setDeliveryAgent(newCollection);
employerRepository.save(employer);

I want to replace all the contents of the existing collection with a new collection. How do I do that?

isADon
  • 3,433
  • 11
  • 35
  • 49

2 Answers2

4

You are missing the concept of Owning entity. The mappedBy annotation in Employer and DeliveryAgent defines the owning entity of the relationship as the EmployerDeliveryAgent entity. Since you have defined all the entities and repositories yourself you have to manage them yourself as well.

Setting relationship properties in a non-owing entity doesn't do any persistence. As a result those properties are for query only. Setting deliveryAgentAssoc or employerAssoc doesn't do anything for JPA.

Also, the Embedded pattern of ManyToMany is newer and generally works better.

Finally, use a Set unless you really think you have a reason to have an ordered List for OneToMany relations. Having multiple List relations in an entity causes JPA problems. Also, do not use Java primitive types in JPA based packages since that leads to problems and confusion.

Print out the SQL statements when writing your code so you can see what is going on. This is important because JPA will do lots of lazy fetches when you are not looking and if you don't understand that is happening and manage them you end up with errors and problems you don't understand. Ask any JPA coder about the dreaded LazyInitializationException.

So, as a suggested result:

@Entity
public class Employer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy="employer")
    private Set<EmployerDeliveryAgent> deliveryAgentAssoc;

@Entity
public class DeliveryAgent {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy="deliveryAgent")
    private Set<EmployerDeliveryAgent> employerAssoc;

@Entity
@ToString
public class EmployerDeliveryAgent {

    @EmbeddedId
    private EmployerDeliveryAgentId id = new EmployerDeliveryAgentId();

    @ManyToOne
    @MapsId("employerId")
    private Employer employer;

    @ManyToOne
    @MapsId("deliveryAgentId")
    private DeliveryAgent deliveryAgent;

    @Column(name = "is_project_lead")
    private Boolean isProjectLead;

And your ID class:

@Embeddable
public class EmployerDeliveryAgentId implements Serializable {
    private static final long serialVersionUID = 1L;
    private Long employerId;
    private Long deliveryAgentId;

And to use it:

    Employer emp1 = new Employer();
    employerRepo.save(emp1);
    DeliveryAgent da1 = new DeliveryAgent();
    deliveryAgentRepo.save(da1);
    EmployerDeliveryAgent eda1 = new EmployerDeliveryAgent();
    eda1.setEmployer(emp1);
    eda1.setDeliveryAgent(da1);
    eda1.setProjectLead(false);
    employerDeliveryAgentRepo.save(eda1);
    DeliveryAgent da2 = new DeliveryAgent();
    deliveryAgentRepo.save(da2);
    EmployerDeliveryAgent eda2 = new EmployerDeliveryAgent();
    eda2.setEmployer(emp1);
    eda2.setDeliveryAgent(da2);
    eda2.setProjectLead(true);
    employerDeliveryAgentRepo.save(eda2);

    employerDeliveryAgentRepo.findAll().forEach(System.out::println);

    EmployerDeliveryAgent edaex = new EmployerDeliveryAgent();
    edaex.setEmployer(emp1);
    employerDeliveryAgentRepo.findAll(Example.of(edaex)).forEach(System.out::println);

    employerDeliveryAgentRepo.deleteAll( employerDeliveryAgentRepo.findAll(Example.of(edaex)));

which results in the following Log outputs:

Hibernate: drop table delivery_agent if exists
Hibernate: drop table employer if exists
Hibernate: drop table employer_delivery_agent if exists
Hibernate: create table delivery_agent (id bigint generated by default as identity, primary key (id))
Hibernate: create table employer (id bigint generated by default as identity, primary key (id))
Hibernate: create table employer_delivery_agent (is_project_lead boolean, delivery_agent_id bigint not null, employer_id bigint not null, primary key (delivery_agent_id, employer_id))
Hibernate: alter table employer_delivery_agent add constraint FKqfdjch3412029revbsh103okx foreign key (delivery_agent_id) references delivery_agent
Hibernate: alter table employer_delivery_agent add constraint FKc3djdeycywdtbpn4muakrhhtq foreign key (employer_id) references employer
2020-05-05 10:56:36.200  INFO 7588 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-05-05 10:56:36.204  INFO 7588 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-05-05 10:56:36.403  INFO 7588 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.455 seconds (JVM running for 1.821)
Hibernate: insert into employer (id) values (null)
Hibernate: insert into delivery_agent (id) values (null)
Hibernate: select employerde0_.delivery_agent_id as delivery2_2_0_, employerde0_.employer_id as employer3_2_0_, employerde0_.is_project_lead as is_proje1_2_0_ from employer_delivery_agent employerde0_ where employerde0_.delivery_agent_id=? and employerde0_.employer_id=?
Hibernate: select deliveryag0_.id as id1_0_0_ from delivery_agent deliveryag0_ where deliveryag0_.id=?
Hibernate: select employer0_.id as id1_1_0_ from employer employer0_ where employer0_.id=?
Hibernate: insert into employer_delivery_agent (is_project_lead, delivery_agent_id, employer_id) values (?, ?, ?)
Hibernate: insert into delivery_agent (id) values (null)
Hibernate: select employerde0_.delivery_agent_id as delivery2_2_0_, employerde0_.employer_id as employer3_2_0_, employerde0_.is_project_lead as is_proje1_2_0_ from employer_delivery_agent employerde0_ where employerde0_.delivery_agent_id=? and employerde0_.employer_id=?
Hibernate: select deliveryag0_.id as id1_0_0_ from delivery_agent deliveryag0_ where deliveryag0_.id=?
Hibernate: select employer0_.id as id1_1_0_ from employer employer0_ where employer0_.id=?
Hibernate: insert into employer_delivery_agent (is_project_lead, delivery_agent_id, employer_id) values (?, ?, ?)
Hibernate: select employerde0_.delivery_agent_id as delivery2_2_, employerde0_.employer_id as employer3_2_, employerde0_.is_project_lead as is_proje1_2_ from employer_delivery_agent employerde0_
Hibernate: select deliveryag0_.id as id1_0_0_ from delivery_agent deliveryag0_ where deliveryag0_.id=?
Hibernate: select employer0_.id as id1_1_0_ from employer employer0_ where employer0_.id=?
Hibernate: select deliveryag0_.id as id1_0_0_ from delivery_agent deliveryag0_ where deliveryag0_.id=?
EmployerDeliveryAgent(id=com.example.demo.EmployerDeliveryAgentId@3e1, employer=com.example.demo.Employer@52ae997b, deliveryAgent=com.example.demo.DeliveryAgent@32f32623, isProjectLead=false)
EmployerDeliveryAgent(id=com.example.demo.EmployerDeliveryAgentId@400, employer=com.example.demo.Employer@52ae997b, deliveryAgent=com.example.demo.DeliveryAgent@7e15f4d4, isProjectLead=true)
Hibernate: select employerde0_.delivery_agent_id as delivery2_2_, employerde0_.employer_id as employer3_2_, employerde0_.is_project_lead as is_proje1_2_ from employer_delivery_agent employerde0_ inner join employer employer1_ on employerde0_.employer_id=employer1_.id where employer1_.id=1
Hibernate: select deliveryag0_.id as id1_0_0_ from delivery_agent deliveryag0_ where deliveryag0_.id=?
Hibernate: select employer0_.id as id1_1_0_ from employer employer0_ where employer0_.id=?
Hibernate: select deliveryag0_.id as id1_0_0_ from delivery_agent deliveryag0_ where deliveryag0_.id=?
EmployerDeliveryAgent(id=com.example.demo.EmployerDeliveryAgentId@3e1, employer=com.example.demo.Employer@62b57479, deliveryAgent=com.example.demo.DeliveryAgent@1903b5d, isProjectLead=false)
EmployerDeliveryAgent(id=com.example.demo.EmployerDeliveryAgentId@400, employer=com.example.demo.Employer@62b57479, deliveryAgent=com.example.demo.DeliveryAgent@5a90265a, isProjectLead=true)
Hibernate: select employerde0_.delivery_agent_id as delivery2_2_, employerde0_.employer_id as employer3_2_, employerde0_.is_project_lead as is_proje1_2_ from employer_delivery_agent employerde0_ inner join employer employer1_ on employerde0_.employer_id=employer1_.id where employer1_.id=1
Hibernate: select deliveryag0_.id as id1_0_0_ from delivery_agent deliveryag0_ where deliveryag0_.id=?
Hibernate: select employer0_.id as id1_1_0_ from employer employer0_ where employer0_.id=?
Hibernate: select deliveryag0_.id as id1_0_0_ from delivery_agent deliveryag0_ where deliveryag0_.id=?
Hibernate: select employerde0_.delivery_agent_id as delivery2_2_0_, employerde0_.employer_id as employer3_2_0_, employerde0_.is_project_lead as is_proje1_2_0_, deliveryag1_.id as id1_0_1_, employer2_.id as id1_1_2_ from employer_delivery_agent employerde0_ inner join delivery_agent deliveryag1_ on employerde0_.delivery_agent_id=deliveryag1_.id inner join employer employer2_ on employerde0_.employer_id=employer2_.id where employerde0_.delivery_agent_id=? and employerde0_.employer_id=?
Hibernate: select employerde0_.delivery_agent_id as delivery2_2_0_, employerde0_.employer_id as employer3_2_0_, employerde0_.is_project_lead as is_proje1_2_0_, deliveryag1_.id as id1_0_1_, employer2_.id as id1_1_2_ from employer_delivery_agent employerde0_ inner join delivery_agent deliveryag1_ on employerde0_.delivery_agent_id=deliveryag1_.id inner join employer employer2_ on employerde0_.employer_id=employer2_.id where employerde0_.delivery_agent_id=? and employerde0_.employer_id=?
Hibernate: delete from employer_delivery_agent where delivery_agent_id=? and employer_id=?
Hibernate: delete from employer_delivery_agent where delivery_agent_id=? and employer_id=?
K.Nicholas
  • 10,956
  • 4
  • 46
  • 66
-1

Either Employer or DeliveryAgent should be responsible for updating EmployerDeliveryAgent collection.

Add following methods in Employer class.

public void addEmployerAssoc(EmployerDeliveryAgent item) {
    item.setEmployer(this);
    deliveryAgentAssoc.add(item);
}

public void removeEmployerAssoc(EmployerDeliveryAgent item) {
    item.setEmployer(null);
    deliveryAgentAssoc.remove(item); // must implement equals/hashcode
}
SeaBiscuit
  • 2,553
  • 4
  • 25
  • 40
  • That does not answer the question on how to replace all the contents of the existing collection with a new collection. How to remove the existing collection? call removeEmployerAssoc for each element? (causes ConcurrentModificationException). Simply call clear on the collection? (Doesn't do anything) – isADon May 05 '20 at 12:33
  • Even removing one element will not work with your example. HibernateException: No part of a composite identifier may be null – isADon May 05 '20 at 12:43