<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.0.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>1.1.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
(2)application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
(3)Mapper代码:
public interface UserMapper {
@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)启动类,这里我们直接用的是@MapperScan而没有在每一个接口上用@Mapper
@MapperScan("com.itmayiedu.mapper")
@SpringBootApplication
public class MybatisApp {
public static void main(String[] args) {
SpringApplication.run(MybatisApp.class, args);
}
}
- 垂直拆分属于分库,水平拆分属于分表
- 根据包来进行划分,就是说包名不同,数据源不同,分包(根据业务来的)
- 使用注解的方式划分多数据源
- 一般情况下根据分包用的比较多:
(1)com.xiyou.test01----datasource01
(2)com.xiyou.test02----datasource02
有点类似于多个不同的jar包,多个不同的业务需求,存放在同一个项目中- 不推荐使用注解的方式实现多数据源,太麻烦
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/test1/*.xml"));
(重要)SpringBoot的2.0版本,即使我们不写@Primary也不会报错。在SpringBoot1.5等版本的时候,如果不指定主数据源(默认数据源)的时候就会报错
java代码实现:
(1)代码架构:
这里以不同的包进行区分,不同的包名下链接不同的数据源
(2)application.properties
server.port=8081
###datasource1 数据源1
spring.datasource.test1.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.test1.jdbc-url = jdbc:mysql://localhost:3307/test?useUnicode=true&characterEncoding=utf-8
spring.datasource.test1.username = root
spring.datasource.test1.password = root
###datasource2 数据源2
spring.datasource.test2.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.test2.jdbc-url = jdbc:mysql://localhost:3307/test_rc?useUnicode=true&characterEncoding=utf-8
spring.datasource.test2.username = root
spring.datasource.test2.password = root
(3)config配置(重要)
package com.xiyou.datasource.config;
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.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
@Configuration
// 表示mapper1包由test1SqlSessionFactory数据源注入
@MapperScan(basePackages = "com.xiyou.datasource.mapper.mapper1", sqlSessionFactoryRef = "test1SqlSessionFactory")
public class DataSourceConfig1 {
/**
* 规定bean的名字,设置注入的数据来源
* @return
*/
@Bean(name = "test1DataSource")
@ConfigurationProperties(prefix = "spring.datasource.test1")
public DataSource testDataSource1(){
return DataSourceBuilder.create().build();
}
/**
* 创建sqlSessionFactory,将自定义的数据源进行注入
* @param dataSource
* @return
*/
@Bean("test1SqlSessionFactory")
@Primary
public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// 如果使用的是xml的形式写sql的话,需要加上下面的配置
// bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper1/test1/*.xml"));
return bean.getObject();
}
/**
* 注入事务的管理
* @param dataSource
* @return
*/
@Bean(name = "test1TransactionManager")
public DataSourceTransactionManager transactionManager(@Qualifier("test1DataSource") DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
/**
* SqlSession模板生成
* @return
*/
@Bean(name = "test1SqlSessionTemplate")
@Primary
public SqlSessionTemplate test1SqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception{
return new SqlSessionTemplate(sqlSessionFactory);
}
}
package com.xiyou.datasource.config;
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.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
@Configuration
// 表示mapper2包由test2SqlSessionFactory数据源注入
@MapperScan(basePackages = "com.xiyou.datasource.mapper.mapper2", sqlSessionFactoryRef = "test2SqlSessionFactory")
public class DataSourceConfig2 {
/**
* 规定bean的名字,设置注入的数据来源
* @return
*/
@Bean(name = "test2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.test2")
public DataSource testDataSource2(){
return DataSourceBuilder.create().build();
}
/**
* 创建sqlSessionFactory,将自定义的数据源进行注入
* @param dataSource
* @return
*/
@Bean("test2SqlSessionFactory")
public SqlSessionFactory test2SqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// 如果使用的是xml的形式写sql的话,需要加上下面的配置
// bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/test2/*.xml"));
return bean.getObject();
}
/**
* 注入事务的管理
* @param dataSource
* @return
*/
@Bean(name = "test2TransactionManager")
public DataSourceTransactionManager transactionManager(@Qualifier("test2DataSource") DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
/**
* SqlSession模板生成
* @return
*/
@Bean(name = "test2SqlSessionTemplate")
public SqlSessionTemplate test2SqlSessionTemplate(@Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception{
return new SqlSessionTemplate(sqlSessionFactory);
}
}
package com.xiyou.datasource.mapper.mapper1;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
/**
* 数据源1 对数据库进行操作
*/
public interface User1Mapper {
@Insert("insert into t_user(userName, age) values(#{name}, #{age})")
public int addUser(@Param("name") String name, @Param("age") Integer age);
}
package com.xiyou.datasource.mapper.mapper2;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
/**
* 数据源2 对数据库进行操作
*/
public interface User2Mapper {
@Insert("insert into user2(NAME, pwd) values(#{name}, #{pwd})")
public int addUser(@Param("name") String name, @Param("pwd") String pwd);
}
package com.xiyou.datasource.service;
import org.springframework.transaction.annotation.Transactional;
/**
* 用户的接口类
*/
@Transactional(transactionManager = "test1TransactionManager")
public interface UserService1 {
/**
* 新增用户(数据源1操作)
* @param name
* @param age
*/
public int addUser(String name, Integer age);
}
package com.xiyou.datasource.service;
import org.springframework.transaction.annotation.Transactional;
/**
* 用户的接口类
*/
@Transactional(transactionManager = "test2TransactionManager")
public interface UserService2 {
/**
* 新增用户(数据源2操作)
* @param name
* @param pwd
* @return
*/
public int addUser(String name, String pwd);
}
package com.xiyou.datasource.service.impl;
import com.xiyou.datasource.mapper.mapper1.User1Mapper;
import com.xiyou.datasource.service.UserService1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 数据源1的实现
*/
@Service
public class UserService1Impl implements UserService1 {
@Autowired
private User1Mapper user1Mapper;
@Override
public int addUser(String name, Integer age) {
int i = user1Mapper.addUser(name, age);
int a = 1/0;
return i;
}
}
package com.xiyou.datasource.service.impl;
import com.xiyou.datasource.mapper.mapper2.User2Mapper;
import com.xiyou.datasource.service.UserService2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 数据源2的实现
*/
@Service
public class UserService2Impl implements UserService2 {
@Autowired
private User2Mapper user2Mapper;
@Override
public int addUser(String name, String pwd) {
int i = user2Mapper.addUser(name, pwd);
return i;
}
}
package com.xiyou.datasource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
// 扫描指定包
@MapperScan(basePackages = {"com.xiyou.datasource.mapper"})
public class MyDatasourceApplication {
public static void main(String[] args) {
SpringApplication.run(MyDatasourceApplication.class, args);
}
}
@Transactional(transactionManager = "test2TransactionManager")
同一个事务下有多个数据源的连接,事务要控制多个数据源的提交与回滚
如上述代码所示的话,因为一个方法中有数据源1和数据源2,但是事务只能管理数据源2,因此数据源1可以成功入库,数据源2则会回滚。
传统的分布式事务解决方案jta+atomikos:将多个数据源事务,注册到同一个全局事务中(几乎不用,了解下行了,性能非常不好,一般就用在多数据源项目,微服务项目中一定不去使用)
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jta-atomikosartifactId>
dependency>
# Mysql 1
mysql.datasource.test1.url = jdbc:mysql://localhost:3306/test01?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test1.username = root
mysql.datasource.test1.password = root
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 2
mysql.datasource.test2.url =jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test2.username =root
mysql.datasource.test2.password =root
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
@Data
@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;
}
@Data
@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;
}
@Configuration
// basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "com.itmayiedu.test01", sqlSessionTemplateRef = "testSqlSessionTemplate")
public class MyBatisConfig1 {
// 配置数据源
@Primary
@Bean(name = "testDataSource")
public DataSource testDataSource(DBConfig1 testConfig) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(testConfig.getUrl());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setPassword(testConfig.getPassword());
mysqlXaDataSource.setUser(testConfig.getUsername());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("testDataSource");
xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
xaDataSource.setTestQuery(testConfig.getTestQuery());
return xaDataSource;
}
@Primary
@Bean(name = "testSqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Primary
@Bean(name = "testSqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("testSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
@Configuration
@MapperScan(basePackages = "com.itmayiedu.test02", sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class MyBatisConfig2 {
// 配置数据源
@Bean(name = "test2DataSource")
public DataSource testDataSource(DBConfig2 testConfig) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(testConfig.getUrl());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setPassword(testConfig.getPassword());
mysqlXaDataSource.setUser(testConfig.getUsername());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("test2DataSource");
xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
xaDataSource.setTestQuery(testConfig.getTestQuery());
return xaDataSource;
}
@Bean(name = "test2SqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Bean(name = "test2SqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
@EnableConfigurationProperties(value = {DBConfig1.class. DBConfig2.class})