Java Spring Jpa数据存储库使用LinkedEntity为多个关系保存(更新)
有两个实体(比如规则和标签)使用链接实体具有多对多关系Java Spring Jpa数据存储库使用LinkedEntity为多个关系保存(更新),java,hibernate,spring-data-jpa,Java,Hibernate,Spring Data Jpa,有两个实体(比如规则和标签)使用链接实体具有多对多关系 规则完整性: @Entity @Table(name = "rule") @JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class, property = "name") public class Rule implements Serializable { @Id @GeneratedValue(strategy = Genera
规则完整性:
@Entity
@Table(name = "rule")
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "name")
public class Rule implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NaturalId
@NotBlank
@Column(unique = true)
private String name;
@Lob
@Column(columnDefinition = "TEXT")
private String content;
@OneToMany(mappedBy = "rule", cascade = {CascadeType.PERSIST,
CascadeType.MERGE})
private List<RuleLabel> labels = new ArrayList<>();
...
@Entity
@Table(name = "label")
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Label implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
private String name;
@OneToMany(mappedBy = "label", cascade = {CascadeType.PERSIST,
CascadeType.MERGE})
private List<RuleLabel> rules = new ArrayList<>();
...
@Entity
public class RuleLabel implements Serializable {
@Id
@ManyToOne
private Rule rule;
@Id
@ManyToOne
private Label label;
...
@Repository
public interface LabelRepository extends JpaRepository<Label, Long>
...
@Repository
public interface RuleRepository extends JpaRepository<Rule, Long>
...
存储库:
@Entity
@Table(name = "rule")
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "name")
public class Rule implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NaturalId
@NotBlank
@Column(unique = true)
private String name;
@Lob
@Column(columnDefinition = "TEXT")
private String content;
@OneToMany(mappedBy = "rule", cascade = {CascadeType.PERSIST,
CascadeType.MERGE})
private List<RuleLabel> labels = new ArrayList<>();
...
@Entity
@Table(name = "label")
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Label implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
private String name;
@OneToMany(mappedBy = "label", cascade = {CascadeType.PERSIST,
CascadeType.MERGE})
private List<RuleLabel> rules = new ArrayList<>();
...
@Entity
public class RuleLabel implements Serializable {
@Id
@ManyToOne
private Rule rule;
@Id
@ManyToOne
private Label label;
...
@Repository
public interface LabelRepository extends JpaRepository<Label, Long>
...
@Repository
public interface RuleRepository extends JpaRepository<Rule, Long>
...
结果导致堆栈溢出错误
java.lang.StackOverflowError: null
at com.mysql.jdbc.ServerPreparedStatement.getInstance(ServerPreparedStatement.java:332)
...
(LabelRepository以相同的方式工作)
如何修复它?
更新: 将fetch策略更改为Lazy之后
@Id
@ManyToOne(fetch = FetchType.LAZY)
private Rule rule;
@Id
@ManyToOne(fetch = FetchType.LAZY)
private Label label;
无限循环问题已经过去,但新的问题已经出现——相关的实体并没有被填充,并且当Hibernate试图将值插入到链接表中时
Hibernate: insert into rule_label (rule_id, label_id) values (?, ?)
我们得到
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: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'rule_id' cannot be null
是的,因为这就是你告诉hibernate要做的 默认情况下,所有
@ManyToOne
和@OneToOne
关联都是急切的加载的,因此当它查询规则
时,它也会查询规则标签
,然后里面又有规则
,这会导致无限的选择
查询。最好让他们懒惰加载
您可以像这样执行字段延迟加载@ManyToOne(fetch=FetchType.lazy)
这就是默认值的含义:
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
一个很好的关于惰性和渴望加载的方法好吧,我一直使用
embeddedableid
来链接JPA实体。我还没有尝试过您提到的hibernate示例,即使用cascade为我完成这项工作。这可能很有趣,但纯JPA和Spring数据存储库之间存在一些差异。通过使用embeddedableid
可以为链接实体创建一个单独的spring存储库。然后你自己管理这些关系。如果不想这样做,则应使用manytomy
注释,但链接实体允许您创建链接实体属性,此处未显示。这段代码将对您有效,并将您带到B点,您可以从那里进行实验:
@Entity
public class Label {
@Id @GeneratedValue private Long id;
@OneToMany(mappedBy = "ruleLabelId.labelId")
private List<RuleLabel> rules = new ArrayList<>();
@Entity
public class Rule {
@Id @GeneratedValue private Long id;
@OneToMany(mappedBy = "ruleLabelId.ruleId")
private List<RuleLabel> labels = new ArrayList<>();
@Entity
public class RuleLabel {
@EmbeddedId
private RuleLabelId ruleLabelId;
@SuppressWarnings("serial")
@Embeddable
public class RuleLabelId implements Serializable {
private Long ruleId;
private Long labelId;
public interface RuleRepository extends JpaRepository<Rule, Long> {
@Query("from Rule r left join fetch r.labels where r.id = :id")
public Rule getWithLabels(@Param("id") Long id);
}
public interface RuleLabelRepository extends JpaRepository<RuleLabel, RuleLabelId> {}
要粘贴这两个实体的代码需要很长时间,对吗?…对不起,我的错。添加了代码片段,谢谢。您是否有一个toString或其他东西正在打印递归关系、RuleLabel列表和标签?当您询问导致特定异常的特定代码段时,请发布该代码,并发布异常堆栈跟踪。否则,我们只能猜测。只是猜测:如果重写toString()方法,请删除它。默认情况下,它们是延迟加载的。你可以让他们用@ManyToOne(fetch=FetchType.lazy)加载lazy,但这是多余的,不是吗?谢谢,设置@ManyToOne(fetch=FetchType.lazy)有助于避免无限循环,但这会导致另一个问题-相关实体没有被填充,我得到了'Caused by:com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:Column'rule_id'不能为null'@K.Nicholas在Hibernate中默认情况下都是延迟加载的,但在JPA中,manytone和OneToOne是急切的loaded@DmitryTkachev使用更新您的问题新异常和您的查询code@K.Nicholas是的,你是对的,这可能也是一个问题,这就是为什么我让他输入更新查询代码以获得更好的想法。奇怪的是,为了通过保存和更新实现一致的行为,我们需要进行如此剧烈的更改并手动管理链接实体。无论如何-非常感谢,您的解决方案很好!