1. 程式人生 > 其它 >ssm-spring整合mybatis事務

ssm-spring整合mybatis事務

ssm-spring整合mybatis事務

事務

MyBatis-Spring庫的引入,無需建立新的MyBatis事務管理器,就能使MyBatis接入到Spring事。 引入的方式既可以是註解,也可以是aop。

未配置事務例項

首先來看看未配置事務時,執行一組先增加後刪除(刪除異常)的資料庫語句場景。資料庫連線配置延用之前的,這裡不再介紹。
  1. 編寫DAO
  2. public interface StudentMapper {
        void add(Map<Object, Object> student);
        void delete(String name);
    }
    聲明瞭新增和刪除兩個介面。
  3. mapper配置檔案中編寫對應sql:
  4. <?xml version="1.0" encoding="UTF-8" ?>
            <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.zx.demo.mybatis.spring.mapper.StudentMapper">
        <insert id="add">
            INSERT INTO student (`name`,sex) VALUES (#{name},#{sex});
        </insert>
        <delete id="delete">
            DELETE FROM student WHERE `name` = #{name};
        </delete>
    </mapper>
  5. 分別編寫測試方法測試正確的增加和刪除:
  6. 增加:
        @Test
        public void testTransaction() throws Exception {
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
            StudentMapper mapper = (StudentMapper) context.getBean("exampleMapper");
            Map<Object, Object> student = new HashMap<>();
            student.put("name", "曹操");
            student.put("sex", "男");
            mapper.add(student);
        }
    
    執行後確定資料庫中有寫入一條新的資料:

    刪除:
        @Test
        public void testTransaction() throws Exception {
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
            StudentMapper mapper = (StudentMapper) context.getBean("exampleMapper");
            mapper.delete("曹操");
        }
    
    執行後正確的將剛才新增的“曹操”記錄刪除:

  7. 模擬異常事務
  8. 現在模擬場景:新增後立即刪除。如果新增成功但是刪除失敗,此時我們的需求期望時整個過程需要回滾。現在先將刪除sql故意寫錯來模擬,看看異常現象:
    <delete id="delete">
        DELETE FROM student WHERE `name` = #{name}s;
    </delete>
    @Test
    public void testTransaction() throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        StudentMapper mapper = (StudentMapper) context.getBean("exampleMapper");
        Map<Object, Object> student = new HashMap<>();
        student.put("name", "曹操");
        student.put("sex", "男");
        mapper.add(student);
        mapper.delete("曹操");
    }
    
    執行時發現,新增成功但是刪除時丟擲了語法錯誤的異常:

    我們看看資料庫結果:

    雖然整個過程的第二步刪除失敗了,但資料還是寫入到了資料庫,這顯然與需求不符,下面我們來看看Mybatis-Spring是如何管理事務的。

Mybatis-Spring事務

基於上面的需求,我們現在來看看通過事務,將寫入和刪除一致性實現。按照官方的事務配置說明,這裡介紹註解式事務實現。
  • 將資料庫操作從test中抽離到業務介面
  • public class StudentServiceImpl implements StudentService {
        private StudentMapper mapper;
    
    public StudentMapper getMapper() {
        return mapper;
    }
    
    public void setMapper(StudentMapper mapper) {
        this.mapper = mapper;
    }
    
    @Override
    public void handle() throws Exception {
        Map&lt;Object, Object> student = new HashMap&lt;>();
        student.put("name", "曹操");
        student.put("sex", "男");
        mapper.add(student);
        mapper.delete("曹操");
    }
    

    }


  • 配置業務介面實現例項

  •     <!--業務實現類,內部通過mapper介面實現了資料業務操作-->
    <bean id="studentServiceImpl" class="com.zx.demo.mybatis.spring.service.StudentServiceImpl">
    <property name="mapper" ref="exampleMapper"/>
    </bean>

  • 配置Spring資料來源事務管理器

  •     <!--配置Spring資料來源事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--使用上面宣告的資料來源-->
    <constructor-arg ref="dataSource"/>
    </bean>

    注意:提供給transactionManager的資料來源一定和建立sqlSessionFactory使用同一個,否則無法生效。
  • 配置aop

  •     <!--aop注入-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
    <tx:method name="" propagation="REQUIRED"/>
    </tx:attributes>
    </tx:advice>
    <aop:config>
    <!--這裡一定是注入到mapper對應的業務介面或其實現方法上,否則不生效,也不要用在@Test方法上-->
    <!--<aop:pointcut id="p" expression="execution(
    com.zx.demo.mybatis.spring.service.StudentServiceImpl.(..))"/>-->
    <aop:pointcut id="p" expression="execution(
    com.zx.demo.mybatis.spring.service.StudentService.(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="p"/>
    </aop:config>

    注意:aop切入點一定是注入到mapper對應的業務介面或其實現方法上,否則不生效,也不要用在@Test方法上
  • 完整配置:

  • <?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:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置資料來源,可以是實現了javax.sql.DataSource介面的任意資料來源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://xxxx"/>
    <property name="username" value="xxx"/>
    <property name="password" value="xxx"/>
    </bean>
    <!--1、SqlSessionFactoryBean:實現了介面org.springframework.beans.factory.FactoryBean-->
    <!--2、SqlSessionFactory:通過SqlSessionFactoryBean物件的getObject()方法來構建-->
    <!--3、dataSource和mapperLocations:getObject()方法中建立SqlSessionFactory物件的屬性-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath
    :com/zx/demo/mybatis/**/.xml"/>
    </bean>
    <!--1、MapperFactoryBean:實現了介面org.springframework.beans.factory.FactoryBean-->
    <!--2、StudentMapper:通過MapperFactoryBean物件的getObject()方法來構建-->
    <!--3、mapperInterface和sqlSessionFactory:getObject()方法中建立StudentMapper介面代理實現物件的屬性-->
    <bean id="exampleMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="com.zx.demo.mybatis.spring.mapper.StudentMapper"/>
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    <!--業務實現類,內部通過mapper介面實現了資料業務操作-->
    <bean id="studentServiceImpl" class="com.zx.demo.mybatis.spring.service.StudentServiceImpl">
    <property name="mapper" ref="exampleMapper"/>
    </bean>
    <!--開啟Spring資料來源事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--使用上面宣告的資料來源-->
    <constructor-arg ref="dataSource"/>
    </bean>
    <!--aop注入-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
    <tx:method name="
    " propagation="REQUIRED"/>
    </tx:attributes>
    </tx:advice>
    <aop:config>
    <!--這裡一定是注入到mapper對應的業務介面或其實現方法上,否則不生效,也不要用在@Test方法上-->
    <!--<aop:pointcut id="p" expression="execution(* com.zx.demo.mybatis.spring.service.StudentServiceImpl.(..))"/>-->
    <aop:pointcut id="p" expression="execution(
    com.zx.demo.mybatis.spring.service.StudentService.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="p"/>
    </aop:config>
    </beans>

  • 編寫測試程式碼:

  •     @Test
    public void testTransaction() throws Exception {
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
    StudentService studentService = (StudentService) context.getBean("studentServiceImpl");
    studentService.handle();
    }

  • 執行檢視結果:



  • 刪除語句依舊報語法錯誤,我們到資料庫中看是否插入成功:



    可以看出,並沒有寫入“曹操”這條資料,說明事務生效。