JPA/Hibernate @OneToMany 在 JoinTable 上带有可选 Child - 保存 parent 影响 JoinTable 上的多个插入
JPA / Hibernate @OneToMany with optional Child on JoinTable - Save parent affect multiple Inserts on JoinTable
我在 Parent (属性) 和 Children (Credit) 之间有关系,我的 Hibernate 配置对我来说工作正常,只要我不尝试保存Cascade 通过 Parent (属性) 的新 Child(信用)。
休眠配置:
@Entity
public class Property implements Ownable {
...
@Valid
// ALSO TRIED WITH CascadeType.ALL !!!
@OneToMany(fetch = LAZY, cascade = {PERSIST, MERGE, REMOVE}, orphanRemoval = true)
@JoinTable(
name = "PROPERTY_CREDIT_OPTIONS",
joinColumns = @JoinColumn(name="PROPERTY_ID", nullable = false),
inverseJoinColumns = @JoinColumn(name = "CREDIT_ID", nullable = false, unique = true)
)
List<NotActiveCredit> creditOptions = new ArrayList<>();
...
}
@Entity
public class NotActiveCredit extends Credit {
@ManyToOne
@JoinTable(
name = "PROPERTY_CREDIT_OPTIONS",
inverseJoinColumns = @JoinColumn(name="PROPERTY_ID", nullable = false),
joinColumns = @JoinColumn(name = "CREDIT_ID", nullable = false, unique = true)
)
@NotNull @NonNull
Property property;
}
控制器调用:
@PostMapping("/save")
public String save (
@ModelAttribute("property") @Valid Property formProperty,
BindingResult errors,
ModelMap modelMap
) {
if (!errors.hasErrors()) {
// formProperty has new Credit in the creditOptions List
// SAVE ONLY DELEGATS TO SPRING DATA REPOSITORY ...
final Property savedProperty = propertyService.save(formProperty);
...
} else {
// BLABLA
}
}
日志看起来像:
Enter InvestmentController.save( ... )
Enter PropertyServiceImpl.save( ... )
insert into not_active_credit (id, interest_rate_nominal_in_percent, name_of_institution, redemption_at_begin_in_percent, special_redemption_each_year_in_percent) values (default, ?, ?, ?, ?)
binding parameter [1] as [NUMERIC] - [null]
binding parameter [2] as [VARCHAR] - [test]
binding parameter [3] as [NUMERIC] - [null]
binding parameter [4] as [NUMERIC] - [null]
insert into property_credit_options (property_id, credit_id) values (?, ?)
binding parameter [1] as [BIGINT] - [1]
binding parameter [2] as [BIGINT] - [1]
Exit PropertyServiceImpl.save Returns Property
// THEN WHEN IT CLOSE THE TRANSACTION IT WANTS TO INSERT JOIN TABLE AGAIN!
insert into property_credit_options (property_id, credit_id) values (?, ?)
binding parameter [1] as [BIGINT] - [1]
binding parameter [2] as [BIGINT] - [1]
SQL Error: -104, SQLState: 23505
integrity constraint violation: unique constraint or index violation; SYS_PK_10222 table: PROPERTY_CREDIT_OPTIONS
我的Object`s内容
任何提示都非常受欢迎。
编辑:
附加信息。在调用 Repository.Save 之后,列表仍然被认为是脏的 (dirty=true)。当它刚刚被保存时它不应该是假的,因为在控制台中我可以看到它只是将 SQL 发送到数据库进行插入。在调试时,如果我给列表 dirty=false 那么第二次 Insert 将不会被触发,一切都很好。为什么在向 DB 发送插入后不将其设置为 false。应该不会更脏吧,还是我想错了?
解决方案
正如 crizzis 所建议的,我从 属性 creditOptions
class Property
中删除了 @JoinTable
并添加了 mappedBy='property'
。该集合现在可以从 parent 更新。
@Valid
@OneToMany(mappedBy="property", fetch = LAZY, cascade = ALL, orphanRemoval = true)
Collection<NotActiveCredit> creditOptions = new ArrayList<>();
另一边classNotActiveCredit
是这样的:
@ManyToOne
@JoinTable(
name = "PROPERTY_CREDIT_OPTIONS",
inverseJoinColumns = @JoinColumn(name="PROPERTY_ID", nullable = false),
joinColumns = @JoinColumn(name = "CREDIT_ID", nullable = false, unique = true)
)
@NotNull
Property property;
关联条目被两次插入联接的原因 table 是您实际上声明了 两个单独的 关联(恰好使用same join table) 而不是只有一个。
在 JPA 中建模双向关联时,其中一端必须是拥有端,另一端必须是反向端[=24] =].相反的一面是其关联映射声明 mappedBy
属性的一面。
解决方案很简单,将关联的一侧声明为反面,并从该侧删除 @JoinTable
注释。否则 JPA 会将每一方视为单独的单向关联。
请注意,就 JPA 所知,您可能出于某种原因希望拥有两个单独的单向关联(例如,使用不同的连接 table)。 JPA 根本不会自动知道您的意图 - 您需要 mappedBy
属性来明确它们。
我在 Parent (属性) 和 Children (Credit) 之间有关系,我的 Hibernate 配置对我来说工作正常,只要我不尝试保存Cascade 通过 Parent (属性) 的新 Child(信用)。
休眠配置:
@Entity
public class Property implements Ownable {
...
@Valid
// ALSO TRIED WITH CascadeType.ALL !!!
@OneToMany(fetch = LAZY, cascade = {PERSIST, MERGE, REMOVE}, orphanRemoval = true)
@JoinTable(
name = "PROPERTY_CREDIT_OPTIONS",
joinColumns = @JoinColumn(name="PROPERTY_ID", nullable = false),
inverseJoinColumns = @JoinColumn(name = "CREDIT_ID", nullable = false, unique = true)
)
List<NotActiveCredit> creditOptions = new ArrayList<>();
...
}
@Entity
public class NotActiveCredit extends Credit {
@ManyToOne
@JoinTable(
name = "PROPERTY_CREDIT_OPTIONS",
inverseJoinColumns = @JoinColumn(name="PROPERTY_ID", nullable = false),
joinColumns = @JoinColumn(name = "CREDIT_ID", nullable = false, unique = true)
)
@NotNull @NonNull
Property property;
}
控制器调用:
@PostMapping("/save")
public String save (
@ModelAttribute("property") @Valid Property formProperty,
BindingResult errors,
ModelMap modelMap
) {
if (!errors.hasErrors()) {
// formProperty has new Credit in the creditOptions List
// SAVE ONLY DELEGATS TO SPRING DATA REPOSITORY ...
final Property savedProperty = propertyService.save(formProperty);
...
} else {
// BLABLA
}
}
日志看起来像:
Enter InvestmentController.save( ... )
Enter PropertyServiceImpl.save( ... )
insert into not_active_credit (id, interest_rate_nominal_in_percent, name_of_institution, redemption_at_begin_in_percent, special_redemption_each_year_in_percent) values (default, ?, ?, ?, ?)
binding parameter [1] as [NUMERIC] - [null]
binding parameter [2] as [VARCHAR] - [test]
binding parameter [3] as [NUMERIC] - [null]
binding parameter [4] as [NUMERIC] - [null]
insert into property_credit_options (property_id, credit_id) values (?, ?)
binding parameter [1] as [BIGINT] - [1]
binding parameter [2] as [BIGINT] - [1]
Exit PropertyServiceImpl.save Returns Property
// THEN WHEN IT CLOSE THE TRANSACTION IT WANTS TO INSERT JOIN TABLE AGAIN!
insert into property_credit_options (property_id, credit_id) values (?, ?)
binding parameter [1] as [BIGINT] - [1]
binding parameter [2] as [BIGINT] - [1]
SQL Error: -104, SQLState: 23505
integrity constraint violation: unique constraint or index violation; SYS_PK_10222 table: PROPERTY_CREDIT_OPTIONS
我的Object`s内容
编辑: 附加信息。在调用 Repository.Save 之后,列表仍然被认为是脏的 (dirty=true)。当它刚刚被保存时它不应该是假的,因为在控制台中我可以看到它只是将 SQL 发送到数据库进行插入。在调试时,如果我给列表 dirty=false 那么第二次 Insert 将不会被触发,一切都很好。为什么在向 DB 发送插入后不将其设置为 false。应该不会更脏吧,还是我想错了?
解决方案
正如 crizzis 所建议的,我从 属性 creditOptions
class Property
中删除了 @JoinTable
并添加了 mappedBy='property'
。该集合现在可以从 parent 更新。
@Valid
@OneToMany(mappedBy="property", fetch = LAZY, cascade = ALL, orphanRemoval = true)
Collection<NotActiveCredit> creditOptions = new ArrayList<>();
另一边classNotActiveCredit
是这样的:
@ManyToOne
@JoinTable(
name = "PROPERTY_CREDIT_OPTIONS",
inverseJoinColumns = @JoinColumn(name="PROPERTY_ID", nullable = false),
joinColumns = @JoinColumn(name = "CREDIT_ID", nullable = false, unique = true)
)
@NotNull
Property property;
关联条目被两次插入联接的原因 table 是您实际上声明了 两个单独的 关联(恰好使用same join table) 而不是只有一个。
在 JPA 中建模双向关联时,其中一端必须是拥有端,另一端必须是反向端[=24] =].相反的一面是其关联映射声明 mappedBy
属性的一面。
解决方案很简单,将关联的一侧声明为反面,并从该侧删除 @JoinTable
注释。否则 JPA 会将每一方视为单独的单向关联。
请注意,就 JPA 所知,您可能出于某种原因希望拥有两个单独的单向关联(例如,使用不同的连接 table)。 JPA 根本不会自动知道您的意图 - 您需要 mappedBy
属性来明确它们。