项目中常常遇到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)