zoukankan      html  css  js  c++  java
  • Spring对hibernate的事物管理

    Hibernate用到的数据源Datasource,Hibernate的SessionFactory实例,事务管理器HibernateTransactionManager,都交给Spring管理。
    .事务的4个特性:
       原子性:一个事务中所有对数据库的操作是一个不可分割的操作序列,要么全做,要么全部做。
       一致性:数据不会因为事务的执行而遭到破坏。
       隔离性:一个事务的执行,不受其他事务(进程)的干扰。既并发执行的个事务之间互不干扰。
       持久性:一个事务一旦提交,它对数据库的改变将是永久的。
    .事务的实现方式:
    实现方式共有两种:编码方式;声明式事务管理方式。
    基于AOP技术实现的声明式事务管理,实质就是:在方法执行前后进行拦截,然后在目标方法开始之前创建并加入事务,执行完目标方法后根据执行情况提交或回滚事务。
    声明式事务管理又有两种方式:基于XML配置文件的方式;另一个是在业务方法上进行@Transactional注解,将事务规则应用到业务逻辑中(一般定在service层)。
    .创建事务的时机:

    是否需要创建事务,是由事务传播行为控制的。读数据不需要或只为其指定只读事务,而数据的插入,修改,删除就需要事务管理了,这是由事务的隔离级别控制的

    四、事物的级别:

    事物的级别分为7个传播级别和4个隔离级别,传播级别定义的是事务创建的时机,隔离级别定义的是对并发事务数据读取的控制

    7个传播级别:

    PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
    PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。 
    PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。 
    PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。 
    PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 
    PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。

    1: PROPAGATION_REQUIRED
    加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务 
    比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候, 
    ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA
    的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。 
    这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被
    提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚 

    2: PROPAGATION_SUPPORTS 
    如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行 


    3: PROPAGATION_MANDATORY 
    必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常 

    4: PROPAGATION_REQUIRES_NEW 
    这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,
    那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,
    他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在 
    两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,
    如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。 

    5: PROPAGATION_NOT_SUPPORTED 
    当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,
    那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。

    6: PROPAGATION_NEVER 
    不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,
    那么ServiceB.methodB就要抛出异常了。 

    7: PROPAGATION_NESTED 
    理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,
    而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。 
    而Nested事务的好处是他有一个savepoint。

    4个隔离级别:

    1、Serializable:最严格的级别,事务串行执行,资源消耗最大; 
    2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
    3、READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
    4、Read Uncommitted:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。

    由于隔离级别导致的问题:

    1: Dirty reads--读脏数据。也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。
    2: non-repeatable reads--数据不可重复读。比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成 200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。
    3: phantom reads--幻象读数据,这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non-repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据),但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。

     

    Dirty reads

    non-repeatable reads

    phantom reads

    Serializable

    不会

    不会

    不会

    REPEATABLE READ

    不会

    不会

    READ COMMITTED

    不会

    Read Uncommitted

    五、spring管理声明式事务的配置:

     1、基于XML配置文件的AOP和TX配置方式

    在applicationContext.xml中配置
    <?xml version="1.0" encoding="gbk"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">


        <bean id="dataSource"
            class="org.apache.commons.dbcp.BasicDataSource">
            <property name="driverClassName"
                value="com.microsoft.sqlserver.jdbc.SQLServerDriver">
            </property>
            <property name="url"
                value="jdbc:sqlserver://localhost:1500;databaseName=ssh">
            </property>
            <property name="username" value="sa"></property>
            <property name="password" value="sa"></property>
        </bean>
        <bean id="sessionFactory"
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            <property name="dataSource">
                <ref bean="dataSource" />
            </property>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">
                        org.hibernate.dialect.SQLServerDialect
                    </prop>
                </props>
            </property>
            <property name="mappingResources">
                <list>
                    <value>bank/entity/Account.hbm.xml</value>
                </list>
            </property>
        </bean>
       
        <bean id="AccountDAO" class="bank.dao.AccountDAO">
            <property name="sessionFactory">
                <ref bean="sessionFactory" />
            </property>
        </bean>

        <bean id="AccountManager" class="bank.biz.AccountManager">
            <property name="dao">
                <ref bean="AccountDAO" />
            </property>
        </bean>
       
        <bean name="/account" class="bank.action.AccountAction">
            <property name="accountManager">
                <ref bean="AccountManager" />
            </property>
        </bean>   
       
        <!--通用事务管理器-->
        <bean id="TransactionManager"
            class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
        <!--指定事务策略,声明一个通知,用以指出要管理哪些事务方法及如何管理-->
        <tx:advice id="txAdvice" transaction-manager="TransactionManager">
            <tx:attributes>
                <!-- 对get/load/search开头的方法要求只读事务 -->
                <tx:method name="find*" propagation="SUPPORTS"
                    read-only="true" />
                <!-- 对其它方法要求事务 -->
                <tx:method name="*" propagation="REQUIRED" />
            </tx:attributes>
        </tx:advice>
       
        <!--声明一个config,用以将事务策略和业务类关联起来-->
        <aop:config>
            <!-- 添加事务支持,因为前面配置的transactionManager是专对Hibernate的事务管理器-->
            <aop:pointcut id="bizMethods" expression="execution(* bank.biz..*.*(..))" />
            <!-- 织入 -->
            <aop:advisor advice-ref="txAdvice" pointcut-ref="bizMethods" />
        </aop:config>           
    </beans>

    2、基于anotation注解形式的事务管理:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd ">
        <!-- 需要引入tx的命名空间 -->
        <tx:annotation-driven transaction-manager="transactionManager" />
            <bean id="dataSource"
            class="org.apache.commons.dbcp.BasicDataSource">
            <property name="driverClassName"
                value="com.microsoft.sqlserver.jdbc.SQLServerDriver">
            </property>
            <property name="url"
                value="jdbc:sqlserver://localhost:1500;databaseName=ssh">
            </property>
            <property name="username" value="sa"></property>
            <property name="password" value="sa"></property>
        </bean>
        <bean id="sessionFactory"
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            <property name="dataSource">
                <ref bean="dataSource" />
            </property>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">
                        org.hibernate.dialect.SQLServerDialect
                    </prop>
                </props>
            </property>
            <property name="mappingResources">
                <list>
                    <value>bank/entity/Account.hbm.xml</value>
                </list>
            </property>
        </bean>
        <bean id="tblUserDAO" class="com.angi.dao.TblUserDAO">
            <property name="sessionFactory">
                <ref bean="sessionFactory" />
            </property>
        </bean>
        <bean id="tblUserService" class="com.angi.dao.service.TblUserService">
            <property name="tblUserDAO">
                <ref bean="tblUserDAO" />
            </property>
        </bean>
        <!-- 声明一个 Hibernate3 的事务管理器供代理类自动管理事务用 -->
        <bean id="transactionManager"
            class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory">
                <ref local="sessionFactory" />
            </property>
        </bean>
    </beans>

    @Transactional
        public void doTransaction() {
            // step1 insert
            TblUser tblUser1 = new TblUser();
            tblUser1.setId(24);
            tblUser1.setUsername("Angi12");
            tblUser1.setPassword("Wang21");
            tblUserDAO.save(tblUser1);
            // step2 update
            TblUser tblUser2 = tblUserDAO.findById(2);
            tblUser2.setPassword(tblUser2.getPassword() + "a");
            tblUserDAO.update(tblUser2);
            // step3 insert
            TblUser tblUser = new TblUser();
            tblUser.setId(23);
            tblUser.setUsername("Angi");
            tblUser.setPassword("Wang");
            tblUserDAO.save(tblUser);
        }

    六、简单说一下SessionFactory、Session

    1、Hibernate中SessionFactory对象的创建代价很高,它是线程安全的对象,被设计成可以为所有的应用程序线程所共享。通常,SessionFactory会在应用程序启动时创建,一旦创建了SessionFactory将不会轻易关闭,只有当应用关闭时,SessionFactory才会关闭。
    2、而Session的对象是轻量级的,它是线程不安全的。对于单个业务进程单个工作单元而言,Session只被使用一次。创建Session时,并不会立即打开与数据库之间的连接,Session只在需要进行数据库操作时,才会获取JDBC连接。因此,打开和关闭Session,并不会对性能造成很大的影响。甚至即使无法确定一个请求是否需要数据访问,也可以打开Session对象,因为如果不进行数据库访问,Session不会获取JDBC连接。
    使用Spring管理hibernate的事务,在每个dao操作中使用SessionFactory.getCurrentSession()方法,该方法可以得到当前事务绑定的session。同时当前的Session和关联的Hibernate事务被绑定到当前线程上,虽然session不是线程安全的,但是通过这样的方式,每一个session都处于单线程中,避免session线程安全问题

    3、不通过Spring管理事务,开启事务的主动性:

    在sessionFactory.openSession()中,Hibernate会初始化数据库连接,与此同时,将其 AutoCommit设为关闭状态,这就是说,从SessionFactory获得session,其自动提交属性就已经被关闭了,事务需要主动、显示的调用才能生效,下面的代码不会对事务性数据库产生任何效果。
    session=sessionFactory.openSession();
       session.save(user);
       session.close();
    如果要使得代码真正作用到数据库,必须显示的调用Transaction指令
       session=sessionFactory.openSession();
       Transaction tx = session.beginTransaction();
       session.save(user);
       tx.commit();
       session.close();

    七、JAVA中的事务管理:JDBC事务、JTA(Java Transaction API)事务、容器事务

    三种事务差异: 
    1、JDBC事务控制的局限性在一个数据库连接内,但是其使用简单。 
    2、JTA事务的功能强大,事务可以跨越多个数据库或多个DAO,使用也比较复杂。 
    3、容器事务,主要指的是J2EE应用服务器提供的事务管理,局限于EJB应用使用。

    详细参照:http://zhidao.baidu.com/question/121440924.html

  • 相关阅读:
    Java练习 SDUT-1117_求绝对值(选择结构)
    Java练习 SDUT-2561_九九乘法表
    Java练习 SDUT-1160_某年某月的天数
    HDU-1024_Max Sum Plus Plus
    博客园页面DIY
    JDBC
    JavaSE | Lambda| Optional| Stream API
    JavaSE| 网络编程
    JavaSE| 反射
    JavaSE | IO流
  • 原文地址:https://www.cnblogs.com/tian830937/p/5181351.html
Copyright © 2011-2022 走看看