美文网首页
Spring 事务管理

Spring 事务管理

作者: SmogZhang | 来源:发表于2017-12-10 17:40 被阅读0次

    简单说一下事务

    ''是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。''

    四大特性(ACID)
    • 原子性(Atomicity): 指事务不可分割,要么全做,要么都不做。
    • 一致性(Consisitency):指事务完成前后,所有数据保持一致状态。譬如转账操作,前后总额不能改后变。
    • 隔离性(Isolation):指开启事务后,不受其它事务影响。
    • 持久性(Durability):指事务完成后,对数据库记录的修改是永久的。
    事务隔离级别

    并发的情况下会破坏掉事务的特性,导致操作数据的不一致。情况有以下几种。

    • 脏读
      当前事务读到另一事务未提交的数据。
      我向你转账1元,然后你看到了,给我两个馒头,我拿到馒头走了,接着把事务回滚了。。
    • 不可重复读
      在一个事务中,多次查询同一记录,结果不一致。原因是别的事务修改了该条记录并提交了。
    • 幻读
      开启事务后,两次统计数据库记录条数,结果显示不一致。
      在做统计时就很为难。

    解决办法利用锁机制,常用的是乐观锁,为表中添加一个vision字段;或者直接指定会话的隔离级别。

    注:不可重复读和幻读都是读取别人以及提交的事务,只是前者是读取一个数据项,后者则是针对一批。

    以往对事务的操作

    JDBC中

    //JDBC中,事务自动提交,得手动打开,(伪代码)
    Connection conn =   
        DataSourceUtils.getConnection();  
     //开启事务  
    conn.setAutoCommit(false);  
    try {  
        Object obj =   
            callback.doInConnection(conn);  
        conn.commit(); //提交事务  
        return retVal;  
    }catch (Exception e) {  
        conn.rollback();//回滚事务  
        throw e;  
    }finally {  
        conn.close();  
    }   
    

    后来交由SessionFactory管理

    Session session = factory.openSession();
            try {
                session.beginTransaction();
                //do something
                //session.save(obj);
                session.beginTransaction().commit();
            } catch (HibernateException e) {
                session.beginTransaction().rollback();
                e.printStackTrace();
            } finally {
                if(session != null && session.isOpen()) {
                    session.close();
                }
            }
    

    再后来还是得不到满足,因为每个方法都得这样,重复代码过多。而且扩展性很差。于是想到了ThreadLocal模式,即将session放在ThreadLocal变量中。并利用struts2拦截器,自动开启事务,提交事务和关闭事务。但是你需要每次都获取当前线程的session。

    @Override
        public String intercept(ActionInvocation invocation) throws Exception {
            String ret = null;
            Session session = null;
            try {
                session = HibernateUtil.getSession();
                session.beginTransaction();
                System.out.println("********获取session开启事务***********");
                ret = invocation.invoke();//该拦截器负责为这次请求创建一个session并管理业务和关闭session
                session.getTransaction().commit();
            } catch (Exception e) {
                session.getTransaction().rollback();
                e.printStackTrace();
            } finally {
                if(session != null && session.isOpen()) {
                    System.out.println("********事务结束关闭session***********");
                    session.close();
                }
            }
            return ret;
        }
    

    对的,没错,像下面这样获取session。

    //ThreadLocal local
    public static Session getSession() {
            Session session = local.get();
            if(session == null || !session.isOpen()) {
                session = factory.openSession();
                local.set(session);
            }
            return session;
        }
    

    那么现在,Spring 来了。你不再从应用程序中主动获取资源,你只需要专注你的业务就好。像这种东西,Spring来帮你搞定!

    //既然是事务,Spring提供了一个管理事务的类,我们将其注入IoC容器
    <!-- 注入事务管理类 -->
        <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory"></property>
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    //上面是通过set方法注入了类依赖的对象,我们也要将其注入
    
    //为类中注入了与数据库连接用到的相关参数
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <constructor-arg name="url" value="jdbc:mysql://127.0.0.1:3306/dbname"></constructor-arg>
            <constructor-arg name="username" value="root"></constructor-arg>
            <constructor-arg name="password" value="root"></constructor-arg>
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        </bean>
    
    //这个是hibernate自身属性配置
    <!-- 配置信息建议查看源代码 -->
        <bean id="sessionFactory"
            class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
            <property name="dataSource" ref="dataSource"></property>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                    <prop key="hibernate.show_sql">true</prop>
                    <prop key="hibernate.hbm2ddl.auto">update</prop>
                </props>
            </property>
            <!-- mappingResources 配置.hbm.xml
                源码解释:Set Hibernate mapping resources to be found in the class path,
                       like "example.hbm.xml" or "mypackage/example.hbm.xml".
                packagesToScan 指定注解类的包,自动扫描,类似spring-component组件
                
                都是数组类型
             -->
            <property name="mappingResources">
                <list>
                    <value>User.hbm.xml</value>
                </list>
            </property>
        </bean>
    

    接下来我们要使用AOP,相关注释很明显了。

    <!-- 定义事务通知,定义了如何实施事务(实施事务的方法名和事务属性),
            需要使用事务管理器管理事务,定义了如何选择目标对象的方法及事务的事务属性 
        -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager" >
            <tx:attributes>
                <tx:method name="get*" read-only="true" />
                <tx:method name="list*" propagation="REQUIRED"/>
                <tx:method name="update*" propagation="REQUIRED"/>
                <tx:method name="save*" propagation="REQUIRED"/>
                <tx:method name="delete*" propagation="REQUIRED"/>
            </tx:attributes>
        </tx:advice>
        <!-- 切入点和事务通知,切入点选择实施事务的目标对象 -->
        <aop:config>
            <aop:pointcut id="tx" expression="execution(* cn.smog.action.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="tx" />
        </aop:config>
    

    为了方便理解,我决定画张图。


    AopTx.png

    相关文章

      网友评论

          本文标题:Spring 事务管理

          本文链接:https://www.haomeiwen.com/subject/bdlvixtx.html