Locking)
During the implementation of business logic, data access needs to be exclusive. For example, in the daily final computing process of the financial system, we want to process the data at a cut-off time point instead of the settlement process (which may be several seconds, or several hours), and the data changes again. At this point, we need to use some mechanisms to ensure that the data will not be modified by the outside world during an operation. This mechanism is called a "Lock" here ", that is, lock the selected target data so that it cannot be modified by other programs. Hibernate supports two lock mechanisms: Pessimistic locking and optimistic
Locking )".
I. pessimistic locking)
Pessimistic lock, as it is called, refers to a conservative attitude towards data being modified by the outside world (including other transactions of the current system, as well as transactions from the external system). Therefore, the data is locked during the entire data processing process. Pessimistic locks often rely on the locks provided by the database (and only the locks provided by the database layer can truly guarantee the exclusive data access; otherwise, even if the locking mechanism is implemented in the system, it cannot be ensured that the external system will not modify the data ). A typical pessimistic lock call for dependent databases: Select * from account where name = "Erica" for update this SQL statement locks the account
All records in the table that meet the search criteria (name = "Erica. Before the transaction is committed (the lock in the transaction process will be released when the transaction is committed), these records cannot be modified by the outside world. The pessimistic lock of Hibernate is also implemented based on the database lock mechanism. The following code locks query records:
String hqlstr = "from Tuser as user where user. name = 'erica '"; query = session. createquery (hqlstr); query. setlockmode ("user", lockmode. upgrade); // lock list userlist = query. list (); // execute the query,
Obtain data query. setlockmode locks the record corresponding to the specific alias in the query statement (we have specified an alias "user" for the Tuser class). In this case, all the returned user records are locked. Observe the SQL statements generated by hibernate during runtime:
1 select tuser0 _. ID as ID, tuser0 _. name as name, tuser0 _. group_id as group_id, tuser0 _. user_type as user_type, tuser0 _. sex as sex from t_user tuser0 _ Where (tuser0 _. name = 'erica ') for update
Here, Hibernate implements the pessimistic lock mechanism by using the for update clause of the database. The locking modes of hibernate include:
? Lockmode. None: No lock mechanism.
? Lockmode. Write: hibernate will automatically obtain the insert and update records.
? Lockmode. Read: hibernate automatically obtains the data when reading the record.
The above three lock mechanisms are generally used internally by hibernate. For example, in order to ensure that objects in the update process are not modified by the outside world, Hibernate will automatically add the write lock to the target object in the implementation of the Save method.
? Lockmode. Upgrade: Use the for update clause to lock the database.
? Lockmode. upgrade_nowait: the specific implementation of Oracle, which uses the Oracle for update Nowait clause to implement locking.
The above two locking mechanisms are commonly used at the application layer. The locking is generally implemented through the following methods:
Criteria. setlockmode
Query. setlockmode
Session. Lock
Note: Only when the lock is set before the query starts (that is, before the hiberate generates the SQL statement) Will the database lock mechanism be used for the lock process. Otherwise, data has been loaded through select SQL that does not contain the for update clause. The so-called database locking is impossible.
2. Optimistic Locking)
Compared with pessimistic locks, optimistic locks adopt a more loose locking mechanism. Pessimistic locks are implemented based on the database lock mechanism in most cases to ensure maximum operation exclusiveness. However, there is a large amount of database performance overhead, especially for long transactions, which is often unbearable. For example, in a financial system, when an operator reads user data and performs Modification on the basis of the read user data (such as changing the user account balance), if a pessimistic lock mechanism is used, this means that the database record is always locked throughout the entire operation process (from the operator reading data, starting modification to submitting modification results, or even the time when the operator goes to coffee preparation, we can imagine what the consequences would be if we were dealing with hundreds of thousands of concurrent jobs.
The Optimistic Locking Mechanism solves this problem to some extent. Optimistic locks are mostly implemented based on the data version record mechanism. What is the data version? Add a version ID for the data. In the database table-based version solution, you can add a "version" field to the database table. When reading the data, read the version number together, and then add one to the version number when updating the data. In this case, the version data of the submitted data is compared with the current version information recorded in the database table. If the submitted data version number is greater than the current version number of the database table, it is updated, otherwise, expired data is considered. For the example of modifying user account information above, assume:
The account information table in the database has a version field. The current value is 1, and the current account balance field (balance) is $100.
1: operator A reads it (version = 1) and deducts $50 ($100-$50) from its account balance ).
2: During operator A's operations, operator B also reads this user information (version = 1) and deducts $20 ($100-$20) from its account balance ).
3: operator A completes the modification and submits the data version number plus one (version = 2), along with the account balance deducted (balance = $50) to the database for updates, at this time, because the submitted data version is larger than the current version of the database record, the data is updated, and the database record version is updated to 2.
4: Operator B completes the operation and tries to submit data (balance = $80) to the database with version 1 (version = 2). However, when comparing the database version, the version number of the data submitted by operator B is 2, and the current version of the database record is 2, which does not meet the optimistic lock policy of "The submitted version must be later than the current version to be updated". Therefore, the submission of operator B is rejected. This avoids the possibility that operator B overwrites the operation result of operator A with the old data modification Result Based on version = 1.
From the above example, we can see that the Optimistic Locking mechanism avoids the database lock overhead in long transactions (no database data lock is applied during operator A and operator B operations ), this greatly improves the overall performance of the system with a large concurrency. It should be noted that the Optimistic Locking Mechanism is often based on the data storage logic in the system, so it also has certain limitations, as in the above example, the Optimistic Locking mechanism is implemented in our system. The updating of user balance from the external system is not controlled by our system, so dirty data may be updated to the database. In the system design phase, we should fully consider the possibility of such situations and make appropriate adjustments (for example, implementing Optimistic Locking policies in database storage processes, only data update based on this stored procedure is available for external users.
Path, instead of making the database table public ). Hibernate has built-in optimistic lock implementation in its data access engine. If you do not need to consider the update operation of the external system's logarithm database, using the transparent optimistic lock provided by hibernate will greatly improve our productivity. In hibernate, the optimistic-lock attribute of the class descriptor can be used together with the version descriptor.
Now, we add an optimistic lock mechanism to the Tuser in the previous example.
1. First, add the optimistic-lock attribute for the Tuser class descriptor:
<Hibernate-mapping>
<Class
Name = "org. hibernate. sample. Tuser"
Table = "t_user"
Dynamic-update = "true"
Dynamic-insert = "true"
Optimistic-lock = "version"
>
......
<>
<>
The optimistic-lock attribute has the following optional values:
? None No optimistic lock
? Version implements optimistic locks through the version Mechanism
? Dirty implements Optimistic Locking by checking changed attributes
? All implement optimistic locks by checking all attributes
Among them, the optimistic lock mechanism implemented through version is the optimistic lock implementation officially recommended by hibernate. It is also in hibernate, currently, the only lock mechanism is effective when the data object is modified out of the session. Therefore, we generally choose the version method as the hibernate optimistic lock implementation mechanism.
2. Add a version attribute descriptor.
Code content:
1 2 <class
3 name = "org. hibernate. sample. Tuser"
4 Table = "t_user"
5 dynamic-update = "true"
6 Dynamic-insert = "true"
7 optimistic-lock = "version"
8>
9 <ID
10 name = "ID"
11 column = "ID"
12 type = "Java. Lang. Integer"
13>
14 <generator class = "native">
15 <>
16 <>
17 <version
18 column = "version"
19 name = "version"
20 type = "Java. Lang. Integer"
21/>
22 ......
23 <>
24 <>
25
Note that the version node must appear after the ID node. Here, we declare a version attribute to store the user's version information and store it in the version field of the Tuser table. At this point, if we try to write a piece of code to update the record data in the Tuser table, such:
Code content:
Criteria = session. createcriteria (Tuser. class); criteria. add (expression. eq ("name", "Erica"); List userlist = criteria. list (); Tuser user = (Tuser) userlist. get (0); transaction Tx = session. begintransaction (); User. setusertype (1); // update the user type field Tx. commit ();
Every time we update the Tuser, we can find that the version in the database is increasing progressively. If we try to start another session before Tx. Commit and operate on the user named Erica to simulate concurrent updates:
Code content:
Session session = getSession();Criteria criteria = session.createCriteria(TUser. class );criteria.add(Expression.eq( " name " , " Erica " ));Session session2 = getSession();Criteria criteria2 = session2.createCriteria(TUser. class );criteria2.add(Expression.eq( " name " , " Erica " ));List userList = criteria.list();List userList2 = criteria2.list();TUser user = (TUser)userList.get( 0 );TUser user2 = (TUser)userList2.get( 0 );Transaction tx = session.beginTransaction();Transaction tx2 = session2.beginTransaction();user2.setUserType( 99 );tx2.commit();user.setUserType( 1 );tx.commit();
Run the above Code. The Code will throw staleobjectstateexception at Tx. Commit () and indicates that the version check fails. The current transaction is trying to submit an expired data. By capturing this exception, we can handle it when optimistic lock validation fails.