項目中常常遇到MyBatis與Spring的組合開發,而且相應的事務管理交給Spring。今天我這裏記錄一下Spring中Mybatis的事務管理。java
先看代碼:mysql
spring-context.xmlspring
<?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:context="http://www.springframework.org/schema/context" 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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--開啓註解--> <context:annotation-config/> <!--加載屬性文件--> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:db.properties</value> </list> </property> </bean> <!--掃描組建--> <context:component-scan base-package="com.xwszt.txdemo"/> <!--開啓事務註解--> <tx:annotation-driven transaction-manager="transactionManager"/> <aop:aspectj-autoproxy proxy-target-class="true"/> <!--配置數據源--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${mysql.jdbc.url}"/> <property name="user" value="${mysql.jdbc.user}"/> <property name="password" value="${mysql.jdbc.password}"/> <!--Connection Pooling Info --> <property name="initialPoolSize" value="3"/> <property name="minPoolSize" value="2"/> <property name="maxPoolSize" value="15"/> <property name="acquireIncrement" value="3"/> <property name="maxStatements" value="8"/> <property name="maxStatementsPerConnection" value="5"/> <property name="maxIdleTime" value="1800"/> <property name="autoCommitOnClose" value="false"/> </bean> <!--mybatis配置--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:mapper/*"/> </bean> <!--mybatis掃描mapper對應類的配置--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.xwszt.txdemo.dao"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> <!--事務配置--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> </beans>
db.propertiessql
##mysql jdbc.driver=com.mysql.jdbc.Driver mysql.jdbc.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8&useSSL=false&useAffectedRows=true&allowPublicKeyRetrieval=true mysql.jdbc.user=root mysql.jdbc.password=*********(這裏根據本身修改)
db.sql數據庫
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(50) DEFAULT NULL, `password` varchar(50) DEFAULT NULL, `salt` varchar(50) DEFAULT NULL, `sex` varchar(10) DEFAULT NULL, `address` varchar(50) DEFAULT NULL, `cellphone` varchar(30) DEFAULT NULL, `email` varchar(30) DEFAULT NULL, `islock` smallint(1) unsigned NOT NULL DEFAULT '0', `isvalidate` smallint(1) unsigned NOT NULL DEFAULT '1', `isdel` smallint(1) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=124 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; SET FOREIGN_KEY_CHECKS = 1
UserDAO.xml網絡
<?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.xwszt.txdemo.dao.UserDAO"> <insert id="insert" parameterType="com.xwszt.txdemo.entities.User"> insert into `user` (`id`, `username`, `password`, `salt`, `sex`, `address`, `cellphone`, `email`, `islock`,`isvalidate`,`isdel`) values (#{id}, #{username},#{password},#{salt},#{sex},#{address},#{cellphone},#{email},#{lock},#{validate},#{del}) </insert> </mapper>
UserDAO.javamybatis
package com.xwszt.txdemo.dao; import com.xwszt.txdemo.entities.User; public interface UserDAO { void insert(User user); }
User.javaapp
package com.xwszt.txdemo.entities; import lombok.Data; import java.io.Serializable; @Data public class User implements Serializable { private Long id; private String username; private String password; private String salt; private String sex; private String address; private String cellphone; private String email; private boolean lock; private boolean validate; private boolean del; }
UserService.javaide
package com.xwszt.txdemo.service; public interface UserService { void doSomething() throws Exception; boolean saveUser() throws Exception; }
UserServiceImpl.java測試
package com.xwszt.txdemo.service.impl; import com.xwszt.txdemo.dao.UserDAO; import com.xwszt.txdemo.entities.User; import com.xwszt.txdemo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserServiceImpl implements UserService { @Autowired private UserService target; @Autowired private UserDAO userDAO; @Override public void doSomething() throws Exception { target.saveUser(); } @Transactional @Override public boolean saveUser() throws Exception { User user = new User(); user.setId(123l); user.setUsername("zhangsan"); user.setPassword("123"); user.setSalt("456"); user.setSex("FEMAIL"); user.setAddress("上海市張江高科"); user.setCellphone("13582911229"); user.setEmail("978732467@qq.com"); user.setLock(false); user.setValidate(true); user.setDel(false); userDAO.insert(user); return true; } }
UserTest.java
package com.xwszt.txdemo; import com.xwszt.txdemo.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring-content.xml") public class UserTest { @Autowired private UserService userService; @Test public void saveUserTest() { try { userService.doSomething(); } catch (Exception e) { e.printStackTrace(); } } }
到此爲止,代碼已經貼完了。
那麼,問題在哪兒呢?
在Service層,若是你仔細看,發如今service層saveUser方法上加了註解@Transactional,那麼運行測試代碼,不出意外(網絡斷了等)的狀況下數據庫裏確定會插入一條數據。
假如,我把@Transactional這個註解去掉了,也就是說saveUser再也不使用spring的事務管理了,那麼數據庫裏是否是沒有插入數據呢?答案是否認的。數據庫裏依然會插入一條數據。
那這又是爲何呢?
若是使用Spring進行事務管理,這裏提交的時候是Spring的事務管理commit了事務。
在沒有使用Spring管理的事務時,是沒有使用Spring容器管理的SqlSession提交了事務。
==================================================
接下來,一個新的問題。
在saveUser方法上使用了@Transactional註解,代表這個方法是Spring容器管理的事務,那麼我在userDAO.insert(user);以後拋出異常,那麼插入的數據會回滾嗎?
@Transactional @Override public boolean saveUser() throws Exception { User user = new User(); user.setId(123l); user.setUsername("zhangsan"); user.setPassword("123"); user.setSalt("456"); user.setSex("FEMAIL"); user.setAddress("上海市張江高科"); user.setCellphone("13582911229"); user.setEmail("978732467@qq.com"); user.setLock(false); user.setValidate(true); user.setDel(false); userDAO.insert(user); if (true) { throw new Exception("破壞性測試"); } return true; }
答案是:不會回滾。
那怎樣纔會回滾呢?配置rollback便可。即:
@Transactional(rollbackFor = Exception.class)