2015-08-07 4 views
1

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 UPDATEpour 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?

+0

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

+0

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

+0

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

Répondre

1

Ne pas avoir <property name="openjpa.RuntimeUnenhancedClasses" value="supported"/> activé dans votre persistence.xml. Déterminez une autre stratégie d'amélioration.

+0

Je sais que ça ne devrait pas rester comme ça - d'où le rappel TODO pour moi-même. L'amélioration va-t-elle résoudre mon problème? – mvreijn

+0

RuntimeUnenhancedClasses a des bugs désagréables ... c'est là que je commencerai. – Rick

+0

J'ai ajouté l'amélioration à mon projet qui était aussi agréable que le travail de rootcanal. ** MAIS il a résolu le problème! ** Plus de mises à jour pour les tables qui n'ont pas été touchées. Merci beaucoup. – mvreijn

1

Essayez d'ajouter à vos applications de @OneToOne cascade=CascadeType.REFRESH) Cela indiquera à votre cadre ORM en cascade que sur l'actualisation (alors que l'entité de lecture) et de ne pas en cascade sur les mises à jour en essayant de sauver une seule de ces entités.

+0

Merci, j'ai essayé votre suggestion mais toujours la table 'Account' entière est mise à jour sur' INSERT' de la table 'Person'. Voir ma modification pour les lignes de trace réelles. – mvreijn

+0

Il semble que votre cadre de l'APP peut être configuré pour autocommit en quelque sorte, quel moteur ORM utilisez-vous dans votre application? Pouvez-vous poster sa config globale? comme le fichier persistence.xml? – guilhebl

+0

J'ai posté les 'persistence.xml', voir MISE À JOUR 2. – mvreijn