防止不需要的级联 table 更新
Prevent unwanted cascaded table updates
我正在创建我的第一个基于 JPA 的项目。我的应用程序有几个 table 具有外键关系以实现完整性目的。许多相关的 tables 是规范化查找 tables。
考虑 tables Person
和 Account
,在 accountid
上有 fk 关系:
+-Person----+
| personid | +-Account---+
| accountid*|==============>| accountid |
| ... | | ... |
+-----------+ +-----------+
在前端 (JSF),我为新的 Person
对象创建了一个表单,其中包含一个包含所有帐户的 selectOneMenu
列表,以及选定的 accountid 被添加到正确插入数据库的新 person 中。
但是,Account
table 也会自动更新,我找不到阻止这种情况发生的方法。
我的类注释如下:
@Entity
@Table(name="usr_person")
@NamedQuery(name="Person.findAll", query="SELECT u FROM Person u")
public class Person implements Serializable
{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="person_id")
private int personId;
//bi-directional one-to-one association to UsrAccount
@OneToOne
@JoinColumn(name="account_id",insertable=true,updatable=true)
private Account usrAccount;
...
@Entity
@Table(name="usr_account")
@NamedQuery(name="UsrAccount.findAll", query="SELECT u FROM Account u")
public class Account implements Serializable
{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="account_id")
private int accountId;
//bi-directional one-to-one association to UsrPerson
@OneToOne(mappedBy="usrAccount")
private Person usrPerson;
...
前端调用以下方法,该方法仅使用 EntityManager
的 merge() 函数来持久化新的 Person
对象:
public String savePerson()
{
try
{
em.getTransaction().begin();
person = em.merge( person );
em.getTransaction().commit();
}
catch (Exception e)
{
...
这导致 UPDATE
对 Account
table 中的每个对象调用 。
更新
我从我的代码中删除了 EntityManager.merge()
调用 ,UPDATE 语句仍然存在。 JSF 表单以某种方式导致支持它的 Account
对象的自动更新。以下几行来自 JPA 跟踪:
17448 <snip> openjpa.jdbc.SQL - <t 1905431636, conn 233456683> executing prepstmnt 1172878998 UPDATE usr_account SET loginname = ?, password = ?, privileges = ? WHERE account_id = ? [params=(String) mvreijn, (String) ba5edce0be3a9b8f6ac9b84c72935192b2289b3a341ad432021256c7144b59f4, (int) 90, (int) 1]
17449 <snip> openjpa.jdbc.SQL - <t 1905431636, conn 233456683> [1 ms] spent
17449 <snip> openjpa.jdbc.SQL - <t 1905431636, conn 233456683> executing prepstmnt 386904456 UPDATE usr_account SET loginname = ?, password = ?, privileges = ? WHERE account_id = ? [params=(String) afolmer, (String) ba5edce0be3a9b8f6ac9b84c72935192b2289b3a341ad432021256c7144b59f4, (int) 90, (int) 2]
17450 <snip> openjpa.jdbc.SQL - <t 1905431636, conn 233456683> [1 ms] spent
17450 <snip> openjpa.jdbc.SQL - <t 1905431636, conn 233456683> executing prepstmnt 1322606395 UPDATE usr_account SET loginname = ?, password = ?, privileges = ? WHERE account_id = ? [params=(String) annuska, (String) ba5edce0be3a9b8f6ac9b84c72935192b2289b3a341ad432021256c7144b59f4, (int) 80, (int) 3]
17451 <snip> openjpa.jdbc.SQL - <t 1905431636, conn 233456683> [1 ms] spent
这些语句是在我commit()
交易时执行的。由于某种原因,查询和后续事务导致 JPA 认为 selectOneMenu
中的所有 Account
对象都已修改;我该如何防止这种情况?先一个一个地分离它们?
更新 2
我的persistence.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="matco">
<class>matco.model.Changelog</class>
<class>matco.model.Item</class>
<class>matco.model.ItemAssignment</class>
<class>matco.model.ItemMaintenance</class>
<class>matco.model.LstType</class>
<class>matco.model.LstColor</class>
<class>matco.model.LstCondition</class>
<class>matco.model.LstSize</class>
<class>matco.model.Team</class>
<class>matco.model.Account</class>
<class>matco.model.Person</class>
<class>matco.model.CatBrand</class>
<class>matco.model.CatItem</class>
<class>matco.model.CatPrice</class>
<class>matco.model.CatSupplier</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/matco" />
<property name="javax.persistence.jdbc.user" value="****" />
<property name="javax.persistence.jdbc.password" value="****" />
<!-- TODO remove this in favor of enhancement -->
<property name="openjpa.RuntimeUnenhancedClasses" value="supported"/>
<property name="openjpa.Log" value="DefaultLevel=WARN, Runtime=INFO, Tool=INFO, SCHEMA=TRACE, SQL=TRACE"/>
<property name="openjpa.ConnectionFactoryProperties" value="PrintParameters=true" />
</properties>
</persistence-unit>
</persistence>
同时,在支持 bean 中查询 Account
对象后,我分离了它们,如下所示:
public List<Account> getUsrAccounts()
{
List<Account> accts = em.createNamedQuery( "Account.findAll", Account.class ).getResultList();
for (Account acct : accts)
em.detach(acct);
return accts;
}
然后当我提交新的 Person
对象时,只有链接到新的 Person
的 Account
被 JPA 更新。即使那样也是不可取的,而且我感觉我在某种程度上滥用了 JPA。
在 JPA 中使用非 editable 查找 table 的正常方法是什么?
尝试添加到您的@OneToOne 映射中cascade=CascadeType.REFRESH)
这将指示您的 ORM 框架仅在刷新时级联(在读取实体时)并且在尝试仅保存其中一个实体时不在更新上级联。
没有在您的 persistence.xml 中启用 <property name="openjpa.RuntimeUnenhancedClasses" value="supported"/>
。找出另一个增强策略。
我正在创建我的第一个基于 JPA 的项目。我的应用程序有几个 table 具有外键关系以实现完整性目的。许多相关的 tables 是规范化查找 tables。
考虑 tables Person
和 Account
,在 accountid
上有 fk 关系:
+-Person----+
| personid | +-Account---+
| accountid*|==============>| accountid |
| ... | | ... |
+-----------+ +-----------+
在前端 (JSF),我为新的 Person
对象创建了一个表单,其中包含一个包含所有帐户的 selectOneMenu
列表,以及选定的 accountid 被添加到正确插入数据库的新 person 中。
但是,Account
table 也会自动更新,我找不到阻止这种情况发生的方法。
我的类注释如下:
@Entity
@Table(name="usr_person")
@NamedQuery(name="Person.findAll", query="SELECT u FROM Person u")
public class Person implements Serializable
{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="person_id")
private int personId;
//bi-directional one-to-one association to UsrAccount
@OneToOne
@JoinColumn(name="account_id",insertable=true,updatable=true)
private Account usrAccount;
...
@Entity
@Table(name="usr_account")
@NamedQuery(name="UsrAccount.findAll", query="SELECT u FROM Account u")
public class Account implements Serializable
{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="account_id")
private int accountId;
//bi-directional one-to-one association to UsrPerson
@OneToOne(mappedBy="usrAccount")
private Person usrPerson;
...
前端调用以下方法,该方法仅使用 EntityManager
的 merge() 函数来持久化新的 Person
对象:
public String savePerson()
{
try
{
em.getTransaction().begin();
person = em.merge( person );
em.getTransaction().commit();
}
catch (Exception e)
{
...
这导致 UPDATE
对 Account
table 中的每个对象调用 。
更新
我从我的代码中删除了 EntityManager.merge()
调用 ,UPDATE 语句仍然存在。 JSF 表单以某种方式导致支持它的 Account
对象的自动更新。以下几行来自 JPA 跟踪:
17448 <snip> openjpa.jdbc.SQL - <t 1905431636, conn 233456683> executing prepstmnt 1172878998 UPDATE usr_account SET loginname = ?, password = ?, privileges = ? WHERE account_id = ? [params=(String) mvreijn, (String) ba5edce0be3a9b8f6ac9b84c72935192b2289b3a341ad432021256c7144b59f4, (int) 90, (int) 1]
17449 <snip> openjpa.jdbc.SQL - <t 1905431636, conn 233456683> [1 ms] spent
17449 <snip> openjpa.jdbc.SQL - <t 1905431636, conn 233456683> executing prepstmnt 386904456 UPDATE usr_account SET loginname = ?, password = ?, privileges = ? WHERE account_id = ? [params=(String) afolmer, (String) ba5edce0be3a9b8f6ac9b84c72935192b2289b3a341ad432021256c7144b59f4, (int) 90, (int) 2]
17450 <snip> openjpa.jdbc.SQL - <t 1905431636, conn 233456683> [1 ms] spent
17450 <snip> openjpa.jdbc.SQL - <t 1905431636, conn 233456683> executing prepstmnt 1322606395 UPDATE usr_account SET loginname = ?, password = ?, privileges = ? WHERE account_id = ? [params=(String) annuska, (String) ba5edce0be3a9b8f6ac9b84c72935192b2289b3a341ad432021256c7144b59f4, (int) 80, (int) 3]
17451 <snip> openjpa.jdbc.SQL - <t 1905431636, conn 233456683> [1 ms] spent
这些语句是在我commit()
交易时执行的。由于某种原因,查询和后续事务导致 JPA 认为 selectOneMenu
中的所有 Account
对象都已修改;我该如何防止这种情况?先一个一个地分离它们?
更新 2
我的persistence.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="matco">
<class>matco.model.Changelog</class>
<class>matco.model.Item</class>
<class>matco.model.ItemAssignment</class>
<class>matco.model.ItemMaintenance</class>
<class>matco.model.LstType</class>
<class>matco.model.LstColor</class>
<class>matco.model.LstCondition</class>
<class>matco.model.LstSize</class>
<class>matco.model.Team</class>
<class>matco.model.Account</class>
<class>matco.model.Person</class>
<class>matco.model.CatBrand</class>
<class>matco.model.CatItem</class>
<class>matco.model.CatPrice</class>
<class>matco.model.CatSupplier</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/matco" />
<property name="javax.persistence.jdbc.user" value="****" />
<property name="javax.persistence.jdbc.password" value="****" />
<!-- TODO remove this in favor of enhancement -->
<property name="openjpa.RuntimeUnenhancedClasses" value="supported"/>
<property name="openjpa.Log" value="DefaultLevel=WARN, Runtime=INFO, Tool=INFO, SCHEMA=TRACE, SQL=TRACE"/>
<property name="openjpa.ConnectionFactoryProperties" value="PrintParameters=true" />
</properties>
</persistence-unit>
</persistence>
同时,在支持 bean 中查询 Account
对象后,我分离了它们,如下所示:
public List<Account> getUsrAccounts()
{
List<Account> accts = em.createNamedQuery( "Account.findAll", Account.class ).getResultList();
for (Account acct : accts)
em.detach(acct);
return accts;
}
然后当我提交新的 Person
对象时,只有链接到新的 Person
的 Account
被 JPA 更新。即使那样也是不可取的,而且我感觉我在某种程度上滥用了 JPA。
在 JPA 中使用非 editable 查找 table 的正常方法是什么?
尝试添加到您的@OneToOne 映射中cascade=CascadeType.REFRESH)
这将指示您的 ORM 框架仅在刷新时级联(在读取实体时)并且在尝试仅保存其中一个实体时不在更新上级联。
没有在您的 persistence.xml 中启用 <property name="openjpa.RuntimeUnenhancedClasses" value="supported"/>
。找出另一个增强策略。