Spring 事务管理

2017-12-10  本文已影响0人  SmogZhang

简单说一下事务

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

四大特性(ACID)
事务隔离级别

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

解决办法利用锁机制,常用的是乐观锁,为表中添加一个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
上一篇 下一篇

猜你喜欢

热点阅读