Je crée mon premier projet basé sur JPA. Mon application comporte plusieurs tables avec des relations de clé étrangère à des fins d'intégrité. De nombreuses tables connexes sont des tables de recherche normalisées.Empêche les mises à jour non désirées de la table en cascade
Tenir compte des tables Person
et Account
, ayant une relation fk sur accountid
:
+-Person----+
| personid | +-Account---+
| accountid*|==============>| accountid |
| ... | | ... |
+-----------+ +-----------+
En l'extrémité avant (JSF), je créé un formulaire pour les nouveaux Person
objets contenant une liste selectOneMenu
avec tous les comptes, et le sélectionné est ajouté à la nouvelle personne qui est insérée correctement dans la base de données.
Cependant, le tableau Account
est également mis à jour automatiquement et je ne trouve pas un moyen d'arrêter cela.
Mes cours sont annotés comme suit:
@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;
...
L'extrémité avant appelle la méthode ci-dessous qui utilise simplement la fonction de EntityManager
persister le nouvel objet Person
fusion():
public String savePerson()
{
try
{
em.getTransaction().begin();
person = em.merge(person);
em.getTransaction().commit();
}
catch (Exception e)
{
...
Ceci conduit à un appel UPDATE
pour chaque objet du tableau Account
.
MISE À JOUR
J'ai enlevé le EntityManager.merge()
appel de mon code et les instructions UPDATE sont toujours là. D'une manière ou d'une autre, le formulaire JSF entraîne des mises à jour automatiques des objets Account
qui le supportent. Les lignes ci-dessous sont de la trace 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
Ces instructions sont exécutées quand je commit()
la transaction. Pour une raison quelconque, la requête et la transaction subséquente amènent JPA à croire que tous les objets Account
du selectOneMenu
sont modifiés; comment puis-je empêcher cela? Détachez-les d'abord un par un?
MISE À JOUR 2
Mon persistence.xml est comme suit:
<?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>
Pendant ce temps, j'ai détaché les objets Account
après que je les interroger dans le backing bean, comme ceci:
public List<Account> getUsrAccounts()
{
List<Account> accts = em.createNamedQuery("Account.findAll", Account.class).getResultList();
for (Account acct : accts)
em.detach(acct);
return accts;
}
Puis, quand Je valide le nouvel objet Person
, seul le Account
lié au nouveau Person
est mis à jour par JPA.Même cela n'est pas souhaitable, et j'ai l'impression que j'utilise mal JPA d'une manière ou d'une autre.
Quelle est la façon normale d'utiliser une table de consultation non modifiable avec JPA?
assez juste. Je trouve difficile de localiser la source des instructions 'UPDATE' en cours d'exécution; le seul appel que fait mon code dans 'EntityManager.merge (Person)' qui ne devrait pas toucher à la liste complète des objets 'Account'. – mvreijn
Jsf lui-même ** jamais ** émet des instructions de mise à jour. C'est toujours la couche jpa qui, à son tour, recherche les objets modifiés/nouvellement créés. Ceux-ci sont probablement créés/modifiés par vous (directement ou à partir de l'interface utilisateur). Ce genre de begaviour est plus facile à déboguer et à tester si vous créez des unittests, cela devient beaucoup plus facile. – Kukeltje
Merci, je comprends que JPA est en train de publier les déclarations; Cependant, je ne comprends pas pourquoi le message du formulaire JSF déclenche des fonctions JPA. Comment puis-je tracer/dépanner la chaîne d'événements? – mvreijn