Hibernate在尝试懒加载获取OneToOne关联时可能会引发EntityExistsException。

huangapple go评论61阅读模式
英文:

Hibernate EntityExistsException if trying to fetch OneToOne association lazily

问题

I have the bidirectional OneToOne association Account <-> Budget.
根据此链接,我尝试懒加载该关联:

对于 Account.java

@Audited
@Entity
public class Account {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE)
  protected long id;

  @OneToOne(mappedBy = "account", cascade = CascadeType.ALL, orphanRemoval=true, fetch=FetchType.LAZY)
  private Budget mainBudget;
} 
...

对于 Budget.java

@Audited
@Entity
public class Budget {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE)
  protected long id;

  @OneToOne(fetch=FetchType.LAZY)
  @MapsId
  private Account account;

  @OneToMany(targetEntity = Budget.class, cascade = CascadeType.ALL, 
    mappedBy = "parentBudget", orphanRemoval=true)
  @Fetch(value = FetchMode.SUBSELECT)
  private List<Budget> subBudget; 
...
} 

现在我正在尝试使用 DAOClass 中的 create 方法创建一个新的带有新预算的帐户,如下所示:

@Resource(name = "DBRouter", type = DatabaseRouter.class)
protected DatabaseRouter router;

@PersistenceContext
protected EntityManager em;

@Transactional
public D create(D d, boolean flush, String resource) {
  router.setDataSource(resource);
  if (flush) {
    em.flush();
  }
  D obj = em.merge(d);
  if (flush) {
    em.flush();
  }
  return obj;
}

这给我带来了以下异常:

javax.persistence.EntityExistsException: 会话中已经关联了具有相同标识符值的不同对象 : [[包名].Account#50](31 行内部代码)
	at org.apache.openejb.persistence.JtaEntityManager.merge(JtaEntityManager.java:203)
	at [包名].DAOClass.create(DaoClass.java:75)
	at [包名].DaoClass.create(DaoClass.java:66)
...

如果我删除 @MapsId 注释,那么我就不会遇到这个问题,尽管懒加载不正常工作。这个异常的原因是什么,如何解决它?

英文:

I have the bidirectional OneToOne association Account &lt;-&gt; Budget.
According to this I tried to lazily load the association:

For the Account.java:

@Audited
@Entity
public class Account {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE)
  protected long id;

  @OneToOne(mappedBy = &quot;account&quot;, cascade = CascadeType.ALL, orphanRemoval=true, fetch=FetchType.LAZY)
  private  Budget mainBudget;
  } 
...
}

For the Budget.java:

@Audited
@Entity
public class Budget {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE)
  protected long id;

  @OneToOne(fetch=FetchType.LAZY)
  @MapsId
  private Account account;

  @OneToMany(targetEntity = Budget.class, cascade = CascadeType.ALL, 
    mappedBy = &quot;parentBudget&quot;, orphanRemoval=true)
  @Fetch(value = FetchMode.SUBSELECT)
  private  List&lt;Budget&gt; subBudget; 
...
} 

Now I am trying to make a new account with a new budget with the create-method in the DAOClass looking as follows:

@Resource(name = &quot;DBRouter&quot;, type = DatabaseRouter.class)
protected DatabaseRouter router;

@PersistenceContext
protected EntityManager em;

@Transactional
public D create(D d, boolean flush, String resource) {
  router.setDataSource(resource);
  if (flush) {
    em.flush();
  }
  D obj = em.merge(d);
  if (flush) {
    em.flush();
  }
  return obj;
}

This gives me the following exception:

javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [[PACKACKENAMES].Account#50](31 internal lines)
	at org.apache.openejb.persistence.JtaEntityManager.merge(JtaEntityManager.java:203)
	at [PACKACKENAMES].DAOClass.create(DaoClass.java:75)
	at [PACKACKENAMES].DaoClass.create(DaoClass.java:66)
...

If I remove the @MapsId annotation, then I don't have this problem, although then the lazy loading does not work properly. What is the reason for this exception and how to solve it?

答案1

得分: 1

在您的情况下,"Account" 是 "Budget" 的父实体。@MapsId 注解告诉 JPA 使用父实体的 ID 来映射带有注解的一对一关系。关联实体(行)的两个 ID 将是相同的。因此,不需要为 "Budget" 生成单独的 ID。从异常信息可以看出,即使生成了单独的 ID,也无法正常工作。

解决方案 - 从 "Budget" 实体中移除 @GeneratedValue 注解。

public class Budget {
    @Id
    private long id;

    @OneToOne(fetch=FetchType.LAZY)
    @MapsId
    private Account account;
    ...
}
英文:

In your case Account is a parent entity for Budget. @MapsId annotation says JPA to use the id of the parent entity for the mapping of annotated one-to-one relationship. Both ids will be identical for associated entities (rows). There is no need to generate a separate id for Budget. As we can see from the exception it would even not work.

Solution - remove the @GeneratedValue annotation from the Budget entity.

public class Budget {
    @Id
    private long id;

    @OneToOne(fetch=FetchType.LAZY)
    @MapsId
    private Account account;
...
}

答案2

得分: 0

你有:

public class Budget {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE)
  protected long id;

  @OneToOne(fetch=FetchType.LAZY)
  @MapsId
  private Account account;

但是这个映射不合理。id不能是一个@GeneratedValue,如果它被映射为与Account的一对一关联。

所以:

  1. 移除@GeneratedValue注解,同时
  2. 确保你明确地将id字段设置为关联的Accountid字段的值。

或者,另一种选择是:

  • 如果你想要一个与主键分离的外键列,就不要使用@MapsId

另请参考:

https://stackoverflow.com/questions/76223714/hibernate-error-entityexistsexception-when-persisting-entity-with-children-mul/76226592#76226592

这是一个类似的问题。

英文:

You have:

public class Budget {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE)
  protected long id;

  @OneToOne(fetch=FetchType.LAZY)
  @MapsId
  private Account account;

But this mapping doesn't make sense. The id can't be a @GeneratedValue if it's mapped by the one-to-one association to Account.

So:

  1. remove the @GeneratedValue annotation, and also
  2. make sure you explicitly set the id field to the value of the id field of the associated Account.

Or, alternatively:

  • just don't use @MapsId, if what you want is a foreign key column that is separate from the primary key.

See also:

<https://stackoverflow.com/questions/76223714/hibernate-error-entityexistsexception-when-persisting-entity-with-children-mul/76226592#76226592>

Which is a similar problem.

huangapple
  • 本文由 发表于 2023年4月7日 01:45:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/75952366.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定