2010-11-30 34 views
0

J'ai créé un cas simple pour tester et je n'obtiens pas le comportement attendu. Je m'intéresse pourquoi hibernate choisit de mettre à jour la table parente (Country) bien que je fournisse l'attribut update = "false" à la relation many-to-one (dans le fichier Address.hbm.xml)?Pourquoi hibernate permet-il la mise à jour d'une table parent bien que update = "false" soit fourni à la relation many-to-one?

DDLS:

CREATE TABLE Country (
    `id` smallint NOT NULL auto_increment, 
    `name` varchar(100) NOT NULL, 
    PRIMARY KEY (`id`) 
) TYPE=InnoDB AUTO_INCREMENT=0; 
CREATE TABLE Address (
    `id` bigint NOT NULL auto_increment, 
    `firstName` varchar(100) NOT NULL, 
    `lastName` varchar(100) NOT NULL, 
    `countryId` smallint NOT NULL, 
    PRIMARY KEY (`id`), 
    FOREIGN KEY (countryId) 
    REFERENCES Country(id) 
    ON UPDATE CASCADE ON DELETE RESTRICT 
) TYPE=InnoDB AUTO_INCREMENT=0; 

Les xmls de mise en veille prolongée:

<class name="Country" table="Country" lazy="false"> 
    <id name="id" column="id" type="integer"> 
     <generator class="native" /> 
    </id> 
    <property name="name" type="string" not-null="true" length="100"/> 
</class> 
<class name="Address" table="Address" > 
    <id name="id" column="id" type="long" unsaved-value="-1"> 
     <generator class="native" /> 
    </id> 
    <property name="addressFirstName" column="firstName" type="string" not-null="true" length="100"/> 
    <property name="addressLastName" column="lastName" type="string" not-null="true" length="100"/> 
    <!-- Country is parent table, Address is child table (holds the FK countryId). 
     From MySQL: 
     CASCADE: Update the row from the parent table and automatically update the matching rows in the child table. 
     RESTRICT: Rejects the delete operation for the parent table. 
     From hibernate: 
     1. cascade (optional): specifies which operations should be cascaded from the parent object to the associated object. 
     2. update, insert (optional - defaults to true): specifies that the mapped columns should be included in SQL UPDATE 
     and/or INSERT statements. Setting both to false allows a pure "derived" association whose value is initialized 
     from another property that maps to the same column(s), or by a trigger or other application. 
    --> 
    <many-to-one name="country" class="Country" column="countryId" 
     not-null="true" cascade="persist,merge,save-update" update="false" /> 
    <many-to-one name="state" class="State" column="stateId" 
     cascade="persist,merge,save-update" update="false" /> 
</class> 

Ma table de pays a une ligne (id = 1):

INSERT INTO Country (name) VALUES ("United States"); 

Si je suit:

Country country = getCountryFrom("US"); 
Address address = new Address(); 
address.setAddressFirstName("Deksa"); 
address.setAddressLastName("Jakim"); 
country.setName("Slovenia"); 
address.setCountry(country); 
address.setId(-1); 
saveAddress(address); 
session.saveOrUpdate(address); 

Je m'attends à avoir une nouvelle ligne insérée dans la table Adresse, mais je reçois également une mise à jour sur la table Pays - la seule ligne, le nom du pays devient Slovénie, au lieu de rester aux États-Unis (comportement attendu < = update = "false"):

Hibernate: select country0_.id as id6_, country0_.name as name6_ from Country country0_ where country0_.stringId='US' 
Hibernate: insert into Address (firstName, lastName, countryId) values (?, ?, ?) 
Hibernate: update Country set name=? where id=? 

Alors, pourquoi veille prolongée mettre à jour la table parent lorsqu'il reçoit des instructions de ne pas?

Cordialement,
Despot

Edit: Peu de temps après avoir écrit ce post, je compris que je confonds la cascade de la DB avec la cascade de la mise en veille prolongée et qui a résolu tous mes problèmes.
La chose est la suivante: Hibernate dit clairement:

update, insert (optionnel - par défaut true): spécifie que les cartographiés colonnes devraient être inclus dans SQL UPDATE et/ou INSERT. La définition de both à false permet une association "dérivée" pure dont la valeur est initialisée à partir d'une autre propriété que correspond à la ou aux mêmes colonnes, ou par un déclencheur ou une autre application.

. Cela signifie que lorsque vous utilisez update = "false" sur la propriété country (sur le countryId), je dis hibernate vous ne pouvez pas faire UPDATE Adresse SET firstName = "x", lastName = "y", countryId = 123 où condition; Seulement faire une mise à jour sans le countryId. Puis j'ai vu que la CASCADE ON UPDATE de la BD fait référence à la table parente (Country) - si le pays a été mis à jour, alors mettez à jour la table enfant (la colonne countryId dans la table Address). Contrairement à ce que je pensais que la mise à jour d'hibernate ferait, j'ai réalisé qu'en fournissant cascade = "save-update", je dis hibebernate, quelles que soient les valeurs de l'objet Country dans l'objet Address les mettent à jour dans sa propre table Country. Donc la solution était d'avoir cascade = "persister, fusionner" sans la partie save-update!

Répondre

1

Réglage update = "false" sur la propriété country ne vous permet pas de mettre à jour le champ countryId correspondant, à savoir de choisir un autre Country pour particulier Address.

Si vous souhaitez désactiver chaning du nom existant Country, vous devez spécifier update = "false" sur la propriété correspondante:

<class name="Country" table="Country" lazy="false"> 
    ... 
    <property name="name" type="string" not-null="true" length="100" update = "false" /> 
</class> 
+0

Je voulais interdire la mise à jour du pays (objet enfant), ne pas interdire un enfant propriété de l'objet. Dans votre cas, si j'ai une autre Classe (par exemple Continent), cela a le Pays comme objet enfant/membre, et si je voulais permettre à cette classe parente (Continent) de mettre à jour le Pays, ce ne serait pas possible. En d'autres termes, je pense que de cette façon, vous allez désactiver la mise à jour de la propriété du nom du pays de partout, ce qui n'est pas ce que je veux. En tout cas merci pour la réponse! – despot