JPA 持久保存来自多对多关系的已持久对象

发布于 12-07 12:36 字数 1208 浏览 0 评论 0原文

我在类 A 和类 B 之间有一个 @ManyToMany 关系:类 A 引用类 B 实例的集合,并且此关系配置为 CascadeType.ALL。因此,当 A 通过实体管理器持久化时,A 引用的 B 实例也会被持久化。

A 和 B 都有一个使用 GenerationType.IDENTITY 策略声明的 ID,以使用 MySQL 数据库中的 auto_inc。

问题是:

  1. 我创建一个新的 A
  2. 我使用 JPQL 从实体管理器加载一些现有的 B 对象
  3. 我将 B 对象添加到 A 的 B 集合

=> JPA 尝试保留 B 对象,尽管它们已经被持久化(它们刚刚被加载)。

我不明白为什么它会尝试再次保留它们,它们已正确加载,并且它们自动生成的 id 也已正确加载。

ManyToMany 声明:

@ManyToMany(cascade={CascadeType.ALL})
@JoinTable(name = "ENTRY_ENTITIES", joinColumns =
@JoinColumn(name = "ENTRY", referencedColumnName = "ID"),
inverseJoinColumns = @JoinColumn(name = "ENTITY", referencedColumnName = "ID"))
private List<Entity> entities;

从数据库加载现有对象的查询:

T result = (T) entityManager.createQuery("SELECT x FROM " + entityName + " x WHERE x.externalId='" + externalId + "'").getSingleResult();

持久化:

UserTransaction transaction = getTransaction();
try {
    transaction.begin();
    entityManager.persist(entity);
    transaction.commit();
} catch (Throwable t) {
    Logger.getLogger(JpaDao.class.getName()).log(Level.SEVERE, null, t);
}

非常感谢您的帮助!

I have a @ManyToMany relationship between class A and class B : class A references a collection of class B instances, and this relationship is configured as CascadeType.ALL. So when a A is persisted with the entity manager, the B instances referenced by A are also persisted.

Both A and B have an ID declared with the GenerationType.IDENTITY strategy to use the auto_inc from the MySQL database.

The problem is :

  1. I create a new A
  2. I load some existing B objects from entityManager using JPQL
  3. I add the B objects to A's collection of Bs

=> JPA tries to persist the B objects though they are already persisted (they just have been loaded).

I don't understand why it tries to persist them again, they have been properly loaded, and their auto generated id has been well loaded as well.

The ManyToMany declaration :

@ManyToMany(cascade={CascadeType.ALL})
@JoinTable(name = "ENTRY_ENTITIES", joinColumns =
@JoinColumn(name = "ENTRY", referencedColumnName = "ID"),
inverseJoinColumns = @JoinColumn(name = "ENTITY", referencedColumnName = "ID"))
private List<Entity> entities;

The Query to load existing objects from database :

T result = (T) entityManager.createQuery("SELECT x FROM " + entityName + " x WHERE x.externalId='" + externalId + "'").getSingleResult();

The persisting :

UserTransaction transaction = getTransaction();
try {
    transaction.begin();
    entityManager.persist(entity);
    transaction.commit();
} catch (Throwable t) {
    Logger.getLogger(JpaDao.class.getName()).log(Level.SEVERE, null, t);
}

Thanks a lot for your help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

陪你到最终2024-12-14 12:36:30

我不确定这是否是您问题的原因,但您应该将整个事情包含在交易中。不仅仅是持久性部分:

start transaction
load B from DB
create new A
add B to A
commit transaction

正如我在评论中所说,您还有其他设计和编码问题:

  • CascadeType.ALL 在 ManyToXxx 关联上是错误的。您不希望在删除 A 时删除 A 的所有 B,因为这些 B 被其他 A 引用。这将导致约束违规(在最好的情况下)或数据库不一致(在最坏的情况下,如果您没有定义约束)
  • 不要在查询中使用字符串连接。使用参数化查询。这将避免引用问题和注入攻击:SELECT x FROM A x WHERE x.externalId = :externalId

I'm not absolutely sure that this is the cause of your problem, but you should include the whole thing inside of a transaction. Not just the persistence part:

start transaction
load B from DB
create new A
add B to A
commit transaction

As I said in my comments, you have other design and coding problems:

  • CascadeType.ALL is wrong on a ManyToXxx association. You don't want all the Bs of an A deleted when you delete A, since those Bs are referenced by other As. This will lead to constraint violations (in the best case) or an inconsistent database (in the worst case, if you have no constraint defined)
  • Don't use string concatenation in your queries. Use parameterized queries. This will avoid quoting problems and injection attacks: SELECT x FROM A x WHERE x.externalId = :externalId
眼眸里的那抹悲凉2024-12-14 12:36:30

在您将它们添加到 A 时,这些 B 实体可能是另一个持久性上下文的一部分。您是否尝试过使用 在启动事务之后、 将 B 实体添加到 A 实体之前对 B 实体进行合并操作。

Those B entities may be part of another persistence context at the point in time that you are adding them to A. Have you tried using the merge operation on the B entities after starting the transaction, before adding them to your A entity.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文