框架学习之Hibernate 第十节 事务原理与分析
1.事务
两种事务:
① JDBC事务:单个数据库的事务
一个SesisonFactory对应一个数据库,使用 JDBC 实现
常用代码的模板:
Session session = null;Transaction tx =null;try {session = sessionFactory.openSession();tx = session.beginTransaction();//processtx.commit();} catch(HibernateException e){if(tx != null)tx.rollback();throw e;}finally {if (session != null)session.close();}connection.setAutoCommit(false);connection.commit();conn.rollback();
②JTA 事务:跨数据库的事务
使用JTATransaction需要配置hibernate.transaction.factory_class参数,该参数缺省值是org.hibernate.transaction. JDBCTransactionFactory,
当使用JTATransaction时需要将该参数改成org.hibernate.transaction.JTATransactionFactory,并配置jta.UserTransaction参数JNDI名
[JNDI:Java Naming and Directory Interface,Java命名和目录接口,是一组在Java应用中访问命名和目录服务的API。
命名服务将名称和对象联系起来,使得我们可以用名称访问对象。目录服务是一种命名服务,在这种服务里,对象不但有名称,还有属性。]
(Hibernate在启动JTATransaction时要用该值到JNDI的上下文Context中去找javax.transaction.UserTransaction)。
javax.transaction.UserTransactin tx = context.lookup(“jndiName”);
javax.transaction.UserTransactin tx = context.lookup(“jndiName”);
try{tx.begin();//多个数据库的session操作;//session1….//session2….tx.commit();
}catch(Exception e){tx.rollback(); throw e;
}
2.Open Session in View
优点:实现了数据访问层 和 业务逻辑层 的脱离,更好的实现了三层架构
缺点:延长了Session和事务的时间,容易造成内存不够或者数据锁死的现象
Open session in view:在生成(渲染)页面时保持 session打开。
session context和事务边界[事务边界就是和事务有关的处理,包括事务打开,提交和回滚]
用current_session_context_class属性来定义context(用sessionFactory.getCurrentSession()来获得session),其值为:
1.thread:ThreadLocal来管理Session实现多个操作共享一个Session,避免反复获取Session,并控制事务边界,
此时session不能调用close,当commit或rollback的时候session会自动关闭(connection.release_mode:after_transaction)。
2.jta:由JTA事务管理器来管理事务(connection.release_mode:after_statement)。
测试代码:[ 注:一下代码经常是放在 J2EE 项目中,而不是J2SE,故下面代码在没有引入JavaEE包时会报错的 ]
package cn.itcast.hibernate;import java.io.IOException;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;import org.hibernate.Session;
import org.hibernate.Transaction;public class OpenSessionInView implements Filter {public void destroy() {// TODO Auto-generated method stub}public void doFilter(ServletRequest arg0, ServletResponse arg1,FilterChain arg2) throws IOException, ServletException {Session session = null;Transaction tx = null;try {session = HibernateUtil.getThreadLocalSession();tx = session.beginTransaction();arg2.doFilter(arg0, arg1); // 控制事务边界,避免反复获取session,这里tx.commit();} catch (Exception e) {if (tx != null)tx.rollback();throw new RuntimeException(e.getMessage(), e);} finally {HibernateUtil.closeSession();}}public void init(FilterConfig arg0) throws ServletException {// TODO Auto-generated method stub}}
注意 HibernateUtils中要添加:
private static ThreadLocal session = new ThreadLocal();public static Session getThreadLocalSession() {if (s == null) {s = getSession();session.set(s);}return s;}public static void closeSession() {Session s = (Session) session.get();if (s != null) {s.close();session.set(null);}}
ThreadLocal 就是当前线程,通过它可以得到 当前线程的 session 对象
3.事务中的悲观锁和乐观锁
问题:A和B同时打开了 一个编辑页面,然后A将原来的名称name改为了name1,并提交,保存到数据库中了
接着B也将原来的名称改了,改成了name2,并提交,也保存到数据库中了。经过了两个人的操作,A的操作没有任何作用,他的操作被B覆盖掉了
更有甚者,如果他们修改的是不同的地方,B修改名称,A修改内容,那么A的操作完全是白费的
简单地说就是:两个不同的线程的session同时操作了同一条数据,并进行了修改,而且修改后的结果不同,那么谁后提交谁的操作才是有效的操作
悲观锁由数据库来实现;乐观锁hibernate用version和timestamp来实现
悲观锁:当A打开编辑时,就把这条数据加上锁
缺点:锁住了之后其他人就不能操作了,一定要等到A提交了才可以
乐观锁:给数据加上一个版本号,每当数据有了更新,就增加版本号。 Hibernate实现了乐观锁定功能,只需要在映射文件中配置一下version就好了
优点:有效的防止了上面的情况,如果提交时检测到提交的数据的版本号低于数据库中的数据的版本号,那么提交就会失败
这种情况下,谁先提交那么谁的提交就会成功!后提交的只能重新打开,重新修改
还有另外一种乐观锁的实现方式就是时间戳,version 中 type 是 timestamp,但是存在时间精度的问题,不如使用version(integer)的效果好
使用version,测试代码:
首先在People类中添加 version 属性,并提供get和set方法
然后再people映射文件中添加: 以integer方式为例
<version name="version" type=""></version>
最后,测试类:VersionTest
package com.yinger.main;import org.hibernate.Session;
import org.hibernate.Transaction;import com.yinger.domain.Name;
import com.yinger.domain.People;
import com.yinger.util.HibernateUtils;public class VersionTest {public static void main(String[] args) {People p = addPeople();System.out.println("---------");testTransaction(p.getId());System.out.println("---------");p = getPeople(p.getId());System.out.println(p.getName().getFirstName());}private static void testTransaction(int id) {Session s1 = HibernateUtils.getSession();Transaction tx1 = s1.beginTransaction();//一个事务开启了People p1 = (People)s1.get(People.class, id);Session s2 = HibernateUtils.getSession();;Transaction tx2 = s2.beginTransaction();//又开启了另一个事务,并且操作的是同一条数据People p2 = (People)s2.get(People.class, id);p2.getName().setFirstName("firstName 2");p1.getName().setFirstName("firstName 1");tx2.commit();tx1.commit();s1.close();s2.close();}public static People getPeople(int id) {People p= (People)HibernateUtils.get(People.class, id);return p;}public static People addPeople() {People p = new People();Name n = new Name();n.setFirstName("firstName");n.setLastName("lastName");p.setName(n);HibernateUtils.add(p);return p;}}
测试结果:
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into People (version, first_name, last_name, id) values (?, ?, ?, ?)
---------
Hibernate: update People set version=?, first_name=?, last_name=? where id=? and version=?
Hibernate: update People set version=?, first_name=?, last_name=? where id=? and version=?
Exception in thread "main" org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.yinger.domain.People#1]at org.hibernate.persister.entity.BasicEntityPersister.check(BasicEntityPersister.java:1456)at org.hibernate.persister.entity.BasicEntityPersister.update(BasicEntityPersister.java:1999)at org.hibernate.persister.entity.BasicEntityPersister.updateOrInsert(BasicEntityPersister.java:1923)at org.hibernate.persister.entity.BasicEntityPersister.update(BasicEntityPersister.java:2163)at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:75)at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:239)at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:223)at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:137)at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:274)at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:675)at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:293)at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:86)at com.yinger.main.VersionTest.testTransaction(VersionTest.java:34)at com.yinger.main.VersionTest.main(VersionTest.java:15)
数据库中数据:
id version first_name last_name
1 1 firstName 2 lastName
改变一下两个事务提交的顺序,重新测试
tx1.commit();
tx2.commit();
数据库中的数据:
id version first_name last_name
1 1 firstName 1 lastName
这个就是乐观锁的方式解决上述问题
框架学习之Hibernate 第十节 事务原理与分析相关推荐
- Java:计算机编程语言Java的简介、安装(编程环境/工具)、学习路线(如何学习Java以及几十项代码编程案例分析)之详细攻略
Java:计算机编程语言Java的简介.安装(编程环境/工具).学习路线(如何学习Java以及几十项代码编程案例分析)之详细攻略 目录 Java的简介 1.Java的工作原理--基于Eclipse等编 ...
- 框架学习之Spring 第四节 Spring集成JDBC组件开发
1.与JDBC集成的配置步骤: ①配置数据源,如: 第一种方式:直接在XML中配置数据源:<bean id="dataSource" class="org.apac ...
- 框架学习之Spring 第五节 SSH整合开发[Spring2.5+Hibernate3.3+Struts2]
1.首先整合Spring和Hibernate ①引入jar包: hibernate核心安装包下的: hibernate3.jar lib\required\*.jar lib\optional\ehc ...
- 小傻蛋的妹妹跟随小甲鱼学习Python的第十节010
列表 一.创建列表的方式: 1. 创建一个普通的列表: member=['小甲鱼','小傻蛋','小布丁','妹妹'] number=[1,2,3,4,5] 2. 创建一个混合列表: mix=[1,' ...
- 小傻蛋的妹妹跟随小甲鱼学习Python的第二十节020
在函数内部仅仅去访问局部变量就好,不要去试图修改它,因为如果修改他的话,Python就会屏蔽(shadowing),会在函数内部创造一个一模一样的变量 内嵌函数 闭包编程方式 更官方的改进方式:non ...
- 学习笔记第五十节:原根相关与二次剩余
正题 原根相关 定义 群:非空集合 G 上定义了一种二元运算,满足封闭性.结合 律.单位元.逆元. 环:非空集合 R 上定义了加法和乘法,在加法下构成交换 群,满足乘法结合律.分配律. 域:非零元素都 ...
- 【框架学习】Struts2(一)工作原理
众所周知Struts2是MVC模式在web开发中经典的框架之一,学习web开发,这也是一个必经过之路吧,实在不行也得瞅瞅,万一哪天用到了. 技术优势: Struts2有两方面的技术优势,一是所有的St ...
- Spring学习总结(26)——Spring事务原理详解
一.事务的基本原理 Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的.对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行: (1).获 ...
- 【C语言进阶深度学习记录】二十二 指针的本质分析
在C语言中,最难的也就是指针了.如果我们了解了指针的本质,它就会变得简单 文章目录 1 回顾:什么是变量? 1.1 *号的意义 1.2 指针使用示例 2 传值调用与传址调用 2.1 利用指针交换两个变 ...
最新文章
- 51nod 1282 时钟
- Codeforces Round #483 (Div. 2) [Thanks, Botan Investments and Victor Shaburov!]
- 常用的php开发工具有哪些?
- 关于计算机的幻想作文600字,科学幻想作文600字
- GIMP的Path的import和export
- (2.1)【经典木马-冰河木马】详细介绍,原理、使用方法
- 计算机c盘怎样重命名,讲解对C盘重命名中无法修改的处理的详细步骤
- 哔哩哔哩视频音频下载器批量下载器
- VS Code下载Beautify扩展插件 | CSDN创作打卡
- Why Would I Ever
- C++与OpenCV同时批量处理图像数据
- sql——字符串处理
- o7_dictionary_accessibility 参数
- 安装屏保软件(Linux终端演示 “黑客帝国” 字母雨界面)和Linux修改管理员密码...
- ImportError: cannot import name ‘etree‘ from ‘lxml‘ 爬虫报错
- (原)Opencv中直方图均衡和图像动态范围拉伸的代码
- CCM5.0 应用实例(SIP X-lite)
- c语言学生管理系统中人数,python下学生管理系统:从文件中读取30位学生的信息(含邮箱),并实现简单的增、删、查找、统计(邮箱使用人数)。---附程序哦!...
- WebMvcConfigurer配置HandlerInterceptor拦截器失效
- 深入浅出通信原理知识点7