ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

hibernate的锁机制

2022-06-01 14:04:25  阅读:187  来源: 互联网

标签:事务 hibernate name Book _. book 机制 数据


概述

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,name,Book,_.,book,机制,数据
来源: https://www.cnblogs.com/haicheng92/p/15970481.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有