乐观锁:乐观的认为我在操作的时候,一定是没有和我一起操作同一个数据的。
原理:在数据库表里面添加一个字段Version。每次修改一次数据的时候,把表里的Version+1,当提交的时候就判断当前的version大不大于数据库里面的version,大于就可以提交,不大于就不能提交并报出异常StaleObjectStateException。
实体Bean:
private Long id;
private String userName;
private Integer age;
private Long version;
public UserBean() {
super();
// TODO Auto-generated constructor stub
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "UserBean [id=" + id + ", userName=" + userName + ", age=" + age + ", version=" + version + "]";
}
xml配置:
<hibernate-mapping>
<class name="org.framestudy.hibernate0810.beans.UserBean" table="t_user" catalog="sm">
<id name="id" column="id" type="java.lang.Long">
<generator class="native" /> <!--根据数据库的主键自动增长-->
<!--identity 自定义,采用数据库的自增长策略,hibernate框架与应用程序不负责ID生产,数据库主键必须是自增长-->
<!--increment 自增长,数据库不能拥有自增长策略,而是由hibernate框架完成ID的自增长生产。极有可能出错 -->
<!--uuid.hex 数据库中id列必须为字符类型,长度不能小于32位,这种生产方式采用计算机IP,MAC,时间戳等
一系列不重复的内容共同组成,然后采用某些特殊算法得到的32位不重复的字符串-->
<!--assigned 数据库与hibernate都不负责ID的生产,ID需要用过应用程序调用SetID()来完成ID的生产-->
<!--foreign 外来的,本表的ID来自于其他表的ID-->
<!--native 框架不提供生产ID,跟着数据库的自增长实现 -->
</id>
<version name="version" column="version" type="java.lang.Long"></version>//version一定是加在id的后面
<property name="userName" column="user_name" type="java.lang.String"/>
<property name="age" column="age" type="java.lang.Integer"></property>
</class>
</hibernate-mapping>
测试:
Session session =SessionUtils.getSession();
Session session2 =SessionUtils.getSession();
Transaction tx1 = session.beginTransaction();
Transaction tx2 = session2.beginTransaction();
UserBean ub = (UserBean) session.get(UserBean.class, 9l);
UserBean ub2 = (UserBean) session.get(UserBean.class, 9l);
ub2.setUserName("小王");
session2.saveOrUpdate(ub2);
tx2.commit();
ub.setUserName("小李");
session.saveOrUpdate(ub);
tx1.commit();
测试结果:
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [org.framestudy.hibernate0810.beans.UserBean#9]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2541)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3285)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3183)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3525)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:159)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1258)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
at org.framestudy.hibernate0810.TestUserServiceImpl.findUserByidupdate(TestUserServiceImpl.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:85)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:86)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:241)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
两次同时访问如果报StaleObjectStateException异常,就证明你的乐观锁成功了。