概述

hibernate 可以通过加锁解决并发问题。

hibernate 的锁分为两种:乐观锁和悲观锁。

乐观锁(Optimistic lock):每次访问数据时,都会乐观的认为其它事务此时肯定不会同时修改该数据。但在真正修改时,会在代码中先判断数据是否已经被其它事务修改过。所以锁 是加在代码中的。

悲观锁(Pessimistic lock):每次访问数据时,都会悲观的认为其它事务一定会同时修改该数据。所以其在访问数据时,在数据库中就会先给数据加锁,以防止其它事务同时修改该数据。所以锁是加在数据库中的。

乐观锁

乐观锁是加在代码中的锁机制,一般充当乐观锁的有两类数据:版本号与时间戳。它们的工作原理是相同的。

举例说明,A、B两个事务从数据库中读取数据时都会读出一个数据版本号。当 A 事务将修改过的数据写入到数据库中时,会使版本号增 1。当 B 事务发生回滚或覆盖时,会首先对比自己数据的版本号与数据库中数据的版本号。若它们相等,则说明数据库中数据没有发生变化,B 事务可以将数据回滚到原始状态,或将修改写入到 DB 中。若小于 DB 中的版本号,则说明其它事务已经修改过该数据,将抛出异常。

悲观锁

悲观锁是加在 DB 中的锁机制,又分为两种:

写锁,又称为排他锁。当 A 事务对某数据加上排他锁后,A 事务将独占该数据,可对该数据进行读、写操作。但其他事务是不能再为该数据添加任何锁的,直到 A 事务将排他锁解锁,将数据释放。 在 SQL 语句中,若要为本次操作(事务)添加排他锁,则可在正常的 SQL 语句最后添 加上 for update 即可。例如: select * from student where age <20 for update

读锁,又称为共享锁。当 A 事务对某数据加上共享锁后,只能对数据进行读操作。但其他事务也同时可以为该数据添加共享锁,读取该数据。但不能添加写锁,直到所有事务将其共享锁解锁,将数据释放,才可再对数据添加排他锁。 在 SQL 语句中,若要为本次操作(事务)添加共享锁,则可在正常的 SQL 语句最后添 加上 lock in share mode 即可。例如:select * from student where age <20 lock in share mode

乐观锁实现

在实体类中添加属性 version,并加注解 @Version

@Data
@Entity
@Table(name = "t_book", schema = "test", catalog = "")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id; @Version
@Column(name="version")
private int version; @Basic
@Column(name = "name")
private String name; @Basic
@Column(name = "author")
private String author; @Basic
@Column(name = "publish_date")
private Date publishDate; @Basic
@Column(name = "price")
private BigDecimal price; @Transient
private Integer catalogId;
}

测试类

@RunWith(SpringRunner.class)
@SpringBootTest
public class OptimisticLockTest {
@Autowired
private SessionFactory sessionFactory; private Session session; @Before
public void setup() {
this.session = sessionFactory.getCurrentSession();
} @Test
@Transactional
public void test01() {
Criteria criteria = this.session.createCriteria(Book.class);
Criterion eqNameCr = Restrictions.eq("name", "三体");
criteria.add(eqNameCr);
Book book = (Book) criteria.uniqueResult(); book.setPrice(new BigDecimal("150")); //在该行打断点,执行到这个地方时,手动修改数据库中“三体”的version字段值,然后继续执行,会抛出
// javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.guide.hibernate.domain.Book#1]
this.session.update(book);
session.flush();
System.out.println(book.getName() + "价格:" + book.getPrice());
}
}

this.session.update(book) 的底层 SQL 如下:

update t_book set author=?, catalog_id=?, name=?, price=?, publish_date=?, version=? where id=? and version=?

悲观锁实现

实体类中不用增加任何属性。只需要在通过 get() 加载对象时,为其加锁。使用 的 get()方法原型为:

T get(Class clazz, Serializable num, LockMode lockMode)

排他锁:LockMode.PESSIMISTIC_WRITE

共享锁:LockMode.PESSIMISTIC_READ

排他锁

对于排他锁,其加锁的时间为通过 get()方法执行 select 查询后,解锁时间为当前事务结束

这期间,当前事务是可以修改被其加锁的数据的,但其它事务是无法对该数据进行修改的。

    Criteria criteria = session.createCriteria(Book.class);
criteria.setLockMode(LockMode.PESSIMISTIC_WRITE);
Criterion eqNameCr = Restrictions.eq("name", "三体");
criteria.add(eqNameCr);
Book book = (Book) criteria.uniqueResult();
//在此处打断点,然后尝试是否可以在数据库中修改 “三体” 这条数据,发现一直卡住了
System.out.println(book.getName() + "价格:" + book.getPrice());

它的底层SQL如下:

select this_.id as id1_0_0_, this_.author as author2_0_0_, this_.catalog_id as catalog_7_0_0_, this_.name as name3_0_0_, this_.price as price4_0_0_, this_.publish_date as publish_5_0_0_, this_.version as version6_0_0_
from t_book this_
where this_.name=? for update

共享锁

对于共享锁,其加锁的时间也为通过 get()方法执行 select 查询后,但其解锁时间则是在其查询的数据被检索出来后的瞬间

即从程序运行来看,get()方法开始执行则加上了读锁, get()方法运行结束,则读锁解除。所以,加了读锁的 get()方法后的修改语句,与读锁是没有任何关系的。

    //读锁,共享锁
Criteria criteria = session.createCriteria(Book.class);
criteria.setLockMode(LockMode.PESSIMISTIC_READ);
Criterion eqNameCr = Restrictions.eq("name", "三体");
criteria.add(eqNameCr);
Book book = (Book) criteria.uniqueResult();
System.out.println(book.getName() + "价格:" + book.getPrice());

它的底层SQL如下:

select this_.id as id1_0_0_, this_.author as author2_0_0_, this_.catalog_id as catalog_7_0_0_, this_.name as name3_0_0_, this_.price as price4_0_0_, this_.publish_date as publish_5_0_0_, this_.version as version6_0_0_
from t_book this_
where this_.name=? for share

hibernate的锁机制的更多相关文章

  1. Hibernate学习---第十二节:Hibernate之锁机制&乐观锁实现

    1.悲观锁 它指的是对数据被外界修改保持保守态度,因些,在整个数据处理过程中,将数据牌锁定状态.悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层的锁机制才能保证数据访问的排他性,否则,即使在本 ...

  2. Hibernate中的锁机制

    锁机制:是数据库为了保证数据的一致性<一个事务的各种操作不相互影响>而使各种共享资源在被并发访问访问变得有序所设计的一种规则,用来保证在当前用户进行操作数据的时候其他的用户不能对同一数据进 ...

  3. 【Hibernate步步为营】--锁机制具体解释

    上篇文章具体讨论了hql的各种查询方法.在讨论过程中写了代码演示样例.hql的查询方法类似于sql,查询的方法比較简单,有sql基础的开发者在使用hql时就会变得相当的简单. Hibernate在操作 ...

  4. Hibernate锁机制

    业务逻辑的实现过程中,往往需要保证数据访问的排他性.因此,我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓的“锁”,即给我们选定的目标数据上锁,使其无 ...

  5. 数据库并发及锁机制及Hibernate锁实现

    数据库事务的定义 数据库事务(Database Transaction),是指作为单个逻辑工作单元执行的一系列操作.一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性.一致性.隔离性和持久性) ...

  6. Hibernate悲观锁/乐观锁

    如果需要保证数据访问的排它性,则需对目标数据加"锁",使其无法被其它程序修改 一,悲观锁 对数据被外界(包括本系统当前的其它事务和来自外部系统的事务处理)修改持保守态度,通过数据库 ...

  7. Hibernate乐观锁和悲观锁

    Hibernate支持两种锁机制: 即通常所说的"悲观锁(Pessimistic Locking)"和 "乐观锁(OptimisticLocking)". 悲观 ...

  8. Hibernate乐观锁、悲观锁和多态

     乐观锁和悲观锁 悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁 ...

  9. [数据库锁机制] 深入理解乐观锁、悲观锁以及CAS乐观锁的实现机制原理分析

    前言: 在并发访问情况下,可能会出现脏读.不可重复读和幻读等读现象,为了应对这些问题,主流数据库都提供了锁机制,并引入了事务隔离级别的概念.数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务 ...

  10. MySQL数据库的锁机制

    在并发访问情况下,很有可能出现不可重复读等等读现象.为了更好的应对高并发,封锁.时间戳.乐观并发控制(乐观锁).悲观并发控制(悲观锁)都是并发控制采用的主要技术方式. 锁分类 ①.按操作划分:DML锁 ...

随机推荐

  1. Linux之密码生成工具pwgen

    linux中生成随机字符串,可以使用pwgen 安装) ubuntu: apt-get install pwgen Centos: yum install pwgen 语法及参数) pwgen [ O ...

  2. Graylog之基本使用

    文档:https://docs.graylog.org/en/3.0/ Graylog Sidecar是一个轻量级配置管理系统,适用于不同的日志收集器,也称为后端.Graylog节点充当包含日志收集器 ...

  3. 【docker-compose】Redis安装教程

    仅供学习参考 ,请勿轻易在生产环境使用 0. 文件目录 下面的文件按照这个目录层级放置,首先创建 /docker/redis 文件夹,/docker/redis 对应下边图中 REDIS. 1. 创建 ...

  4. 调用非托管dll常出现的bug及解决办法

    转自http://www.51testing.com/html/00/n-832200.html C和C++有很多好的类库的沉淀,在.NET中,完全抛弃它们而重头再来是非常不明智的.也是不现实的,所以 ...

  5. js修改表单值不会触发input事件

    前情 最近在做需求开发时候,需要监听表单的input事件 坑位 当通过JS动态修改表单的值的发现并不会触发表单的input事件 Why? 个人猜测是浏览器默认行为,input只会针对用户手动输入做响应 ...

  6. Spring AOP实例操作 简单易懂

    AOP的功能,不改变源代码可以增强类中的方法      (增强 = 代理) AOP切入点表达式: execution([权限修饰符] [返回值类型] [类全路径] [方法名称] ([参数列表])) 例 ...

  7. 【实训总结报告】大二下学期期末Java EE实训报告

    站点在微信公众号(萌狼蓝天)回复便可以访问

  8. Qt 6 中的 Qt Extras 模块

    一.前言 Qt 6 是有意识地努力使框架更加高效和易于使用的结果. 我们尝试在每个版本中保持所有公共 API 的二进制和源代码兼容性. 但为了使 Qt 成为更好的框架,一些更改是不可避免的. 其中一项 ...

  9. 【OpenCV】features2d_converters.cpp:2:10: fatal error: common.h: 没有那个文件或目录

    Linux环境下使用opencv的dnn模块调用yolov4遇到的坑(纯CPU)一.问题描述Ubuntu安装opencv4.4,第一次编译完成安装成功,发现编译时少加了几个选项,于是重新编译,结果报如 ...

  10. 在CLion中如何为CMakeLists.txt文件添加第三方依赖库

    cmake_minimum_required(VERSION 3.5)project(ImageBasedModellingEdu)set(CMAKE_MODULE_PATH "${CMAK ...