开发者

Why does hibernate permit the update of a parent table although update="false" is provided to the many-to-one relationship?

开发者 https://www.devze.com 2023-01-27 08:38 出处:网络
I created a simple case for testing and I don\'t get the expected behavior. I am interested why hibernate chooses to update the parent table (Country) although I provide update=\"false\" attribute to

I created a simple case for testing and I don't get the expected behavior. I am interested why hibernate chooses to update the parent table (Country) although I provide update="false" attribute to the many-to-one relationship (in the Address.hbm.xml)?

The 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;

The hibernate xmls:

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

My Country table has one row (id=1):

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

If I do following:

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);

I expect that I have one new row inserted in the Address table, but I also get an update on the Country table - the only row, the name of the country becomes Slovenia, instead of staying United States (expected behavior <= 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=?

So why does hibernate update the parent table when instructed not to?

Kind regards,

Despot

Edit: Shortly after writing this post I realized that I am confusing the DB's cascade with the hibernate's cascade and that solved all my problems.

The thing is the following: Hibernate clearly says:

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 开发者_StackOverflow社区a trigger or other application.

. This means that when using update="false" on the country property (on the countryId), I say to hibernate you cannot do UPDATE Address SET firstName="x", lastName="y", countryId=123 where condition; Only do an update without the countryId.

Than I saw that the DB's ON UPDATE CASCADE refers to the parent table (Country) - if the Country was updated, than update the child table (the countryId column in Address table). Contrary to what I thought hibernate's update would do, I realized that by providing cascade="save-update", I say to hibebernate, whatever values you have for the Country object in the Address object update them in it's own Country table. So the solution was to have cascade="persist,merge" without the save-update part!


Setting update = "false" on country property doesn't allow you to update the corresponding countryId field, i.e. to choose another Country for particular Address.

If you want to disable chaning of the name of existing Country, you need to specify update = "false" on the corresponding property:

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

精彩评论

暂无评论...
验证码 换一张
取 消