【Spring Boot学习之四】Spring Boot事务管理
环境
eclipse 4.7
jdk 1.8
Spring Boot 1.5.2
一、springboot整合事务
事务分类:编程事务、声明事务(XML、注解),推荐使用注解方式,springboot默认集成事物,只主要在方法上加上@Transactional即可
1、controller
package com.wjy.controller; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import com.wjy.test1.service.UserServiceTest1; @RestController
public class UserController { @Autowired
public UserServiceTest1 userServiceTest1; @RequestMapping("/insertTest1ByService")
public String insertTest1ByService(String name,Integer age) {
userServiceTest1.insertuser1(name, age);
return "success";
} }
2、service
/**
*
*/
package com.wjy.test1.service; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import com.wjy.test1.dao.UserMapperTest1; /**
* @Desc
* @author wangjy15
*/
@Service
public class UserServiceTest1 { @Autowired
private UserMapperTest1 userMapperTest1; /**
* @Description: 如果没有事务控制 那么报错之后 插入到库里的数据不会回滚 加上 注解@Transactional 就可以回滚
*/
@Transactional
public String insertuser1(String name,Integer age) {
userMapperTest1.insert(name, age);
int i =1/0;
return "success";
} }
3、mapper
/**
*
*/
package com.wjy.test1.dao; import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select; import com.wjy.entity.User; /**
* @Desc
* @author wangjy15
*/
public interface UserMapperTest1 { @Select("SELECT * FROM users WHERE NAME = #{name}")
User findByName(@Param("name") String name); @Insert("insert into users (name,age) values(#{name},#{age})")
int insert(@Param("name") String name,@Param("age") Integer age);
}
4、APP
package com.wjy; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
public class APP { public static void main(String[] args) {
SpringApplication.run(APP.class, args);
} }
5、测试验证
http://localhost:8080/insertTest1ByService?name=wangsan0010&age=1000
二、SpringBoot分布式事务管理
使用springboot+jta+atomikos分布式事务管理,service层有事务控制,dao层没有,一般情况下都需要调用其他数据源的dao层,这就需要进行分布式事务管理。
将多个数据源注册到atomikos进行管理,进行2PC(Two-phaseCommit)二阶段提交。
理解一下分布式事务:
对于上面一中示例做一下修改,再引入一个数据源test2,修改一下service:
/**
*
*/
package com.wjy.test1.service; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import com.wjy.test1.dao.UserMapperTest1;
import com.wjy.test2.dao.UserMapperTest2;
import com.wjy.test2.service.UserServiceTest2; /**
* @Desc
* @author wangjy15
*/
@Service
public class UserServiceTest1 { @Autowired
private UserMapperTest1 userMapperTest1; @Autowired
private UserMapperTest2 userMapperTest2; @Autowired
private UserServiceTest2 userServiceTest1; /**
* @Desc: 如果没有事务控制 那么报错之后 插入到库里的数据不会回滚 加上 注解@Transactional 就可以回滚
*/
@Transactional
public String insertuser1(String name,Integer age) {
userMapperTest1.insert(name, age);
userServiceTest1.insertuser2(name, age);//有事务控制 可以回滚
int i =1/0;
return "success";
} }
/**
*
*/
package com.wjy.test2.service; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional; import com.wjy.test2.dao.UserMapperTest2; /**
* @Desc
* @author wangjy15
*/
public class UserServiceTest2 {
@Autowired
private UserMapperTest2 userMapperTest2; /**
* @Desc: 如果没有事务控制 那么报错之后 插入到库里的数据不会回滚 加上 注解@Transactional 就可以回滚
*/
@Transactional
public String insertuser2(String name,Integer age) {
userMapperTest2.insert(name, age);
return "success";
} }
这时test1数据库里没有插入数据,test2数据库也没有插入数据库,因为test2在service层也有事务控制。
再修改一下test1 service:
/**
*
*/
package com.wjy.test1.service; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import com.wjy.test1.dao.UserMapperTest1;
import com.wjy.test2.dao.UserMapperTest2;
import com.wjy.test2.service.UserServiceTest2; /**
* @Desc
* @author wangjy15
*/
@Service
public class UserServiceTest1 { @Autowired
private UserMapperTest1 userMapperTest1; @Autowired
private UserMapperTest2 userMapperTest2;
/**
* @Desc: 如果没有事务控制 那么报错之后 插入到库里的数据不会回滚 加上 注解@Transactional 就可以回滚
*/
@Transactional
public String insertuser1(String name,Integer age) {
userMapperTest1.insert(name, age);
userMapperTest2.insert(name, age);//没有事务控制 不可以回滚 int i =1/0;
return "success";
} }
这时test1数据库里没有插入数据,test2数据库有数据插入到数据库 没有回滚,因为test2在mapper层也没有事务控制。
下面引入atomikos 和springboot整合:
1、数据源配置信息
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
# Mysql 1
mysql.datasource.test1.url = jdbc:mysql://192.168.118.102:3306/springboot?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=utf-8
mysql.datasource.test1.username = root
mysql.datasource.test1.password = 123456 mysql.datasource.test1.minPoolSize = 3
mysql.datasource.test1.maxPoolSize = 25
mysql.datasource.test1.maxLifetime = 20000
mysql.datasource.test1.borrowConnectionTimeout = 30
mysql.datasource.test1.loginTimeout = 30
mysql.datasource.test1.maintenanceInterval = 60
mysql.datasource.test1.maxIdleTime = 60
mysql.datasource.test1.testQuery = select 1 # Mysql 2
mysql.datasource.test2.url =jdbc:mysql://192.168.118.102:3306/springboot2?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=utf-8
mysql.datasource.test2.username =root
mysql.datasource.test2.password =123456 mysql.datasource.test2.minPoolSize = 3
mysql.datasource.test2.maxPoolSize = 25
mysql.datasource.test2.maxLifetime = 20000
mysql.datasource.test2.borrowConnectionTimeout = 30
mysql.datasource.test2.loginTimeout = 30
mysql.datasource.test2.maintenanceInterval = 60
mysql.datasource.test2.maxIdleTime = 60
mysql.datasource.test2.testQuery = select 1
2、数据源配置类
package com.wjy.datasource; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix="mysql.datasource.test1")
public class DBConfig1 { private String url;
private String username;
private String password;
private int minPoolSize;
private int maxPoolSize;
private int maxLifetime;
private int borrowConnectionTimeout;
private int loginTimeout;
private int maintenanceInterval;
private int maxIdleTime;
private String testQuery;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMinPoolSize() {
return minPoolSize;
}
public void setMinPoolSize(int minPoolSize) {
this.minPoolSize = minPoolSize;
}
public int getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public int getMaxLifetime() {
return maxLifetime;
}
public void setMaxLifetime(int maxLifetime) {
this.maxLifetime = maxLifetime;
}
public int getBorrowConnectionTimeout() {
return borrowConnectionTimeout;
}
public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
this.borrowConnectionTimeout = borrowConnectionTimeout;
}
public int getLoginTimeout() {
return loginTimeout;
}
public void setLoginTimeout(int loginTimeout) {
this.loginTimeout = loginTimeout;
}
public int getMaintenanceInterval() {
return maintenanceInterval;
}
public void setMaintenanceInterval(int maintenanceInterval) {
this.maintenanceInterval = maintenanceInterval;
}
public int getMaxIdleTime() {
return maxIdleTime;
}
public void setMaxIdleTime(int maxIdleTime) {
this.maxIdleTime = maxIdleTime;
}
public String getTestQuery() {
return testQuery;
}
public void setTestQuery(String testQuery) {
this.testQuery = testQuery;
} }
package com.wjy.datasource; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix="mysql.datasource.test2")
public class DBConfig2 {
private String url;
private String username;
private String password;
private int minPoolSize;
private int maxPoolSize;
private int maxLifetime;
private int borrowConnectionTimeout;
private int loginTimeout;
private int maintenanceInterval;
private int maxIdleTime;
private String testQuery; public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMinPoolSize() {
return minPoolSize;
}
public void setMinPoolSize(int minPoolSize) {
this.minPoolSize = minPoolSize;
}
public int getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public int getMaxLifetime() {
return maxLifetime;
}
public void setMaxLifetime(int maxLifetime) {
this.maxLifetime = maxLifetime;
}
public int getBorrowConnectionTimeout() {
return borrowConnectionTimeout;
}
public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
this.borrowConnectionTimeout = borrowConnectionTimeout;
}
public int getLoginTimeout() {
return loginTimeout;
}
public void setLoginTimeout(int loginTimeout) {
this.loginTimeout = loginTimeout;
}
public int getMaintenanceInterval() {
return maintenanceInterval;
}
public void setMaintenanceInterval(int maintenanceInterval) {
this.maintenanceInterval = maintenanceInterval;
}
public int getMaxIdleTime() {
return maxIdleTime;
}
public void setMaxIdleTime(int maxIdleTime) {
this.maxIdleTime = maxIdleTime;
}
public String getTestQuery() {
return testQuery;
}
public void setTestQuery(String testQuery) {
this.testQuery = testQuery;
} }
package com.wjy.datasource; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; ////注册到springboot容器中
@Configuration
////@MapperScan可以指定要扫描的Mapper类的包的路径 sqlSessionFactoryRef 表示定义了 key ,表示一个唯一 SqlSessionFactory 实例
@MapperScan(basePackages="com.wjy.test1",sqlSessionFactoryRef="sqlSessionFactory1")
public class TestMyBatisConfig1 { @Bean(name="dataSource1")
@Primary
public DataSource dataSource1(DBConfig1 dbConfig1) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(dbConfig1.getUrl());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setPassword(dbConfig1.getPassword());
mysqlXaDataSource.setUser(dbConfig1.getUsername());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("dataSource1"); xaDataSource.setMinPoolSize(dbConfig1.getMinPoolSize());
xaDataSource.setMaxPoolSize(dbConfig1.getMaxPoolSize());
xaDataSource.setMaxLifetime(dbConfig1.getMaxLifetime());
xaDataSource.setBorrowConnectionTimeout(dbConfig1.getBorrowConnectionTimeout());
xaDataSource.setLoginTimeout(dbConfig1.getLoginTimeout());
xaDataSource.setMaintenanceInterval(dbConfig1.getMaintenanceInterval());
xaDataSource.setMaxIdleTime(dbConfig1.getMaxIdleTime());
xaDataSource.setTestQuery(dbConfig1.getTestQuery());
return xaDataSource;
} //注意 这里没有事务管理类 因为事务全部交给AtomikosDataSourceBean来管理 @Primary
@Bean(name = "sqlSessionFactory1")
public SqlSessionFactory sqlSessionFactory1(@Qualifier("dataSource1") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
} @Primary
@Bean(name = "sqlSessionTemplate1")
public SqlSessionTemplate sqlSessionTemplate1(
@Qualifier("sqlSessionFactory1") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
} }
package com.wjy.datasource; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; @Configuration
@MapperScan(basePackages="com.wjy.test2",sqlSessionFactoryRef="sqlSessionFactory2")
public class TestMyBatisConfig2 { @Bean(name="dataSource2")
public DataSource dataSource2(DBConfig2 dbConfig2) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(dbConfig2.getUrl());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setPassword(dbConfig2.getPassword());
mysqlXaDataSource.setUser(dbConfig2.getUsername());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("dataSource2"); xaDataSource.setMinPoolSize(dbConfig2.getMinPoolSize());
xaDataSource.setMaxPoolSize(dbConfig2.getMaxPoolSize());
xaDataSource.setMaxLifetime(dbConfig2.getMaxLifetime());
xaDataSource.setBorrowConnectionTimeout(dbConfig2.getBorrowConnectionTimeout());
xaDataSource.setLoginTimeout(dbConfig2.getLoginTimeout());
xaDataSource.setMaintenanceInterval(dbConfig2.getMaintenanceInterval());
xaDataSource.setMaxIdleTime(dbConfig2.getMaxIdleTime());
xaDataSource.setTestQuery(dbConfig2.getTestQuery());
return xaDataSource;
} //注意 这里没有事务管理类 因为事务全部交给AtomikosDataSourceBean来管理 @Bean(name = "sqlSessionFactory2")
public SqlSessionFactory sqlSessionFactory2(@Qualifier("dataSource2") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
} @Bean(name = "sqlSessionTemplate2")
public SqlSessionTemplate sqlSessionTemplate2(
@Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
3、controller
package com.wjy.controller; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import com.wjy.test1.service.UserServiceTest1; @RestController
public class UserController { @Autowired
public UserServiceTest1 userServiceTest1; @RequestMapping("/insertTest1ByService")
public String insertTest1ByService(String name,Integer age) {
userServiceTest1.insertuser1(name, age);
return "success";
} }
4、service
/**
*
*/
package com.wjy.test1.service; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import com.wjy.test1.dao.UserMapperTest1;
import com.wjy.test2.dao.UserMapperTest2; /**
* @Desc
* @author wangjy15
*/
@Service
public class UserServiceTest1 { @Autowired
private UserMapperTest1 userMapperTest1; @Autowired
private UserMapperTest2 userMapperTest2; /**
* @Desc: 如果没有事务控制 那么报错之后 插入到库里的数据不会回滚 加上 注解@Transactional 就可以回滚
*/
@Transactional
public String insertuser1(String name,Integer age) {
userMapperTest1.insert(name, age);
userMapperTest2.insert(name, age);//没有事务控制 不可以回滚 int i =1/0;
return "success";
} }
5、APP
package com.wjy; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import com.wjy.datasource.DBConfig1;
import com.wjy.datasource.DBConfig2; @SpringBootApplication
@EnableConfigurationProperties(value = { DBConfig1.class, DBConfig2.class }) //启动的时候读取两个类对应的配置信息
public class APP { public static void main(String[] args) {
SpringApplication.run(APP.class, args);
} }
6、测试验证
http://localhost:8080/insertTest1ByService?name=wangsan&age=10
执行后 两个数据库里都没有数据
【Spring Boot学习之四】Spring Boot事务管理的更多相关文章
- Spring Boot学习——数据库操作及事务管理
本文讲解使用Spring-Data-Jpa操作数据库. JPA定义了一系列对象持久化的标准. 一.在项目中使用Spring-Data-Jpa 1. 配置文件application.properties ...
- spring boot学习(6) SpringBoot 之事务管理
两个操作要么同时成功,要么同时失败: 事务的一致性: 以前学ssh ssm都有事务管理service层通过applicationContext.xml配置,所有service方法都加上事务操作: 用来 ...
- spring框架学习笔记7:事务管理及案例
Spring提供了一套管理项目中的事务的机制 以前写过一篇简单的介绍事务的随笔:http://www.cnblogs.com/xuyiqing/p/8430214.html 还有一篇Hibernate ...
- Spring+Mybatis+MySql+Maven 简单的事务管理案例
利用Maven来管理项目中的JAR包,同时使用Spring在业务处理层进行事务管理.数据库使用MySq,数据处理层使用Spring和Mybatis结合. 本案例代码主要结构如图: 1.数据库脚本 -- ...
- Spring整合JMS(四)——事务管理
原文链接:http://haohaoxuexi.iteye.com/blog/1983532 Spring提供了一个JmsTransactionManager用于对JMS ConnectionFact ...
- Spring整合JMS(四)——事务管理(转)
*注:别人那复制来的 Spring提供了一个JmsTransactionManager用于对JMS ConnectionFactory做事务管理.这将允许JMS应用利用Spring的事务管理特性.Jm ...
- Spring Cloud 学习 之 Spring Cloud Eureka(源码分析)
Spring Cloud 学习 之 Spring Cloud Eureka(源码分析) Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 ...
- Spring Cloud 学习 之 Spring Cloud Eureka(搭建)
Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 文章目录 搭建服务注册中心: 注册服务提供者: 高可用注册中心: 搭建服务注册中心: ...
- spring boot中的声明式事务管理及编程式事务管理
这几天在做一个功能,具体的情况是这样的: 项目中原有的几个功能模块中有数据上报的功能,现在需要在这几个功能模块的上报之后生成一条消息记录,然后入库,在写个接口供前台来拉取消息记录. 看到这个需求,首先 ...
随机推荐
- C++输入输出流 cin/cout 及格式化输出简介
C++ 可通过流的概念进行程序与外界环境( 用户.文件等 )之间的交互.流是一种将数据自源( source )推送至目的地( destination )的管道.在 C++ 中,与标准输入/输出相关的流 ...
- 通信、端点、IO、文件
什么是网络套接字(Socket)?一时还真不好回答,而且网络上也有各种解释,莫衷一是.下文将以本人所查阅到的资料来说明一下什么是Socket. Socket定义 Socket在维基百科的定义: A n ...
- react 沉思录
react = Virtual DOM + component + data flow + jsx 核心是Virtual DOM结构的状态维护.渲染机制及UI系统的DOM组织功能: 基于Virtual ...
- pom.xml中使用“import”的scope来解决Maven项目单继承问题
测试环境 maven 3.3.9 想必大家在做SpringBoot应用的时候,都会有如下代码: <parent> <groupId>org.springframework.bo ...
- 关于“Error: listen EADDRINUSE: address already in use 127.0.0.1:3000”
运行vue项目的时候报 Error: listen EADDRINUSE: address already 这个错,表示3000端口号被占用. 解决方法: 1.打开cmd,执行 netstat -n ...
- CF379C-New Year Ratings Change
https://www.luogu.org/problemnew/show/CF379C 一道水题,折腾了我好久! 解题: 先排序,从小到大挨个满足客户,把最终rating放进集合里,判断是否已经给出 ...
- 51nod1681 公共祖先
[传送门] 很明显,可以转化成求每个点在两棵树中对应的子树中有多少个相同的节点,对答案的贡献就是$C(x, 2)$.关键就是怎么求这个东西.一是,对第一棵树求出dfs序,然后dfs第二棵树,用树状数组 ...
- hadoop spark合并小文件
一.输入文件类型设置为 CombineTextInputFormat hadoop job.setInputFormatClass(CombineTextInputFormat.class) sp ...
- (尚030)Vue_案例_存储数据(localStorage本地存储技术)
当刷新页面时,会变为原来的状态 1.问题:当我刷新是不希望改变原来状态 需要缓存我当前的数据 2.究竟是缓存在内存里还是在文件里??? 缓存在文件里,因为浏览器关闭了,内存就没了;而我们需要重新打开浏 ...
- create-react-app不支持less的解决方式
进入node_modules\react-scripts\config目录 修改webpack.config.dev.js跟webpack.config.prod.js中关于loader的配置即可,注 ...