Hibernate,JPA注解@Version

Hibernate实现悲观锁和乐观锁。

1,悲观锁

用例代码如下:

  • 数据库DDL语句:
  • hibernate.cfg.xml
  • java类

以上代码(除下面的main之外)同乐观锁。

main

 package a3_Version;
import org.hibernate.LockOptions;
import org.hibernate.Session;
import daoUtil.HibernateUtil; public class Test_pessiLock { public static void main(String[] args) {
Session session = HibernateUtil.getSession(); try {
Cat cat = (Cat)session.get(Cat.class, "8a6cc5a34c54de57014c54de588e0000", LockOptions.UPGRADE); System.out.println("这行设置断点,到数据库");
System.out.println("使用SQL:select * from CAT t WHERE T.ID='"+cat.getId()+"' FOR UPDATE");
System.out.println("验证CAT表,ID='"+cat.getId()+"'的行数据是否被锁住了。");
} catch (RuntimeException e) {
throw e;
} finally {
session.close();
}
}
}

2,乐观锁

JPA通过@Version添加对表数据的乐观锁定的支持

根据EJB3规范,version列可以是numeric类型(推荐方式)也可以是timestamp类型. Hibernate支持任何自定义类型,只要该类型实现了UserVersionType.

用例代码如下:

  • 数据库DDL语句:
 create table CAT
(
id VARCHAR2(32 CHAR) not null,
create_time TIMESTAMP(6),
update_time TIMESTAMP(6),
cat_name VARCHAR2(255 CHAR),
version NUMBER(10) not null
)
  • hibernate.cfg.xml
 <?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 数据库驱动配置 -->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@127.0.0.1:1521:orcl</property>
<property name="connection.username">wxuatuser</property>
<property name="connection.password">xlh</property>
<property name="show_sql">true</property>
<!-- 自动执行DDL属性是update,不是true -->
<property name="hbm2ddl.auto">update</property>
<!-- hibernate实体类 --> <mapping class="a3_Version.Cat"/> </session-factory>
</hibernate-configuration>
  • java类

实体类 - 基类

 package model;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import org.hibernate.annotations.GenericGenerator;
/**
* 实体类 - 基类
*/
@MappedSuperclass
public class BaseEntity implements Serializable { private static final long serialVersionUID = -6718838800112233445L; private String id;// ID
private Date create_time;// 创建日期
private Date update_time;// 修改日期
@Id
@Column(length = 32, nullable = true)
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Column(updatable = false)
public Date getCreate_time() {
return create_time;
}
public void setCreate_time(Date create_time) {
this.create_time = create_time;
}
public Date getUpdate_time() {
return update_time;
}
public void setUpdate_time(Date update_time) {
this.update_time = update_time;
}
@Override
public int hashCode() {
return id == null ? System.identityHashCode(this) : id.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass().getPackage() != obj.getClass().getPackage()) {
return false;
}
final BaseEntity other = (BaseEntity) obj;
if (id == null) {
if (other.getId() != null) {
return false;
}
} else if (!id.equals(other.getId())) {
return false;
}
return true;
}
}

实体类

 package a3_Version;
import javax.persistence.Entity;
import javax.persistence.Version;
import model.BaseEntity;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate; @Entity
@DynamicInsert
@DynamicUpdate
public class Cat extends BaseEntity{
/**
* 实体类
*/
private static final long serialVersionUID = -2776330321385582872L; private String cat_name; private int version;
@Version
public int getVersion() {
return version;
} public void setVersion(int version) {
this.version = version;
} public String getCat_name() {
return cat_name;
} public void setCat_name(String cat_name) {
this.cat_name = cat_name;
}
}

Dao

 package daoUtil;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder; public class HibernateUtil { private static final SessionFactory sessionFactory; static {
try {
Configuration cfg = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(cfg.getProperties()).buildServiceRegistry();
sessionFactory = cfg.buildSessionFactory(serviceRegistry);
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
} public static Session getSession() throws HibernateException {
return sessionFactory.openSession();
} public static Object save(Object obj){
Session session = HibernateUtil.getSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.save(obj);
tx.commit();
} catch (RuntimeException e) {
if (tx != null) {
tx.rollback();
}
throw e;
} finally {
session.close();
}
return obj;
} public static void delete(Class<?> clazz,String id){
Session session = HibernateUtil.getSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Object obj = session.get(clazz,id);
session.delete(obj);
tx.commit();
} catch (RuntimeException e) {
if (tx != null) {
tx.rollback();
}
throw e;
} finally {
session.close();
}
}
}

main

 package a3_Version;
import java.util.ArrayList;
import java.util.Iterator;
import org.hibernate.Session;
import org.hibernate.StaleObjectStateException;
import org.hibernate.Transaction;
import a3_Version.Cat;
import daoUtil.HibernateUtil; public class Test_optiLock extends Thread { private String transactionType;
private Log log;
private String id; public Test_optiLock(String transactionType, Log log,String id) {
this.transactionType = transactionType;
this.log = log;
this.id = id;
} public Test_optiLock() {} public void run() {
try {
if (transactionType.equals("modify"))
modify(id);
else
update(id);
} catch (Exception e) {
e.printStackTrace();
}
} public void modify(String id) throws Exception {
Session session = HibernateUtil.getSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
log.write("modify():开始事务");
Thread.sleep(500); Cat cat = (Cat) session.get(Cat.class, id); log.write("modify():查询到cat_name为:" + cat.getCat_name());
Thread.sleep(500); cat.setCat_name(cat.getCat_name()+"modify");
log.write("modify():把cat_name改为:" + cat.getCat_name()); tx.commit();
log.write("modify():提交事务");
Thread.sleep(500);
} catch (StaleObjectStateException e) {
if (tx != null) {
tx.rollback();
}
e.printStackTrace();
System.out.println("cat_name已被其他事务修改,本事务被撤销,请重新开始modify事务");
log.write("modify():cat_name已被其他事务修改,本事务被撤销");
} catch (RuntimeException e) {
if (tx != null) {
tx.rollback();
}
throw e;
} finally {
session.close();
}
} public void update(String id) throws Exception {
Session session = HibernateUtil.getSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
log.write("update():开始事务");
Thread.sleep(500); Cat cat = (Cat) session.get(Cat.class, id); log.write("update():查询到cat_name为:" + cat.getCat_name());
Thread.sleep(500); cat.setCat_name(cat.getCat_name()+"update");
log.write("update():把cat_name改为:" + cat.getCat_name()); tx.commit();
log.write("update():提交事务");
Thread.sleep(500);
} catch (StaleObjectStateException e) {
if (tx != null) {
tx.rollback();
}
e.printStackTrace();
System.out.println("cat_name已被其他事务修改,本事务被撤销,请重新开始update事务");
log.write("update():cat_name已被其他事务修改,本事务被撤销");
} catch (RuntimeException e) {
if (tx != null) {
tx.rollback();
}
throw e;
} finally {
session.close();
}
} public static void main(String args[]) throws Exception {
Cat cat = new Cat();
cat.setCat_name("test3@optiLock");
HibernateUtil.save(cat); Log log = new Log();
String id = cat.getId();
Thread modifyThread = new Test_optiLock("modify", log ,id);
Thread updateThread = new Test_optiLock("update", log ,id); modifyThread.start();
updateThread.start(); while (modifyThread.isAlive() || updateThread.isAlive()) {
Thread.sleep(100);
}
log.print();
}
} class Log {
private ArrayList<String> logs = new ArrayList<String>(); synchronized void write(String text) {
logs.add(text);
} public void print() {
for (Iterator<String> it = logs.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
}

执行后控制台信息如下:

Hibernate: insert into Cat (cat_name, version, id) values (?, ?, ?)
Hibernate: select cat0_.id as id1_0_0_, cat0_.create_time as create_t2_0_0_, cat0_.update_time as update_t3_0_0_, cat0_.cat_name as cat_name4_0_0_, cat0_.version as version5_0_0_ from Cat cat0_ where cat0_.id=?
Hibernate: select cat0_.id as id1_0_0_, cat0_.create_time as create_t2_0_0_, cat0_.update_time as update_t3_0_0_, cat0_.cat_name as cat_name4_0_0_, cat0_.version as version5_0_0_ from Cat cat0_ where cat0_.id=?
Hibernate: update Cat set cat_name=?, version=? where id=? and version=?
Hibernate: update Cat set cat_name=?, version=? where id=? and version=?
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [a3_Version.Cat#8a6cc5a34c6ea5f2014c6ea5f3740000]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2523)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3242)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3140)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3470)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:140)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:393)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:385)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:302)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:339)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1240)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:404)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at a3_Version.Test_optiLock.update(Test_optiLock.java:87)
at a3_Version.Test_optiLock.run(Test_optiLock.java:29)
cat_name已被其他事务修改,本事务被撤销,请重新开始update事务
modify():开始事务
update():开始事务
modify():查询到cat_name为:test3@optiLock
update():查询到cat_name为:test3@optiLock
modify():把cat_name改为:test3@optiLockmodify
update():把cat_name改为:test3@optiLockupdate
modify():提交事务
update():cat_name已被其他事务修改,本事务被撤销

  数据库层面,存入数据时,version是0,update后是1。

环境:JDK1.6,MAVEN

源码地址:http://files.cnblogs.com/files/xiluhua/hibernate%40Version.rar

上一篇:php.ini 配置介绍


下一篇:RabbitMQ 3.6 安装