Updating and Inserting Data

Update and insert operations are very simple to program; actually we have very little code to write. All we need to do is to make sure our mappings are correct; we can then leave Hibernate to do all the hard work. Let's start with the TestDao.save(Test) implementation (see Listing 9-23).

Listing 9-23: TestDao.save(Test) Implementation

image from book
package com.apress.prospring.ch9.data;      import java.util.List;      import org.springframework.orm.hibernate.support.HibernateDaoSupport;      import com.apress.prospring.ch9.domain.Test;          public class HibernateTestDao extends HibernateDaoSupport implements TestDao {          public void save(Test test) {         getHibernateTemplate().saveOrUpdate(test);     }     // other methods omitted for clarity }
image from book

This code is very simple. Hibernate decides whether to perform an insert or an update based on the value of the property defined in the ID element.

In Listing 9-24, we look at the mapping of the Test domain object's identity property. Hibernate uses the value of the identity property to determine whether it needs to perform an insert or an update.

Listing 9-24: Hibernate Mapping File for the Test Object

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC         "-//Hibernate/Hibernate Mapping DTD//EN"         "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">      <hibernate-mapping auto-import="true">          <class name="com.apress.prospring.ch9.domain.Test" table="Test">         <id name="testId" column="TestId" type="int" unsaved-value="0">             <generator bold">sequence">                 <param name=" sequence"> Test_TestId_Seq</param>             </generator>         </id>         <!-- etc -->     </class>      </hibernate-mapping> 
image from book

The mapping defines that the primary key is stored in a property named testId, with an unsaved value of 0. The saveOrUpdate() method checks the primary key property, and if the value is equal to the value specified in the unsaved-value attribute, Hibernate performs an insert operation; otherwise, it performs an update. To demonstrate this, in Listing 9-25, we add a few lines to the Main class.

Listing 9-25: Main Class Implementation Showing the TestDao Method Usage

image from book
package com.apress.prospring.ch9;      public class Main {          private ApplicationContext context;              private void test() {         TestDao testDao = (TestDao)context.getBean("testDao");         Test test = new Test();         test.setName("name");         test.setRunDate(new Date());         testDao.save(test);     }          // the rest of the code omitted for clarity      }
image from book

When we instantiate the Test object, testId is initialized to the default value for the int primitive, which is 0. The value 0 is also defined in the unsaved-value attribute in the mapping file, which means that a subsequent call to testDao.save(test) performs the SQL operations shown in Listing 9-26.

Listing 9-26: SQL Statements Generated by the insert Operation

image from book
select nextval ('Test_TestId_Seq') insert into Test (Name, RunDate, TestId) values (?, ?, ?)
image from book

Hibernate correctly identifies that the Test domain object is not saved in the database yet, and it selects the next value for the primary key from the sequence and performs the insert operation.

If we set the testId property to a value other than 0, Hibernate tries to perform an update. Hibernate does not check for concurrent updates, but it does check the number of rows updated, which must be 1. Any other value indicates that you tried to update a row that does not exist. The code fragment in Listing 9-27 simulates an update of a nonexistent row.

Listing 9-27: Code Fragment from Main.test() Method

image from book
Test test = new Test(); test.setTestId(-1); test.setName("name"); test.setRunDate(new Date()); testDao.save(test); 
image from book

If we run the code fragment in Listing 9-27, Hibernate throws a HibernateException to indicate that the number of update rows is incorrect.

Concurrency

Hibernate supports versioned data for optimistic locking. The concept of optimistic locking means that the database allows many users to read a row, but it allows only the first user to perform an update. All other users need to reread the data in order to save it. This allows for much higher performance, but it may inconvenience users. However, in most cases, versioned concurrency offers the best performance while keeping the implementation simple and requires only one additional column in each versioned table.

Hibernate offers two ways of versioning the data: by version number or by timestamp. The concept of version numbers is quite simple; each row in the table has an additional column that specifies the version of the row. An update operation is allowed only if the version in the domain object matches the version in the table. An update operation also increases the version number.

Timestamping works in a similar way. Each row has a column that specifies the last time the row was saved. An update operation fails if the timestamp value in the table is newer than the timestamp in the domain object.

Because both methods require an additional column in each row, we recommend that you use versioning because it is a more reliable way of ensuring the data integrity.

The code in Listing 9-28 adds versioning support to our Test table and Test domain object.

Listing 9-28: Alter Script for the Test Table

image from book
alter table Test add column Version int not null default 0;
image from book

Now we need to modify the Test object, add an int version property, and modify the mapping file by adding the version element (see Listing 9-29).

Listing 9-29: Version Element in the Mapping File

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC         "-//Hibernate/Hibernate Mapping DTD//EN"         "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">      <hibernate-mapping auto-import="true">          <class name="com.apress.prospring.ch9.domain.Test" table="Test">         <id name="testId" column="TestId" type="int" unsaved-value="0">             <generator >                 <param name="sequence">Test_TestId_Seq</param>             </generator>         </id>         <version name="version" column="Version" unsaved-value="negative"/>         <property name="name" column="Name"/>         <property name="runDate" column="RunDate"/>     </class>      </hibernate-mapping>
image from book

Provided that we have added the version property to the Test domain object and have run the alter SQL script, we can now check that versioning works (see Listing 9-30).

Listing 9-30: test() Method in Main

image from book
package com.apress.prospring.ch9;      public class Main {          private void test() {         TestDao testDao = (TestDao)context.getBean("testDao");         Test test1, test2;         test1 = testDao.getById(1);         test2 = testDao.getById(1);         test1.setName("new Name");         test2.setName("other name");         testDao.save(test1);         testDao.save(test2);     }          // other methods omitted for clarity }
image from book

The code in Listing 9-30 simulates concurrent updates. If test1 is loaded by Alice and test2 by Bob, then Alice is allowed to update the record, but Bob's update is rejected. However, if Bob saved the record first, Alice's changes are rejected. The output of running the build script shown here clearly demonstrates that the version is checked and that testDao.save(test2) throws an exception:

select test0_.TestId as TestId0_, test0_.Version as Version0_,      test0_.Name as Name0_, test0_.RunDate as RunDate0_ from Test test0_  where test0_.TestId=? select test0_.TestId as TestId0_, test0_.Version as Version0_,      test0_.Name as Name0_, test0_.RunDate as RunDate0_ from Test test0_  where test0_.TestId=? update Test set Version=?, Name=?, RunDate=? where TestId=? and Version=? update Test set Version=?, Name=?, RunDate=? where TestId=? and Version=? StaleObjectStateException:27 - An operation failed due to stale data net.sf.hibernate.StaleObjectStateException: ¿ Row was updated or deleted by another transaction ¿ (or unsaved-value mapping was incorrect) for ¿ com.apress.prospring.ch9.domain.Test instance with identifier: 1 

As you can see, implementing optimistic locking is very simple, but you must add another column to each table that contains versioned data.

There is another way to implement optimistic locking that may (in very few cases) further improve user experience, but it requires much more memory; in this case, in addition to "working" values, you also need to keep original values. Also, when you are performing an update, you need to check that the original values match in the where clause. Because you are comparing the values of the columns in the database and the original values stored in the domain object, you do not need to add an additional column, but the domain objects require double the memory to store the original and new property values.

Why Hibernate?

You have seen how Hibernate can speed up the development cycle. It is quite amazing how quickly you can implement changes in the data structure. Hibernate also supports persisted enum types, which is very useful for object canonicalization.

Of course, all this flexibility comes at a price. First of all, you are somewhat limited by the fact that you cannot manually optimize the SQL statements. You may also find that some of the features of the framework influence your decisions about the design of the application, sometimes even resulting in implementing the application in a certain way, not because it is the best way to do it, but because you know that Hibernate works well with that design.

Perhaps the worst part of Hibernate is that you cannot easily control the data you are selecting, and this can lead to some very inefficient queries. This is especially true for 1:N and M:N selects, which can get too complex. However, this is not a fault of Hibernate; it is a feature of the technology. If you do not want to start writing messy JDBC code, but you want to get more control over the object creation and SQL code, read Chapter 10, which deals with iBATIS.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net