环境: intellij idea 2017.1.4 + spring boot 1.5.9
简介: 用这种方式实现多数据源的前提必须要清楚两个知识点:AOP原理和AbstractRoutingDataSource抽象类。
1、AOP:相当于拦截器,只要满足要求的都会被拦截过来,然后进行一些列的操作。具体需要自己去体会。。。
2、AbstractRoutingDataSource:这个类是实现多数据源的关键,他的作用就是动态切换数据源,实质:有多少个数据源就存多少个数据源在targetDataSources(是AbstractRoutingDataSource的一个map类型的属性,其中value为每个数据源,key表示每个数据源的名字)这个属性中,然后根据determineCurrentLookupKey()这个方法获取当前数据源在map中的key值,然后determineTargetDataSource()方法中动态获取当前数据源,如果当前数据源不存并且默认数据源也不存在就抛出异常。
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
//多数据源map集合
private Map<Object, Object> targetDataSources;
//默认数据源
private Object defaultTargetDataSource;
//其实就是targetDataSources,后面的afterPropertiesSet()方法会将targetDataSources赋值给resolvedDataSources
private Map<Object, DataSource> resolvedDataSources;
private DataSource resolvedDefaultDataSource;
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
this.targetDataSources = targetDataSources;
}
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = this.determineCurrentLookupKey();
DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
} else {
return dataSource;
}
}
protected abstract Object determineCurrentLookupKey();
}
具体实现:
1、定义一个动态数据源:继承AbstractRoutingDataSource 抽象类,并重写determineCurrentLookupKey()方法
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
DataSourceType.DataBaseType dataBaseType = DataSourceType.getDataBaseType();
return dataBaseType;
}
}
项目文件结构:
附上代码:
application.properties
#spring.datasource.driverClassName=com.mysql.jdbc.Driver
#spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
#spring.datasource.username=root
#spring.datasource.password=
#mybatis.mapper-locations: classpath:mapper/*.xml
server.port=8080
## test1 database
spring.datasource.db1.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.db1.username=root
spring.datasource.db1.password=
spring.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
## test2 database
spring.datasource.db2.url=jdbc:mysql://localhost:3306/news?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.db2.username=root
spring.datasource.db2.password=
spring.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driver
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.neo</groupId>
<artifactId>spring-boot-hello</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>spring-boot-hello</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<!--<version>2.0.0.RELEASE</version>-->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
注:springboot 原来用的是2.0,但有个类(org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;)会找不到,切到1.5.9反而可以。
entity
package com.neo.entity;
import org.springframework.stereotype.Component;
@Component
public class Sound {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
private String title;
}
package com.neo.entity;
import org.springframework.stereotype.Component;
@Component
public class User {
private Long id;
private String username;
private int age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
mapper
package com.neo.mapper.db1;
import com.neo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface UserMapper {
//获取用户名单
List<User> getUser() throws Exception;
//根据id删除用户
void deleteUser(int id)throws Exception;
//新增用户
void addUser(User user)throws Exception;
//修改用户信息
void updateUser(User user) throws Exception;
// //用注解实现的
// //获取用户名单
// @Select("select * from user")
// public List<User> getUser() throws Exception;
// //根据id删除用户
// @Delete("delete from user where id = #{id}")
// public void deleteUser(int id)throws Exception;
// //新增用户
// @Insert("insert into user(id,username,age)values(#{id},#{username},#{age})")
// public void addUser(User user)throws Exception;
// //修改用户信息
// @Update("update user set username = #{name} where id = #{id}")
// public void updateUser(User user) throws Exception;
}
package com.neo.mapper.db2;
import com.neo.entity.Sound;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface SoundMapper {
//获取列表
List<Sound> getSound() throws Exception;
//根据id删除
void deleteSound(int id)throws Exception;
//新增
void addSound(Sound sound)throws Exception;
//修改信息
//public void updateSound(Sound sound) throws Exception;
}
mappering
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://www.mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.neo.mapper.db1.UserMapper">
<select id="getUser" resultType="com.neo.entity.User">
select * from user
<where>
<if test="username != null">
username=#{username}
</if>
<if test="age!= null">
and age=#{age}
</if>
</where>
</select>
<delete id="deleteUser" parameterType="Integer">
delete from user where id =#{id}
</delete>
<insert id="addUser" parameterType="com.neo.entity.User">
insert into user(id,username,age)values(#{id},#{username},#{age})
</insert>
<update id="updateUser" parameterType="com.neo.entity.User">
update user
<set>
<if test = "username != null">
user.username = #{username},
</if>
<if test = "age != 0">
user.age = #{age}
</if>
</set>
where id = #{id}
</update>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://www.mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.neo.mapper.db2.SoundMapper">
<select id="getSound" resultType="com.neo.entity.Sound">
select * from t_sound
<where>
<if test="title != null">
title=#{title}
</if>
<if test="id!= null">
and id=#{id}
</if>
</where>
</select>
<delete id="deleteSound" parameterType="Integer">
delete from t_sound where id =#{id}
</delete>
<insert id="addSound" parameterType="com.neo.entity.Sound">
insert into t_sound(id,title)values(#{id},#{title})
</insert>
<update id="updateSound" parameterType="com.neo.entity.Sound">
update t_sound
<set>
<if test = "title != null">
sound.title = #{title},
</if>
</set>
where id = #{id}
</update>
</mapper>
dao
package com.neo.dao;
import com.neo.entity.Sound;
import com.neo.mapper.db2.SoundMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class SoundDao {
@Autowired
private SoundMapper tm1;
public List<Sound> getSound() {
try {
return tm1.getSound();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
package com.neo.dao;
import com.neo.entity.User;
import com.neo.mapper.db1.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class UserDao {
@Autowired
private UserMapper tm1;
public List<User> getUser() {
try {
return tm1.getUser();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
service
package com.neo.service.db1;
import com.neo.dao.SoundDao;
import com.neo.dao.UserDao;
import com.neo.entity.Sound;
import com.neo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserDao ts1;
@Transactional
public List<User> getUser(){
return ts1.getUser();
}
}
package com.neo.service.db2;
import com.neo.dao.SoundDao;
import com.neo.entity.Sound;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class SoundService {
@Autowired
private SoundDao ts1;
@Transactional
public List<Sound> getSound(){
return ts1.getSound();
}
}
datasource
package com.neo.datasource;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DataSourceAop {
@Before("execution(* com.neo.service.db1..*(..))")
public void setDataSource2test01() {
System.err.println("test01业务");
DataSourceType.setDataBaseType(DataSourceType.DataBaseType.DB1);
}
@Before("execution(* com.neo.service.db2..*(..))")
public void setDataSource2test02() {
System.err.println("test02业务");
DataSourceType.setDataBaseType(DataSourceType.DataBaseType.DB2);
}
}
package com.neo.datasource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
@MapperScan(basePackages = "com.neo.mapper", sqlSessionFactoryRef = "SqlSessionFactory")
public class DataSourceConfig {
@Primary
@Bean(name = "db1DataSource")
@ConfigurationProperties(prefix = "spring.datasource.db1")
public DataSource getDateSource1() {
return DataSourceBuilder.create().build();
}
@Bean(name = "db2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.db2")
public DataSource getDateSource2() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
public DynamicDataSource DataSource(@Qualifier("db1DataSource") DataSource db1DataSource,
@Qualifier("db2DataSource") DataSource db2DataSource) {
Map<Object, Object> targetDataSource = new HashMap<>();
targetDataSource.put(DataSourceType.DataBaseType.DB1, db1DataSource);
targetDataSource.put(DataSourceType.DataBaseType.DB2, db2DataSource);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSource);
dataSource.setDefaultTargetDataSource(db1DataSource);
return dataSource;
}
@Bean(name = "SqlSessionFactory")
public SqlSessionFactory test1SqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dynamicDataSource);
bean.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath*:mappering/*/*.xml"));
return bean.getObject();
}
}
package com.neo.datasource;
public class DataSourceType {
public enum DataBaseType {
DB1, DB2
}
// 使用ThreadLocal保证线程安全
private static final ThreadLocal<DataBaseType> TYPE = new ThreadLocal<DataBaseType>();
// 往当前线程里设置数据源类型
public static void setDataBaseType(DataBaseType dataBaseType) {
if (dataBaseType == null) {
throw new NullPointerException();
}
System.err.println("[将当前数据源改为]:" + dataBaseType);
TYPE.set(dataBaseType);
}
// 获取数据源类型
public static DataBaseType getDataBaseType() {
DataBaseType dataBaseType = TYPE.get() == null ? DataBaseType.DB1 : TYPE.get();
System.err.println("[获取当前数据源的类型为]:" + dataBaseType);
return dataBaseType;
}
// 清空数据类型
public static void clearDataBaseType() {
TYPE.remove();
}
}
package com.neo.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
DataSourceType.DataBaseType dataBaseType = DataSourceType.getDataBaseType();
return dataBaseType;
}
}
control
package com.neo.controller;
import com.neo.entity.Sound;
import com.neo.service.db2.SoundService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class SoundController {
@Autowired
private SoundService soundService;
@Autowired
private Sound sound;
//显示用户
@RequestMapping("sound/list")
public List<Sound> index() throws Exception {
return soundService.getSound();
}
// //增加用户
// @RequestMapping("sound/add")
// public String addSound() throws Exception {
// sound.setTitle("平凡之路");
// soundService.addSound(sound);
// return "增加";
// }
}
package com.neo.controller;
import com.neo.entity.User;
import com.neo.service.db1.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
private UserService userService;
@Autowired
private User user;
//显示用户
@RequestMapping("list")
public List<User> index() throws Exception {
return userService.getUser();
}
// //删除用户
// @RequestMapping("delete/{id}")
// public String delete(@PathVariable int id) throws Exception {
// userService.deleteUser(id);
// return "你已经删掉了id为"+id+"的用户";
// }
// //增加用户
// @RequestMapping("addUser")
// public String addUser() throws Exception {
// user.setAge(33);
// user.setUsername("阿花");
// userService.addUser(user);
// return "增加用户";
// }
}
启动类
package com.neo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HelloApplication {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
}
项目完整包下载地址: 下载
--- end ---