休眠JPA父

我有一个父表,即包含一个列ID的audit_log(父)。 对于audit_log中的给定ID,我有一个供应商ID列表。 我将它们存储在单独的表audit_log_vendorid(子表)中。 我希望子表作为其中一列(parent_id)从父表中获取id。 这是表格模式。

AUDIT_LOG

+-------+------------+------+-----+---------+-------+
| Field | Type       | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| id    | bigint(19) | NO   | PRI | NULL    |       |
+-------+------------+------+-----+---------+-------+

audit_log_vendorid

+-----------+------------+------+-----+---------+----------------+
| Field     | Type       | Null | Key | Default | Extra          |
+-----------+------------+------+-----+---------+----------------+
| id        | bigint(19) | NO   | PRI | NULL    | auto_increment |
| vendor_id | bigint(19) | NO   |     | NULL    |                |
| parent_id | bigint(19) | NO   |     | NULL    |                |
+-----------+------------+------+-----+---------+----------------+

我按照以下方式定义了我的hibernate类

@Entity
@Table(name="audit_log")
public class AuditLog  {

private List<AuditVendorPair> vendorIDs;


public AuditLog(List<AuditVendorPair> vendorIds) throws Exception {
    this.vendorIDs = vendorIDs;
}

@OneToMany(cascade=CascadeType.ALL)
@JoinTable(name = "audit_log_vendorid",  
   joinColumns = { @JoinColumn(name = "parent_id", referencedColumnName="id") })
public List<AuditVendorPair> getVendors() {
    return vendorIDs;
}

@Id @Column(name="ID")
public Long getId() {
    return super.getId();
}

public void setHostServices(List<AuditVendorPair> vendorIDs){
    this.vendorIDs = vendorIDs;
}

}

下面是我的用于audit_log_vendorid的hibernate映射类。 我传入一个供应商id,期望其他两个字段通过休眠来填充。 我想从audit_log中的“id”字段获取parent_id字段。 它现在被初始化为null,导致mysql约束异常。

@Entity
@Table(name="audit_log_vendorid")
public class AuditVendorPair {

private Long id;
private Long parent_id;
private Long vendor_id;
public AuditVendorPair(Long vendor_id){
    this.vendor_id = vendor_id;
}

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
public Long getId(){
    return id;
}

public void setId(Long id){
    this.id = id;
}

@Column(name="vendor_id")
public Long getVendorID() {
    return vendor_id;
}

public void setVendorID(Long vendor_id){
    this.vendor_id = vendor_id;
}


@Column(name="parent_id")
public Long getParentId() {
    return parent_id;
}

public void setParentId(Long parentID){
    this.parent_id = parentID;
}
}

我很想知道我的注释是否正确。 我基本上希望通过hibernate将audit_log表中的id填充到audit_log_vendorid表中的parent_id字段中。


不,他们不正确。 audit_log_vendorid不是连接表。 连接表是没有映射到实体的表,并且包含映射到其他表的两个关联实体的ID。

您在AuditVendorPair中也不应该有parent_id字段。 不仅因为它不尊重Java命名约定,而且因为它应该被对ManyToOne映射的AuditLog引用所取代。

因此,简而言之,您应该有一个双向OneToMany关联,按照文档中的说明进行映射。


我认为你忽略了JPA中的一个关键概念,即实体是对象,所以你不会有直接使用ID引用它的父实体的实体,你可以引用该对象(查询数据库时JPA将使用该ID)

@Entity
@Table(name="audit_log")
public class AuditLog  {

    @OneToMany(cascade= CascadeType.ALL, mappedBy = "auditLog")
    private Collection<AuditVendorPair> vendorIDs;

    @Id @Column(name="id")
    private Long id;

    public AuditLog() {
    }

    public Collection<AuditVendorPair> getVendors() {
        if (vendorIDs == null) {
            vendorIDs = new ArrayList<>();
        }
        return vendorIDs;
    }

    public long getId() {
        return id;
    }
}

@Entity
@Table(name = "audit_log_vendorid")
public class AuditVendorPair {

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

    @JoinColumn(nullable = false, name = "parent_id")
    @ManyToOne(optional = false)
    private AuditLog auditLog;

    @Column(name = "vendor_id")
    private Long vendorId;

    public AuditVendorPair() {
    }

    public long getVendorId() {
        return vendorId;
    }

    public void setVendorId(long vendorId) {
        this.vendorId = vendorId;
    }

    public AuditLog getAuditLog() {
        return auditLog;
    }

    public void setAuditLog(AuditLog auditLog) {
        this.auditLog = auditLog;
    }
}

AuditVendorPair指的是使用Entity的AuditLog,由于这是一个实体关系,所以您必须使用@JoinColumn来指定名称。

JPA / Hibernate的一些最佳实践

  • 规范不需要任何参数构造函数。
  • 除非用代码生成主键,否则不要使用主键。
  • 你不能使用基元来处理字段。 但是,如果数据库强制执行NOT NULL,则应该在getter和setters中使用基元,以便IDE可以警告您NPE,而不是等待测试失败。
  • 使用集合而不是列表,因为列表意味着顺序。
  • 在字段名中完成使用下划线,但在指定列名时使用它们。
  • 注意getVendors()中的空检查和集合创建,这是为了避免在首次创建对象时避开NPE。 这意味着您可以使用相同的逻辑来添加AuditVendorPair,而不管审计日志是刚刚创建的,还是从数据库加载的。 这也意味着我不会创建一个setVendors(),除非我需要替换整个列表,这种情况很少出现,并且在什么情况下,通常需要显式删除列表中的每个实体。

  • 听起来你应该多读一些JPA背后的原理。 这是一个'原始的'JPA例子,代码看起来像什么,如果你使用Spring-data和autowire @PersistanceUnit,你不需要自己管理entityManager。

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("PU-Name");
    EntityManager em = emf.createEntityManager();
    
    try {
        long primaryKey = 1L; // comes from somewhere else
        long vendorId = 1L;   // comes from somewhere
        AuditLog log = em.find(AuditLog.class, primaryKey); // loaded from DB
    
        AuditVendorPair pair = new AuditVendorPair();
        pair.setAuditLog(log);
        pair.setVendorId(vendorId);
        em.getTransaction().begin();
        em.persist(pair);
        em.getTransaction().commit();
    } finally {
        em.close();
    }
    

    您必须在代码中连接实体,并且必须从数据库加载任何现有实体。 如果要存储新的AuditVendorPair,则首先必须加载AuditLog对象,然后将其设置为新创建的AuditVendorPair。 通常供应商也将是一个实体,所以它也必须被查找。

    注意 :上面的示例不维护AuditLog和AuditVendorPair之间的双向关系,因为AuditVendorPair未添加到AuditLog上的供应商集合。 由于定义关系的列在AuditVendorPair(已存储)上,所以这不是问题,因为下次从数据库加载AuditLog实例时,AuditVendorPair将成为供应商集合的一部分。 但是,如果在持久化上下文关闭后使用AuditLog,则可能需要维护双向关系。

    注意 :上面的示例假定您使用主键获取请求。 一般而言,您绝对不应该在任何前端公开主键,在我们的系统中,我们会为每个在UI中公开的实体生成一个唯一字段(UUID)。

    链接地址: http://www.djcxy.com/p/37059.html

    上一篇: Hibernate JPA Parent

    下一篇: Hibernate criteria query with subquery joining two columns