JPA @OneToMany 关联用空值保存 id
JPA @OneToMany association saves id with null value
我实际上正在尝试定义由 JPA 管理的两个实体(在我的示例中称为 Aggregate1 和 Aggregate2)之间的关系,映射 table 表示为另一个 JPA 实体(称为 Association)
这里是 Aggregate1 的定义 class :
@Data
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Entity
@Table(name = "aggregate1")
public class Aggregate1 {
@Id
@GeneratedValue
private Long id;
@Column
private String something;
@OneToMany(fetch = FetchType.EAGER, cascade = { ALL }, orphanRemoval = true)
@JoinColumn(name = "aggregate1_id")
private final Set<Association> associations = new HashSet<>();
public Aggregate1(String something) {
this.something = something;
}
// methods to add, update and remove association omitted
}
和协会的定义class:
@Value
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
@AllArgsConstructor
@Entity
@Table(name = "association")
public class Association implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "aggregate1_id")
private final Long aggregate1Id;
@Id
@Column(name = "aggregate2_id")
private final Long aggregate2Id;
@Column(name = "association_value")
private final String associationValue;
// methods omitted
}
您可以找到 运行 代码的完整示例 here。
我知道表示两个实体之间多对多关系的标准方法是使用 @ManyToMany
关联(如 post 中所述),但我不想 link 直接 Aggregate1
和 Aggregate2
实体,而是通过身份 link 它们。我不需要从 Aggregate2
到 Aggregate1
的 link。
所以使用这个 JPA 映射,我确实可以检索一个 Aggregate1
及其 Association
,但是当我尝试添加一个新的关联时,在新的 Association
实体,抛出如下异常:
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
...
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
...
Caused by: org.h2.jdbc.JdbcSQLException: NULL not allowed for column "AGGREGATE1_ID"; SQL statement:
insert into association (association_value, aggregate2_id, aggregate1_id) values (?, ?, ?) [23502-196]
这是保存期间发生的 Hibernate 日志:
2018-02-23 10:13:36.664 DEBUG 9076 --- [ main] org.hibernate.SQL : select associatio0_.aggregate2_id as aggregat1_2_0_, associatio0_.aggregate1_id as aggregat2_2_0_, associatio0_.association_value as associat3_2_0_ from association associatio0_ where associatio0_.aggregate2_id=? and associatio0_.aggregate1_id=?
2018-02-23 10:13:36.666 TRACE 9076 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [3]
2018-02-23 10:13:36.666 TRACE 9076 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1]
2018-02-23 10:13:36.686 DEBUG 9076 --- [ main] org.hibernate.SQL : insert into association (association_value, aggregate2_id, aggregate1_id) values (?, ?, ?)
2018-02-23 10:13:36.687 TRACE 9076 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [test association]
2018-02-23 10:13:36.687 TRACE 9076 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [null]
2018-02-23 10:13:36.687 TRACE 9076 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [BIGINT] - [null]
2018-02-23 10:13:36.688 WARN 9076 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 23502, SQLState: 23502
2018-02-23 10:13:36.688 ERROR 9076 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : NULL not allowed for column "AGGREGATE1_ID"; SQL statement:
insert into association (association_value, aggregate2_id, aggregate1_id) values (?, ?, ?) [23502-196]
因此值设置正确,如关联 table 中的初始 select 所示,但似乎 aggregate1Id
和 aggregate2Id
属性值已重置保存前为空。
谁能解释一下保存过程中到底发生了什么,以及为什么这些值被重置为空?
所以DN1提出的解决方案有效,下面的AssociationId
class实际上是缺失的:
@Value
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
@AllArgsConstructor
public class AssociationId implements Serializable {
private static final long serialVersionUID = 1L;
private final Long aggregate1Id;
private final Long aggregate2Id;
}
并且,在 Association
class 中,@IdClass 注释必须定义为:
@Value
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
@AllArgsConstructor
@Entity
@Table(name = "association")
@IdClass(AssociationId.class)
public class Association implements Serializable {
...
}
---编辑---
为了能够添加和删除关联,Aggregate1
实体定义必须使用这些更改进行更新:
@Data
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Entity
@Table(name = "aggregate1")
public class Aggregate1 {
...
@OneToMany(fetch = FetchType.EAGER, mappedBy = "aggregate1Id", cascade = { ALL }, orphanRemoval = true)
private final Set<Association> associations = new HashSet<>();
...
}
我实际上正在尝试定义由 JPA 管理的两个实体(在我的示例中称为 Aggregate1 和 Aggregate2)之间的关系,映射 table 表示为另一个 JPA 实体(称为 Association)
这里是 Aggregate1 的定义 class :
@Data
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Entity
@Table(name = "aggregate1")
public class Aggregate1 {
@Id
@GeneratedValue
private Long id;
@Column
private String something;
@OneToMany(fetch = FetchType.EAGER, cascade = { ALL }, orphanRemoval = true)
@JoinColumn(name = "aggregate1_id")
private final Set<Association> associations = new HashSet<>();
public Aggregate1(String something) {
this.something = something;
}
// methods to add, update and remove association omitted
}
和协会的定义class:
@Value
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
@AllArgsConstructor
@Entity
@Table(name = "association")
public class Association implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "aggregate1_id")
private final Long aggregate1Id;
@Id
@Column(name = "aggregate2_id")
private final Long aggregate2Id;
@Column(name = "association_value")
private final String associationValue;
// methods omitted
}
您可以找到 运行 代码的完整示例 here。
我知道表示两个实体之间多对多关系的标准方法是使用 @ManyToMany
关联(如 post 中所述),但我不想 link 直接 Aggregate1
和 Aggregate2
实体,而是通过身份 link 它们。我不需要从 Aggregate2
到 Aggregate1
的 link。
所以使用这个 JPA 映射,我确实可以检索一个 Aggregate1
及其 Association
,但是当我尝试添加一个新的关联时,在新的 Association
实体,抛出如下异常:
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
...
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
...
Caused by: org.h2.jdbc.JdbcSQLException: NULL not allowed for column "AGGREGATE1_ID"; SQL statement:
insert into association (association_value, aggregate2_id, aggregate1_id) values (?, ?, ?) [23502-196]
这是保存期间发生的 Hibernate 日志:
2018-02-23 10:13:36.664 DEBUG 9076 --- [ main] org.hibernate.SQL : select associatio0_.aggregate2_id as aggregat1_2_0_, associatio0_.aggregate1_id as aggregat2_2_0_, associatio0_.association_value as associat3_2_0_ from association associatio0_ where associatio0_.aggregate2_id=? and associatio0_.aggregate1_id=?
2018-02-23 10:13:36.666 TRACE 9076 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [3]
2018-02-23 10:13:36.666 TRACE 9076 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1]
2018-02-23 10:13:36.686 DEBUG 9076 --- [ main] org.hibernate.SQL : insert into association (association_value, aggregate2_id, aggregate1_id) values (?, ?, ?)
2018-02-23 10:13:36.687 TRACE 9076 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [test association]
2018-02-23 10:13:36.687 TRACE 9076 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [null]
2018-02-23 10:13:36.687 TRACE 9076 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [BIGINT] - [null]
2018-02-23 10:13:36.688 WARN 9076 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 23502, SQLState: 23502
2018-02-23 10:13:36.688 ERROR 9076 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : NULL not allowed for column "AGGREGATE1_ID"; SQL statement:
insert into association (association_value, aggregate2_id, aggregate1_id) values (?, ?, ?) [23502-196]
因此值设置正确,如关联 table 中的初始 select 所示,但似乎 aggregate1Id
和 aggregate2Id
属性值已重置保存前为空。
谁能解释一下保存过程中到底发生了什么,以及为什么这些值被重置为空?
所以DN1提出的解决方案有效,下面的AssociationId
class实际上是缺失的:
@Value
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
@AllArgsConstructor
public class AssociationId implements Serializable {
private static final long serialVersionUID = 1L;
private final Long aggregate1Id;
private final Long aggregate2Id;
}
并且,在 Association
class 中,@IdClass 注释必须定义为:
@Value
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
@AllArgsConstructor
@Entity
@Table(name = "association")
@IdClass(AssociationId.class)
public class Association implements Serializable {
...
}
---编辑---
为了能够添加和删除关联,Aggregate1
实体定义必须使用这些更改进行更新:
@Data
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Entity
@Table(name = "aggregate1")
public class Aggregate1 {
...
@OneToMany(fetch = FetchType.EAGER, mappedBy = "aggregate1Id", cascade = { ALL }, orphanRemoval = true)
private final Set<Association> associations = new HashSet<>();
...
}