惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
CXSECURITY Database RSS Feed - CXSecurity.com
Stack Overflow Blog
Stack Overflow Blog
月光博客
月光博客
T
Threat Research - Cisco Blogs
小众软件
小众软件
有赞技术团队
有赞技术团队
酷 壳 – CoolShell
酷 壳 – CoolShell
Apple Machine Learning Research
Apple Machine Learning Research
C
Cyber Attacks, Cyber Crime and Cyber Security
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Tailwind CSS Blog
Cisco Talos Blog
Cisco Talos Blog
V
V2EX
博客园 - 【当耐特】
C
Cybersecurity and Infrastructure Security Agency CISA
Hugging Face - Blog
Hugging Face - Blog
The Cloudflare Blog
The Last Watchdog
The Last Watchdog
Simon Willison's Weblog
Simon Willison's Weblog
T
Threatpost
S
Secure Thoughts
O
OpenAI News
P
Proofpoint News Feed
S
SegmentFault 最新的问题
Forbes - Security
Forbes - Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Application and Cybersecurity Blog
Application and Cybersecurity Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Last Week in AI
Last Week in AI
宝玉的分享
宝玉的分享
Scott Helme
Scott Helme
T
Tenable Blog
A
Arctic Wolf
L
LINUX DO - 热门话题
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
www.infosecurity-magazine.com
www.infosecurity-magazine.com
V
Visual Studio Blog
Hacker News: Ask HN
Hacker News: Ask HN
Hacker News - Newest:
Hacker News - Newest: "LLM"
腾讯CDC
博客园 - Franky
WordPress大学
WordPress大学
Know Your Adversary
Know Your Adversary
博客园_首页
雷峰网
雷峰网
IT之家
IT之家
PCI Perspectives
PCI Perspectives
L
LINUX DO - 最新话题
H
Heimdal Security Blog

博客园 - 三颗纽扣

windows下安装redmine-2.1 try-cache-finally 在线程被杀掉时还有作用吗? 使用InfoBright实现20-100亿条原始话单记录的检索 并非文学化编程,在VS环境下的折中方案 快速复制NuGet引用 语义版本规范 基础框架的基础组件 在 IIS 6 上架设 NuGet Server DotNet开发利器之MyEclipseShortcuts 通过 POI 获取图片在 Excel 表格中的位置 我们是原始生物 没有别的,只有天使 灯是用来照亮的,而不是引路的 戒了,过去完成时 我送给你们的,没有别的——只有天使。 Builder 链——另类一点的Builder模式 多线程JUnit单元测试:GroboUtils and ConTest 控制内存的使用之二:对象缓存 pool and cache 控制内存的使用
Hibernate乐观锁真的会抛出异常吗?
三颗纽扣 · 2012-07-29 · via 博客园 - 三颗纽扣

关于Hibernate 悲观锁和乐观的论述,G一下大把,来源大多一致,示例代码好像差不多,据说乐观锁不使用数据库锁机制,并且都贴出一段乐观锁代码,启动两个事务修改同一条记录,据说将抛出异常,然而我突然就觉得很不可思议,如果不使用数据库锁机制,也没有内部锁机制,两个事务如果先后提交的话,数据库貌似不一定会提交失败吧。

下面是抄来的代码,据说会抛出异常:

public void update(){ 
    //开启事务tx1 
    Session session1 = HibernateUtil.getSession();           
    Transaction tx1 = session1.beginTransaction(); 
    Users users1 = (Users) session1.get(Users.class, 1);           //获取id为1的用户 
     
    //开启事务tx2 
    Session session2 = HibernateUtil.getSession(); 
    Transaction tx2 = session2.beginTransaction(); 
    Users users2 = (Users) session2.get(Users.class, 1);         //获取id为1的用户 
     
    users1.getName().setFirstName("first name1");   
    users2.getName().setFirstName("first name2"); 
     
    tx1.commit();             //..........1 
    tx2.commit();             //..........2 
 
    session1.close(); 
    session2.clear(); 
     
} 

        执行以上代码,代码将在.....2处抛出StaleObjectStateException异 常,并指出版本检查失败。

 我也自己写了一个:

    @Test
    public void test03 () throws Exception {
        init();
        ChrTask task = new ChrTask();
        task.inputFile = "test";
        task.x1Output = "x1";
        task.doOutput = "do";
        task.dfOutput = "df";
        task.id = "id";
        HibernateSessionFactory.getSession().insert(ChrTaskManager.ACTIVE_QUEUE, task);
        HibernateSessionFactory.closeSession();
        
        StatelessSession s1 = HibernateSessionFactory.getStatelessSession();
        
        Transaction t1 = s1.beginTransaction();
        ChrTask tt1 = (ChrTask) s1.get(ChrTaskManager.ACTIVE_QUEUE, task.id);
        tt1.state = 1;
        s1.update(ChrTaskManager.ACTIVE_QUEUE, tt1);
        System.in.read();

        StatelessSession s2 = HibernateSessionFactory.getStatelessSession();
        Transaction t2 = s2.beginTransaction();
        ChrTask tt2 = (ChrTask) s2.get(ChrTaskManager.ACTIVE_QUEUE, "id"); 
        tt2.state = 2;
        s2.update(ChrTaskManager.ACTIVE_QUEUE, tt2);
        System.in.read();
        
        t1.commit();
        s1.close();
        t2.commit(); 
        s2.close();
    }

然而很不幸的是,并没有在 t2.commit() 处抛出异常,而是执行到 ChrTask tt2 = (ChrTask) s2.get(ChrTaskManager.ACTIVE_QUEUE, "id");  这里的时候,日志打印出相应的 sql 语句后,就block住了,只能终止程序。显然,在前一个事务提交前,后面的 select 都不允许了,这一定是使用了某种排他锁机制,说不定是数据库的表级锁定。不过保险起见,我又做了个简单的测试,写了两个测试用例,分到两个进程去跑。

    @Test
    public void test01 () throws Exception {
        init();
        ChrTask task = new ChrTask();
        task.inputFile = "test";
        task.x1Output = "x1";
        task.doOutput = "do";
        task.dfOutput = "df";
        task.id = "id";
        HibernateSessionFactory.getSession().insert(ChrTaskManager.ACTIVE_QUEUE, task);
        HibernateSessionFactory.closeSession();
        
        StatelessSession s1 = HibernateSessionFactory.getStatelessSession();
        
        Transaction t1 = s1.beginTransaction();
        ChrTask tt1 = (ChrTask) s1.get(ChrTaskManager.ACTIVE_QUEUE, task.id);
        tt1.state = 1;
        s1.update(ChrTaskManager.ACTIVE_QUEUE, tt1);
        System.in.read();
        t1.commit();
        
        s1.close();
    }
    
    @Test
    public void test02 () throws Exception {
        HibernateSessionFactory.getSession();
        StatelessSession s2 = HibernateSessionFactory.getStatelessSession();
        Transaction t2 = s2.beginTransaction();
        ChrTask tt2 = (ChrTask) s2.get(ChrTaskManager.ACTIVE_QUEUE, "id");
        tt2.state = 2;
        s2.update(ChrTaskManager.ACTIVE_QUEUE, tt2);
        System.in.read();
        t2.commit();
        s2.close();
    }
    

这个和前面的没什么不同,不过是把两个事务分到两个进程中去,用命令行输入来控制两个进程的同步,保证在一个事务提交前,另一个事务已经启动,然而结果很意外,在eclipse 下直接分别启动两个测试用例,由于 test01 先启动,它暂停在 System.in.read() 处,test02则同样block在 ChrTask tt2 = (ChrTask) s2.get(ChrTaskManager.ACTIVE_QUEUE, "id"); 处,打印出select 语句后就卡住了,在test01按下回车后test02才继续,但是,但是,两个进程都顺利执行完毕了!!!没有抛出任何异常,这是显然的,由于第二个事务的 select 是在第一个事务结束后才开始的,两个事务被串行化了。事实上,数据库中存在的记录也表明,state=2, version=2

如果在第一个事务update之前开始第二个事务的 select 呢?将 System.in.read() 移动到 s1.update 之前,test02 不变,还是先启动 test01,结果两个都挺在了 read() 上了,在 01 中按下回车后,它继续到 update 又被 block 住了,在 02 按下回车后,02 顺利结束,而 01 则抛出了异常,检测到事务冲突。但异常是在 update 处抛出的,并非在 commit 的时候。

这些测试中,之所以总是会在 select 或者 update 的时候被 block 住,应该还是数据库事务串行化起到了左右,当然,即使在没有事务的数据库平台或未启动事务的情况下执行并发修改,由于 update 操作在数据库层面是原子操作,所以后面的 update 一定会检测到数据变更从而引发异常。