【问题标题】:JPA 2.0 many-to-many with extra column - Update collection带有额外列的 JPA 2.0 多对多 - 更新集合
【发布时间】:2020-05-05 10:12:35
【问题描述】:

我正在使用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
}

关联类

@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;
}

公会PK类:

public class EmployerDeliveryAgentId implements Serializable {

    private int employer;
    private int deliveryAgent;

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

如何更新List&lt;EmployerDeliveryAgent&gt; deliveryAgentAssoc;

如果我获得 Employer 实体并执行简单的 setDeliveryAgentAssoc() 并将其设置为新列表并保存 Employer 实体,我最终会在我的数据库中得到旧列表和新列表。

我也尝试过以下代码,但由于某种原因它再次没有删除旧集合:

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

我想用新集合替换现有集合的所有内容。我该怎么做?

【问题讨论】:

    标签: hibernate jpa


    【解决方案1】:

    您缺少Owning 实体的概念。 EmployerDeliveryAgent 中的mappedBy 注释将关系的拥有实体定义为EmployerDeliveryAgent 实体。由于您自己定义了所有实体和存储库,因此您也必须自己管理它们。

    在非所有者实体中设置关系属性不会做任何持久性。因此,这些属性仅供查询。设置 deliveryAgentAssocemployerAssoc 对 JPA 没有任何作用。

    此外,ManyToManyEmbedded 模式更新,通常效果更好。

    最后,使用Set,除非你真的认为你有理由为OneToMany 关系订购List。在一个实体中有多个 List 关系会导致 JPA 问题。此外,不要在基于 JPA 的包中使用 Java 原始类型,因为这会导致问题和混乱。

    在编写代码时打印出 SQL 语句,以便您了解发生了什么。这很重要,因为 JPA 会在您不查看时执行大量惰性提取,如果您不了解正在发生的事情并对其进行管理,您最终会遇到您不了解的错误和问题。向任何 JPA 编码人员询问可怕的 LazyInitializationException

    所以,作为建议的结果:

    @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;
    

    还有你的 ID 等级:

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

    并使用它:

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

    导致以下日志输出:

    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=?
    

    【讨论】:

      【解决方案2】:

      Employer 或 DeliveryAgent 应负责更新 EmployerDeliveryAgent 集合。

      在 Employer 类中添加以下方法。

      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
      }
      

      【讨论】:

      • 这并没有回答如何用新集合替换现有集合的所有内容的问题。如何删除现有的集合?为每个元素调用 removeEmployerAssoc? (导致 ConcurrentModificationException)。只需调用 clear 上的集合? (什么都不做)
      • 即使删除一个元素也不适用于您的示例。 HibernateException:复合标识符的任何部分都不能为空
      猜你喜欢
      • 2014-07-13
      • 1970-01-01
      • 2018-07-09
      • 1970-01-01
      • 2020-03-03
      • 2012-10-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多