这里有新鲜出炉的精品教程,程序狗速度看过来!
Spring是什么呢?首先它是一个开源的项目,而且目前非常活跃;它是一个基于IOC和AOP的构架多层j2ee系统的框架,但它不强迫你必须在每一层 中必须使用Spring,因为它模块化的很好,允许你根据自己的需要选择使用它的某一个模块;它实现了很优雅的MVC,对不同的数据访问技术提供了统一的接口,采用IOC使得可以很容易的实现bean的装配,提供了简洁的AOP并据此实现Transcation Managment,等等
本篇文章主要介绍了springboot + mybatis配置多数据源示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源。
代码结构:
简要原理:
1)DatabaseType列出所有的数据源的key---key
2)DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法
3)DynamicDataSource继承AbstractRoutingDataSource并重写其中的方法determineCurrentLookupKey(),在该方法中使用DatabaseContextHolder获取当前线程的DatabaseType
4)MyBatisConfig中生成2个数据源DataSource的bean---value
5)MyBatisConfig中将1)和4)组成的key-value对写入到DynamicDataSource动态数据源的targetDataSources属性(当然,同时也会设置2个数据源其中的一个为DynamicDataSource的defaultTargetDataSource属性中)
6)将DynamicDataSource作为primary数据源注入到SqlSessionFactory的dataSource属性中去,并且该dataSource作为transactionManager的入参来构造DataSourceTransactionManager
7)使用的时候,在dao层或service层先使用DatabaseContextHolder设置将要使用的数据源key,然后再调用mapper层进行相应的操作,建议放在dao层去做(当然也可以使用spring aop+自定注解去做)
注意:在mapper层进行操作的时候,会先调用determineCurrentLookupKey()方法获取一个数据源(获取数据源:先根据设置去targetDataSources中去找,若没有,则选择defaultTargetDataSource),之后在进行数据库操作。
1、假设有两个数据库,配置如下
application.properties
- #the first datasource
- jdbc.driverClassName = com.mysql.jdbc.Driver
- jdbc.url = jdbc:mysql://xxx:3306/mytestdb?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
- jdbc.username = root
- jdbc.password = 123
- #the second datasource
- jdbc2.driverClassName = com.mysql.jdbc.Driver
- jdbc2.url = jdbc:mysql://xxx:3306/mytestdb2?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
- jdbc2.username = root
- jdbc2.password = 123
说明:在之前的配置的基础上,只增加了上述的第二个数据源。
2、DatabaseType
- package com.xxx.firstboot.common.datasource;
- /**
- * 列出所有的数据源key(常用数据库名称来命名)
- * 注意:
- * 1)这里数据源与数据库是一对一的
- * 2)DatabaseType中的变量名称就是数据库的名称
- */
- public enum DatabaseType {
- mytestdb,
- mytestdb2
- }
作用:列举数据源的key。
3、DatabaseContextHolder
- package com.xxx.firstboot.common.datasource;
- /**
- * 作用:
- * 1、保存一个线程安全的DatabaseType容器
- */
- public class DatabaseContextHolder {
- private static final ThreadLocal < DatabaseType > contextHolder = new ThreadLocal < >();
- public static void setDatabaseType(DatabaseType type) {
- contextHolder.set(type);
- }
- public static DatabaseType getDatabaseType() {
- return contextHolder.get();
- }
- }
作用:构建一个DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法
4、DynamicDataSource
- package com.xxx.firstboot.common.datasource;
- import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
- /**
- * 动态数据源(需要继承AbstractRoutingDataSource)
- */
- public class DynamicDataSource extends AbstractRoutingDataSource {
- protected Object determineCurrentLookupKey() {
- return DatabaseContextHolder.getDatabaseType();
- }
- }
作用:使用DatabaseContextHolder获取当前线程的DatabaseType
5、MyBatisConfig
- package com.xxx.firstboot.common;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Properties;
- import javax.sql.DataSource;
- import org.apache.ibatis.session.SqlSessionFactory;
- import org.mybatis.spring.SqlSessionFactoryBean;
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.beans.factory.annotation.Autowired;
- 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 org.springframework.core.env.Environment;
- import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
- import org.springframework.jdbc.datasource.DataSourceTransactionManager;
- import com.alibaba.druid.pool.DruidDataSourceFactory;
- import com.xxx.firstboot.common.datasource.DatabaseType;
- import com.xxx.firstboot.common.datasource.DynamicDataSource;
- /**
- * springboot集成mybatis的基本入口 1)创建数据源(如果采用的是默认的tomcat-jdbc数据源,则不需要)
- * 2)创建SqlSessionFactory 3)配置事务管理器,除非需要使用事务,否则不用配置
- */
- @Configuration // 该注解类似于spring配置文件
- @MapperScan(basePackages = "com.xxx.firstboot.mapper")
- public class MyBatisConfig {
- @Autowired
- private Environment env;
- /**
- * 创建数据源(数据源的名称:方法名可以取为XXXDataSource(),XXX为数据库名称,该名称也就是数据源的名称)
- */
- @Bean
- public DataSource myTestDbDataSource() throws Exception {
- Properties props = new Properties();
- props.put("driverClassName", env.getProperty("jdbc.driverClassName"));
- props.put("url", env.getProperty("jdbc.url"));
- props.put("username", env.getProperty("jdbc.username"));
- props.put("password", env.getProperty("jdbc.password"));
- return DruidDataSourceFactory.createDataSource(props);
- }
- @Bean
- public DataSource myTestDb2DataSource() throws Exception {
- Properties props = new Properties();
- props.put("driverClassName", env.getProperty("jdbc2.driverClassName"));
- props.put("url", env.getProperty("jdbc2.url"));
- props.put("username", env.getProperty("jdbc2.username"));
- props.put("password", env.getProperty("jdbc2.password"));
- return DruidDataSourceFactory.createDataSource(props);
- }
- /**
- * @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
- * @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
- */
- @Bean
- @Primary
- public DynamicDataSource dataSource(@Qualifier("myTestDbDataSource") DataSource myTestDbDataSource,
- @Qualifier("myTestDb2DataSource") DataSource myTestDb2DataSource) {
- Map<Object, Object> targetDataSources = new HashMap<>();
- targetDataSources.put(DatabaseType.mytestdb, myTestDbDataSource);
- targetDataSources.put(DatabaseType.mytestdb2, myTestDb2DataSource);
- DynamicDataSource dataSource = new DynamicDataSource();
- dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
- dataSource.setDefaultTargetDataSource(myTestDbDataSource);// 默认的datasource设置为myTestDbDataSource
- return dataSource;
- }
- /**
- * 根据数据源创建SqlSessionFactory
- */
- @Bean
- public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {
- SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
- fb.setDataSource(ds);// 指定数据源(这个必须有,否则报错)
- // 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
- fb.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));// 指定基包
- fb.setMapperLocations(
- new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));//
- return fb.getObject();
- }
- /**
- * 配置事务管理器
- */
- @Bean
- public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
- return new DataSourceTransactionManager(dataSource);
- }
- }
作用:
6、使用
ShopMapper:
- package com.xxx.firstboot.mapper;
- import org.apache.ibatis.annotations.Param;
- import org.apache.ibatis.annotations.Result;
- import org.apache.ibatis.annotations.Results;
- import org.apache.ibatis.annotations.Select;
- import com.xxx.firstboot.domain.Shop;
- public interface ShopMapper {
- @Select("SELECT * FROM t_shop WHERE id = #{id}")@Results(value = {@Result(id = true, column = "id", property = "id"),
- @Result(column = "shop_name", property = "shopName")
- }) public Shop getShop(@Param("id") int id);
- }
ShopDao:
- package com.xxx.firstboot.dao;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Repository;
- import com.xxx.firstboot.common.datasource.DatabaseContextHolder;
- import com.xxx.firstboot.common.datasource.DatabaseType;
- import com.xxx.firstboot.domain.Shop;
- import com.xxx.firstboot.mapper.ShopMapper;
- @Repository
- public class ShopDao {
- @Autowired
- private ShopMapper mapper;
- /**
- * 获取shop
- */
- public Shop getShop(int id) {
- DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb2);
- return mapper.getShop(id);
- }
- }
注意:首先设置了数据源的key,然后调用mapper(在mapper中会首先根据该key从动态数据源中查询出相应的数据源,之后取出连接进行数据库操作)
ShopService:
- package com.xxx.firstboot.service;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import com.xxx.firstboot.dao.ShopDao;
- import com.xxx.firstboot.domain.Shop;
- @Service
- public class ShopService {
- @Autowired
- private ShopDao dao;
- public Shop getShop(int id) {
- return dao.getShop(id);
- }
- }
ShopController:
- package com.xxx.firstboot.web;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.RestController;
- import com.xxx.firstboot.domain.Shop;
- import com.xxx.firstboot.service.ShopService;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- @RestController
- @RequestMapping("/shop")
- @Api("shopController相关api")
- public class ShopController {
- @Autowired
- private ShopService service;
- @ApiOperation("获取shop信息,测试多数据源")
- @RequestMapping(value = "/getShop", method = RequestMethod.GET)
- public Shop getShop(@RequestParam("id") int id) {
- return service.getShop(id);
- }
- }
补:其实DatabaseContextHolder和DynamicDataSource完全可以合为一个类
来源: http://www.phperz.com/article/17/1130/358161.html