刚刚接触SSH框架,虽然可能这个框架已经比较过时了,但是个人认为,SSH作为一个成熟的框架,作为框架的入门还是可以的。

马马虎虎学完了Hibernate的基础,总结一点心得之类的。

学习Hibernate的乐观锁时:

  • 首先要知道为什么要用乐观锁。之所以要用乐观锁,就是为了避免脏数据。这很像数据库原理中的共享锁(读锁)和排它锁(写锁)。不管是乐观锁、共享锁、排它锁,其目的都是为了保证数据的一致性,也可以说是保证数据的正确性。所有锁的本质都是一样的。
  • 其次要知道什么时候要用乐观锁。一般有多个事务要对同一数据进行操作时,就需要使用乐观锁。比如很经典的银行取钱问题,只有用锁来保证数据只能被一个事务所使用,在该事务结束使用之前,别的事务都不能对它做任何事,否则就可能出现丢失修改、读脏数据、数据不一致等问题。
  • 第三要知道为什么可以使用乐观锁。Hibernate本身是对JDBC的轻量级封装,其目的是为了使开发人员可以像操作对象一样操作数据库,其本质就是数据库操作,所以数据库的锁机制是可以实现和使用的。
  • 第四要知道乐观锁的实现机制。version元素是利用一个递增的整数来跟踪数据表中记录的版本的。在读取数据时,会将version一同读取出来,而在更新时,将version+1(使用hql在update时不校验version)。将提交数据的version与数据库库表中对应记录的version进行比较,如果提交的数据的version大于数据库库表中记录的version,则执行更新,否则便认为是过期或无效数据,不执行更新,并抛出异常。
  • 最后要知道怎么使用乐观锁。
  • 先要有一个entity class。在该类里面增加一个version属性,设为int类型,这个字段表示版本信息。

【注:代码中有@的可以不用管,这个是注解方式。即直接在类上使用注解,来达到相同的配置效果。】

 package hibernate;

 import java.util.Set;

 import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table; @Entity
@Table(name = "product_")
public class Product {
int id;
String name;
float price;
Category category;
Set<User> users;
int version; @Column(name = "version")
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
} @Column(name = "users")
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
} @Column(name = "category")
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
} @Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
} @Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
} @Column(name = "price")
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
  • 接下来就是进行修改entity class的配置文件。在"类名.hbm.xml"文件中,增加一个version字段,用于版本信息控制,这就是乐观锁的核心机制。

【注:version标签必须跟在id标签后面,否则会有错,运行程序会报错,无法读取XML文件。 hibernate需要访问的属性一定要在"类名.hbm.xml"中定义】

<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="hibernate"> <!-- *****表示类Product对应表product_***** --> <class name="Product" table="product_"> <!-- *****表示属性id,映射表里的字段id***** --> <id name="id" column="id"> <!-- *****id的自增长方式采用数据库的本地方式***** --> <generator class="native"> </generator> </id> <!--version标签必须跟在id标签后面 --> <version name="version" column="ver" type="int"></version> <!-- *****只写了属性name,没有通过column="name" 显式的指定字段,那么字段的名字也是name.***** --> <property name="name" /> <property name="price" /> <!-- 使用标签many-to-one设置多对一关系 --> <!-- name="category" 对应Product类中的category属性 --> <!-- class="Category" 表示对应Category类 --> <!-- column="cid" 表示指向category_表的外键 --> <many-to-one name="category" class="Category" column="cid"/> <set name="users" table="user_product" lazy="false"> <key column="pid"/> <many-to-many column="uid" class="User"/> </set> </class> </hibernate-mapping>

  • 还有在"包名.cfg.xml"文件中要配置好映射。
<mapping resource="hibernate/Product.hbm.xml" />
  • 最后测试一下就好了。
 public class TestHibernate {

     public static void main(String[] args) {
SessionFactory sf = new Configuration().configure().buildSessionFactory(); Session s1 = sf.openSession();
Session s2 = sf.openSession(); s1.beginTransaction();
s2.beginTransaction(); Product p1 = (Product) s1.get(Product.class, 1);
System.out.println("产品原本价格是: " + p1.getPrice());
p1.setPrice(p1.getPrice() + 1000); Product p2 = (Product) s2.get(Product.class, 1);
p2.setPrice(p2.getPrice() + 1000); s1.update(p1);
s2.update(p2); s1.getTransaction().commit();
s2.getTransaction().commit(); Product p = (Product) s1.get(Product.class, 1);
System.out.println("经过两次价格增加后,价格变为: " + p.getPrice()); sf.close();
} }
  • 运行结果:在main线程中出现了报错,因为s1已经修改了数据,但是s2也想修改,但是version的值已经由1变为2了,所以s2不能执行,报错Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect),意思是"行被另一事务更新或删除(或未保存的值映射不正确)",即s1已经修改了该行,产品原本价格是: 10000.0,程序执行完之后为11000.0,s2没有执行。
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
八月 05, 2018 10:49:36 上午 com.mchange.v2.log.MLog <clinit>
信息: MLog clients using java 1.4+ standard logging.
八月 05, 2018 10:49:38 上午 com.mchange.v2.c3p0.C3P0Registry banner
信息: Initializing c3p0-0.9.1 [built 16-January-2007 14:46:42; debug? true; trace: 10]
八月 05, 2018 10:49:38 上午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.PoolBackedDataSource@7cf67e4e [ connectionPoolDataSource -> com.mchange.v2.c3p0.WrapperConnectionPoolDataSource@3c314b76 [ acquireIncrement -> 2, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, debugUnreturnedConnectionStackTraces -> false, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 2x00zq9x27t6vc4ficmh|4d1b0d2a, idleConnectionTestPeriod -> 3000, initialPoolSize -> 5, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 50000, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 100, maxStatementsPerConnection -> 0, minPoolSize -> 5, nestedDataSource -> com.mchange.v2.c3p0.DriverManagerDataSource@ce96782d [ description -> null, driverClass -> null, factoryClassLocation -> null, identityToken -> 2x00zq9x27t6vc4ficmh|52feb982, jdbcUrl -> jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8, properties -> {user=******, password=******} ], preferredTestQuery -> null, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false; userOverrides: {} ], dataSourceName -> null, factoryClassLocation -> null, identityToken -> 2x00zq9x27t6vc4ficmh|4fcd19b3, numHelperThreads -> 3 ]
Hibernate: select product0_.id as id0_0_, product0_.ver as ver0_0_, product0_.name as name0_0_, product0_.price as price0_0_, product0_.cid as cid0_0_ from product_ product0_ where product0_.id=?
Hibernate: select users0_.pid as pid0_1_, users0_.uid as uid1_, user1_.id as id3_0_, user1_.name as name3_0_ from user_product users0_ inner join user_ user1_ on users0_.uid=user1_.id where users0_.pid=?
Hibernate: select products0_.uid as uid3_1_, products0_.pid as pid1_, product1_.id as id0_0_, product1_.ver as ver0_0_, product1_.name as name0_0_, product1_.price as price0_0_, product1_.cid as cid0_0_ from user_product products0_ inner join product_ product1_ on products0_.pid=product1_.id where products0_.uid=?
Hibernate: select products0_.uid as uid3_1_, products0_.pid as pid1_, product1_.id as id0_0_, product1_.ver as ver0_0_, product1_.name as name0_0_, product1_.price as price0_0_, product1_.cid as cid0_0_ from user_product products0_ inner join product_ product1_ on products0_.pid=product1_.id where products0_.uid=?
Hibernate: select products0_.uid as uid3_1_, products0_.pid as pid1_, product1_.id as id0_0_, product1_.ver as ver0_0_, product1_.name as name0_0_, product1_.price as price0_0_, product1_.cid as cid0_0_ from user_product products0_ inner join product_ product1_ on products0_.pid=product1_.id where products0_.uid=?
产品原本价格是: 10000.0
Hibernate: select product0_.id as id0_0_, product0_.ver as ver0_0_, product0_.name as name0_0_, product0_.price as price0_0_, product0_.cid as cid0_0_ from product_ product0_ where product0_.id=?
Hibernate: select users0_.pid as pid0_1_, users0_.uid as uid1_, user1_.id as id3_0_, user1_.name as name3_0_ from user_product users0_ inner join user_ user1_ on users0_.uid=user1_.id where users0_.pid=?
Hibernate: select products0_.uid as uid3_1_, products0_.pid as pid1_, product1_.id as id0_0_, product1_.ver as ver0_0_, product1_.name as name0_0_, product1_.price as price0_0_, product1_.cid as cid0_0_ from user_product products0_ inner join product_ product1_ on products0_.pid=product1_.id where products0_.uid=?
Hibernate: select products0_.uid as uid3_1_, products0_.pid as pid1_, product1_.id as id0_0_, product1_.ver as ver0_0_, product1_.name as name0_0_, product1_.price as price0_0_, product1_.cid as cid0_0_ from user_product products0_ inner join product_ product1_ on products0_.pid=product1_.id where products0_.uid=?
Hibernate: select products0_.uid as uid3_1_, products0_.pid as pid1_, product1_.id as id0_0_, product1_.ver as ver0_0_, product1_.name as name0_0_, product1_.price as price0_0_, product1_.cid as cid0_0_ from user_product products0_ inner join product_ product1_ on products0_.pid=product1_.id where products0_.uid=?
Hibernate: update product_ set ver=?, name=?, price=?, cid=? where id=? and ver=?
Hibernate: update product_ set ver=?, name=?, price=?, cid=? where id=? and ver=?
Exception in thread "main" org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [hibernate.Product#1]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1950)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2594)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2494)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2821)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:113)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:185)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
at hibernate.TestHibernate.main(TestHibernate.java:404)
  • PS:除了乐观锁还有悲观锁,弄懂了乐观锁之后可以研究一下悲观锁。

关于Hibernate基于version的乐观锁的更多相关文章

  1. ElasticSearch(九)基于version进行乐观锁并发控制

    一.基于version进行乐观锁并发控制 1).查看一条document GET /test_version/test_version_type/ { "_index" : &qu ...

  2. 并发-AtomicInteger源码分析—基于CAS的乐观锁实现

    AtomicInteger源码分析—基于CAS的乐观锁实现 参考: http://www.importnew.com/22078.html https://www.cnblogs.com/mantu/ ...

  3. 21.实验基于_version进行乐观锁并发控制

    21.实验基于_version进行乐观锁并发控制 主要知识点: 实验基于_version进行乐观锁并发控制 1.实验实战演练基于_version进行乐观锁并发控制 (1)先构造一条数据出来 PUT / ...

  4. AtomicInteger源码分析——基于CAS的乐观锁实现

    AtomicInteger源码分析——基于CAS的乐观锁实现 1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时 ...

  5. 基于redis的乐观锁实践

    redis真是一个分布式应用场景下的好东西,对于我们的应用设计,功劳大大的! 今天要研究的是基于redis的事务机制以及watch指令(CAS)实现乐观锁的过程. 所谓乐观锁,就是利用版本号比较机制, ...

  6. AtomicInteger源码分析——基于CAS的乐观锁实

    1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换.切换涉及 ...

  7. 基于Django的乐观锁与悲观锁解决订单并发问题的一点浅见

    订单并发这个问题我想大家都是有一定认识的,这里我说一下我的一些浅见,我会尽可能的让大家了解如何解决这类问题. 在解释如何解决订单并发问题之前,需要先了解一下什么是数据库的事务.(我用的是mysql数据 ...

  8. Elasticsearch 基于external的乐观锁的版本控制

    version_type=external,唯一的区别在于,_version,只有当你提供的version与es中的_version一模一样的时候,才可以进行修改,只要不一样,就报错:当version ...

  9. 基于external version进行乐观锁并发控制

    ?version=1?version=1&version_type=external它们的唯一区别在于,_version,只有当你提供的version与es中的_version一模一样的时候, ...

随机推荐

  1. Android 集成百度统计

    在这里简单的介绍下怎么统计自己研发的APP 的用户活跃度,和使用量,以此来展示自己APP的用户使用量! 我们的APP都需要注入数据分析,以供我们实时的了解APP的下载和使用量提供了依据! 不过我还是更 ...

  2. c++ inline 的位置不当导致的 无法解析的外部符号

    这几天编写代码碰到 无法解析的外部符号 visual studio. 在类中 inline 修饰符应该放在类函数定义的时候而不是声明的地方 即 // test.h 头文件 class A { publ ...

  3. 微信小程序——代码构成

    通过上一章讲解,我们了解到如何初始化一个小程序项目,这里是官方给到demo地址,通过demo来看教程更方便于我们理解项目架构. 由四种文件构成: .json 后缀的 JSON 配置文件 .wxml 后 ...

  4. IIS 无法安装URL重写模块的解决办法 UrlReWrite (.NET`SQL技术交流 群号206656202)

    下载和安装URL Rewrite IIS8默认是没有安装URL重写工具的,必须要自己下载安装. 如果IIS上默认有安装Web平台安装程序,我们可以使用平台自动安装URL Rewrite重写工具,打开I ...

  5. Linux->ZooKeeper集群搭建

    人,总免不了有心结,限制着自己,难以前行.对于ZooKeeper的理解,以及实践也拖了很久,今天用零散时间学习一下,补点干货. 一.简述 Zookeeper是Google的Chubby一个开源的实现, ...

  6. QueryDSL

    QueryDSL只是通用的查询框架,不支持写操作 查询出来的结果是List,我们不得不做额外的工作将它转化为领域实体

  7. yii2.0的gradview点击按钮通过get方式传参

    1.直接看views层里的代码就可以了 , <!--?= GridView::widget([ 'dataProvider' =--> $dataProvider, 'filterMode ...

  8. Sql去重一些技巧

    下午的时候遇到点问题,Sql去重,简单的去重可以用 DISTINCT 关键字去重,不过,很多情况下用这个解决不了问题.重复的数据千变万化,例如:类似于qq.微信的最近联系人功能,读取这些数据肯定要和消 ...

  9. AngularJs学习笔记--Understanding Angular Templates

    原版地址:http://docs.angularjs.org/guide/dev_guide.mvc.understanding_model angular template是一个声明规范,与mode ...

  10. 你真的了解现在的PHP吗?

    前段时间,公司的项目从PHP5.3升级到PHP7,现在项目里开始使用PHP7的一些新语法和特性.反观PHP的5.4.5.5.5.6版本,有点认知缺失的感觉.所以,决定看<Modern PHP&g ...