SpringBoot的事务管理以及配置多个数据源
事务的概念
什么是事务:就是指多个sql语句作为单一逻辑单元进行执行的操作,要么全部执行,要么全都不执行
特性:ACID
原子性:多条sql语句,要么全执行,要么全不执行
一致性:事务完成,必须使所有的数据保持一致
隔离性:指当前的事务必须与与其他事务隔离开(不能同时操作一个数据)
持久性:事务执行完毕之后,对数据的影响是永久的
如何实现事务?
@Override
@Transactional
public void testTran(User user){
usermapper.update(user);
int a=2/0;
usermapper.delete(user);
}
当我们在实现的方法上添加 @Transactional注解时,改方法就已经有了事务的支持。通俗的说,当整个方法运行时,首先运行完毕,期间遇到的所有sql语句先暂放起来,如何这个方法没有报错,那么所有的sql语句一起执行
多数据源的配置
看来网上的教程,很多都一样,但是当自己做起来的时候,问题可多了!中间一直出错,一直在解决错误,总算是成功了!
1.添加maven依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
<!-- 整合spring-data-jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
2.新建一个实体
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author Administrator
*/
@Data
//jpa下的注解
@Entity
@Table(name="t_user")
public class User implements Serializable{
private static final long serialVersionUID = -6503710493488425450L;
@Id
@GeneratedValue
@Column(name = "id")
private Integer id;
@Column(name = "loginid")
private String loginid;
@Column(name = "pwd")
private String pwd;
@Column(name = "roleid")
private Integer roleid;
@Column(name = "username")
private String username;
@Column(name = "deleteflag")
private Byte deleteflag;
@Column(name = "classid")
private Integer classid;
@Column(name = "openid")
private String openid;
@Column(name = "enablesign")
private Byte enablesign;
@Column(name = "type")
private Byte type;
@Column(name = "belongs")
private Byte belongs;
@Column(name = "blemac")
private String blemac;
@Column(name = "region")
private Byte region;
@Column(name = "begintime")
private String begintime;
@Transient
private Integer stepnums;
public User(String loginid, String pwd, String username) {
this.loginid = loginid;
this.pwd = pwd;
this.username = username;
}
}
3.配置多数据源
spring:
datasource:
master:
jdbc-url: jdbc:mysql://localhost:3306/springboot?serverTimezone=Asia/Shanghai
username: root
password: zhou
driver-class-name: com.mysql.jdbc.Driver
slave:
jdbc-url: jdbc:mysql://localhost:3306/zgq?serverTimezone=Asia/Shanghai
username: root
password: zhou
driver-class-name: com.mysql.jdbc.Driver
注意: 这里需要用jdbc-url,否则会报错:
jdbcUrl is required with driverClassName.
4.配置主数据源配置文件
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.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
/**
* 主数据库连接
*/
//表示这个类为一个配置类
@Configuration
// 配置mybatis的接口类放的地方
@MapperScan(basePackages = "com.example.echart.mapper.master", sqlSessionTemplateRef = "masterSqlSessionTemplate")
public class MasterDataSourceConfig {
// 将这个对象放入Spring容器中
@Bean(name = "masterDataSource")
// 读取application.properties中的配置参数映射成为一个对象,prefix表示参数的前缀
@ConfigurationProperties(prefix = "spring.datasource.master")
// 表示这个数据源是默认数据源
@Primary
public DataSource testDataSource() {
return DataSourceBuilder.create().build();
}
/**
* @Qualifier表示查找Spring容器中名字为masterDataSource的对象
* @param dataSource
* @return
* @throws Exception
*/
@Bean(name = "masterSqlSessionFactory")
@Primary
public SqlSessionFactory testSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Bean(name = "masterTransactionManager")
@Primary
public DataSourceTransactionManager testTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "masterSqlSessionTemplate")
@Primary
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
5.配置次数据源配置文件
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.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
/**
* 从数据库连接
*/
//这里配置mapper所在的包
@Configuration
@MapperScan(basePackages = "com.example.echart.mapper.slave", sqlSessionTemplateRef = "slaveSqlSessionTemplate")
public class SlaveDataSourceConfig {
// 将这个对象放入Spring容器中
@Bean(name = "slaveDataSource")
// 读取application.properties中的配置参数映射成为一个对象,prefix表示参数的前缀
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource testDataSource() {
return DataSourceBuilder.create().build();
}
/**
* @Qualifier表示查找Spring容器中名字为slaveDataSource的对象
* @param dataSource
* @return
* @throws Exception
*/
@Bean(name = "slaveSqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Bean(name = "slaveTransactionManager")
public DataSourceTransactionManager testTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "slaveSqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
6.新建控制层,查询多数据库的数据
@Controller
@RequestMapping(value={"/user"})
public class UserController {
@Autowired
User1Mapper user1Mapper;
@Autowired
User2Mapper user2Mapper;
@RequestMapping(value = "getUser1")
@ResponseBody
public Object getUser1(){
return user1Mapper.getUserById(1);
}
@RequestMapping(value = "getUser2")
@ResponseBody
public Object getUser2(){
return user2Mapper.getUserById(1);
}
}
7.总是报错的缘由
1.首先这是我的项目目录
在加入多数据源之前,我就已经整合了很多的技术,比如mapper在加入的时候,默认的扫描路径mapper文件,所以当我在mapper文件下加入文件夹的时候,默认的数据源路径后面增加了master路径,一般来说spring boot默认的扫描路径是启动类当前的包和子包,拿我当前的项目来说,启动类的路径是:com.example.echart,数据接口类是它的子包:com.example.echart.mapper,但是当我们在mapper中加入文件夹的时候,项目是扫描不到的,所以就会出现注入的一个报错
Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
解决的办法就是在启动类上增加注解,告诉项目默认mapper的接口路径
//指定mapper文件的位置
@MapperScan("com.example.echart.mapper")
7.运行项目,查看运行结构
这样就可以看到,我们的两个方法已经从两个数据库中取出了数据