eclipse 使用maven 搭建 springboot+mybatis + 多数据源
2017-03-16 13:38
746 查看
本文转载至:http://www.cnblogs.com/java-zhao/p/5413845.html
在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源。
代码结构:
简要原理:
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的入参来构造DataSourceTran
sactionManager
7)使用的时候,在dao层或service层先使用DatabaseContextHolder设置将要使用的数据源key,然后再调用mapper层进行相应的操作,建议放在dao层去做(当然也可以使用spring aop+自定注解去做)
注意:在mapper层进行操作的时候,会先调用determineCurrentLookupKey()方法获取一个数据源(获取数据源:先根据设置去targetDataSources中去找,若没有,则选择defaultTargetDataSource),之后在进行数据库操作。
1、系统配置文件application.properties
2、DatabaseType
作用:列举数据源的key。
3、DatabaseContextHolder
作用:构建一个DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法
4、DynamicDataSource
作用:使用DatabaseContextHolder获取当前线程的DatabaseType
5、MyBatisConfig
作用:
通过读取application.properties文件生成两个数据源(readDataSource、writeDataSource)
使用以上生成的两个数据源构造动态数据源dataSource
@Primary:指定在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@Autowire注解报错(一般用于多数据源的情况下)
@Qualifier:指定名称的注入,当一个接口有多个实现类的时候使用(在本例中,有两个DataSource类型的实例,需要指定名称注入)
@Bean:生成的bean实例的名称是方法名(例如上边的@Qualifier注解中使用的名称是前边两个数据源的方法名,而这两个数据源也是使用@Bean注解进行注入的)
通过动态数据源构造SqlSessionFactory和事务管理器(如果不需要事务,后者可以去掉)
6、使用
UserMapper.java
UserDao.java
UserService.java
UserController.java
user.sql 和UserMapper.xml
模拟测试数据库切换
第一情况:用户存在master库 select 权限,截图如下:
第二种情况:用户存在master库update 权限,截图如下:
在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源。
代码结构:
简要原理:
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的入参来构造DataSourceTran
sactionManager
7)使用的时候,在dao层或service层先使用DatabaseContextHolder设置将要使用的数据源key,然后再调用mapper层进行相应的操作,建议放在dao层去做(当然也可以使用spring aop+自定注解去做)
注意:在mapper层进行操作的时候,会先调用determineCurrentLookupKey()方法获取一个数据源(获取数据源:先根据设置去targetDataSources中去找,若没有,则选择defaultTargetDataSource),之后在进行数据库操作。
1、系统配置文件application.properties
#the read datasource jdbc.driverClassName = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://127.0.0.1:3306/master?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8 jdbc.username = test jdbc.password = 123456 #the write datasource jdbc2.driverClassName = com.mysql.jdbc.Driver jdbc2.url = jdbc:mysql://127.0.0.1:3306/master?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8 jdbc2.username = root jdbc2.password = 123456 mybatis.typeAliasesPackage=com.zzg.myboot.domain mybatis.mapperLocations=classpath:mapper/*.xml
2、DatabaseType
package com.zzg.myboot.datasource; /** * 列出所有的数据源key(常用数据库名称来命名) * 注意: * 1)这里数据源与数据库是一对一的 * 2)DatabaseType中的变量名称就是数据库的名称 */ public enum DatabaseType { read,write }
作用:列举数据源的key。
3、DatabaseContextHolder
package com.zzg.myboot.datasource; /** * 作用: * 1、保存一个线程安全的DatabaseType容器 */ public class DatabaseContextHolder { private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<DatabaseType>(); public static void setDatabaseType(DatabaseType type){ contextHolder.set(type); } public static DatabaseType getDatabaseType(){ return contextHolder.get(); } }
作用:构建一个DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法
4、DynamicDataSource
package com.zzg.myboot.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.zzg.myboot.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.zzg.myboot.datasource.DatabaseType; import com.zzg.myboot.datasource.DynamicDataSource; /** * springboot集成mybatis的基本入口 1)创建数据源(如果采用的是默认的tomcat-jdbc数据源,则不需要) * 2)创建SqlSessionFactory 3)配置事务管理器,除非需要使用事务,否则不用配置 */ @Configuration // 该注解类似于spring配置文件 @MapperScan(basePackages = "com.zzg.myboot.mapper") public class MyBatisConfig { @Autowired private Environment env; /** * 创建数据源(数据源的名称:方法名可以取为XXXDataSource(),XXX为数据库名称,该名称也就是数据源的名称) */ @Bean public DataSource readDataSource() 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 writeDataSource() 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("readDataSource") DataSource readDataSource, @Qualifier("writeDataSource") DataSource writeDataSource) { Map<Object, Object> targetDataSources = new HashMap<Object, Object>(); targetDataSources.put(DatabaseType.read, readDataSource); targetDataSources.put(DatabaseType.write, writeDataSource); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法 dataSource.setDefaultTargetDataSource(readDataSource);// 默认的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); } }
作用:
通过读取application.properties文件生成两个数据源(readDataSource、writeDataSource)
使用以上生成的两个数据源构造动态数据源dataSource
@Primary:指定在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@Autowire注解报错(一般用于多数据源的情况下)
@Qualifier:指定名称的注入,当一个接口有多个实现类的时候使用(在本例中,有两个DataSource类型的实例,需要指定名称注入)
@Bean:生成的bean实例的名称是方法名(例如上边的@Qualifier注解中使用的名称是前边两个数据源的方法名,而这两个数据源也是使用@Bean注解进行注入的)
通过动态数据源构造SqlSessionFactory和事务管理器(如果不需要事务,后者可以去掉)
6、使用
UserMapper.java
package com.zzg.myboot.mapper; import com.zzg.myboot.domain.User; public interface UserMapper { int deleteByPrimaryKey(Long id); int insert(User record); int insertSelective(User record); User selectByPrimaryKey(Long id); int updateByPrimaryKeySelective(User record); int updateByPrimaryKey(User record); }
UserDao.java
package com.zzg.myboot.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import com.zzg.myboot.datasource.DatabaseContextHolder; import com.zzg.myboot.datasource.DatabaseType; import com.zzg.myboot.domain.User; import com.zzg.myboot.mapper.UserMapper; @Repository public class UserDao { @Autowired private UserMapper mapper; public int deleteByPrimaryKey(Long id){ DatabaseContextHolder.setDatabaseType(DatabaseType.write); return mapper.deleteByPrimaryKey(id); } public int insert(User record){ DatabaseContextHolder.setDatabaseType(DatabaseType.write); return mapper.insert(record); } public int insertSelective(User record){ DatabaseContextHolder.setDatabaseType(DatabaseType.write); return mapper.insertSelective(record); } public User selectByPrimaryKey(Long id){ DatabaseContextHolder.setDatabaseType(DatabaseType.read); return mapper.selectByPrimaryKey(id); } public int updateByPrimaryKeySelective(User record){ DatabaseContextHolder.setDatabaseType(DatabaseType.write); return mapper.updateByPrimaryKeySelective(record); } public int updateByPrimaryKey(User record){ DatabaseContextHolder.setDatabaseType(DatabaseType.write); return mapper.updateByPrimaryKey(record); } }
UserService.java
package com.zzg.myboot.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.zzg.myboot.dao.UserDao; import com.zzg.myboot.domain.User; @Service public class UserService { @Autowired private UserDao dao; public int deleteByPrimaryKey(Long id){ return dao.deleteByPrimaryKey(id); } public int insert(User record){ return dao.insert(record); } public int insertSelective(User record){ return dao.insertSelective(record); } public User selectByPrimaryKey(Long id){ return dao.selectByPrimaryKey(id); } public int updateByPrimaryKeySelective(User record){ return dao.updateByPrimaryKeySelective(record); } public int updateByPrimaryKey(User record){ return dao.updateByPrimaryKey(record); } }
UserController.java
package com.zzg.myboot.controller; 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.zzg.myboot.domain.User; import com.zzg.myboot.service.UserService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @RestController @RequestMapping("/user") @Api("UserController相关api") public class UserController { @Autowired private UserService service; @ApiOperation("获取user信息,测试多数据源") @RequestMapping(value = "/getUser", method = RequestMethod.GET) public User getShop(@RequestParam("id") String id) { return service.selectByPrimaryKey(Long.parseLong(id)); } }
user.sql 和UserMapper.xml
DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` bigint(19) NOT NULL auto_increment, `loginname` varchar(64) default NULL, `name` varchar(64) default NULL, `password` varchar(64) default NULL, `sex` tinyint(2) default '0', `age` tinyint(2) default '0', `usertype` tinyint(2) default '0', `status` tinyint(2) default '0', `organization_id` int(11) default '0', `createdate` datetime default NULL, `phone` varchar(20) default NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户';
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.zzg.myboot.mapper.UserMapper" > <resultMap id="BaseResultMap" type="com.zzg.myboot.domain.User" > <id column="id" property="id" jdbcType="BIGINT" /> <result column="loginname" property="loginname" jdbcType="VARCHAR" /> <result column="name" property="name" jdbcType="VARCHAR" /> <result column="password" property="password" jdbcType="VARCHAR" /> <result column="sex" property="sex" jdbcType="TINYINT" /> <result column="age" property="age" jdbcType="TINYINT" /> <result column="usertype" property="usertype" jdbcType="TINYINT" /> <result column="status" property="status" jdbcType="TINYINT" /> <result column="organization_id" property="organizationId" jdbcType="INTEGER" /> <result column="createdate" property="createdate" jdbcType="TIMESTAMP" /> <result column="phone" property="phone" jdbcType="VARCHAR" /> </resultMap> <sql id="Base_Column_List" > id, loginname, name, password, sex, age, usertype, status, organization_id, createdate, phone </sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" > select <include refid="Base_Column_List" /> from user where id = #{id,jdbcType=BIGINT} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" > delete from user where id = #{id,jdbcType=BIGINT} </delete> <insert id="insert" parameterType="com.zzg.myboot.domain.User" > insert into user (id, loginname, name, password, sex, age, usertype, status, organization_id, createdate, phone) values (#{id,jdbcType=BIGINT}, #{loginname,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{sex,jdbcType=TINYINT}, #{age,jdbcType=TINYINT}, #{usertype,jdbcType=TINYINT}, #{status,jdbcType=TINYINT}, #{organizationId,jdbcType=INTEGER}, #{createdate,jdbcType=TIMESTAMP}, #{phone,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" parameterType="com.zzg.myboot.domain.User" > insert into user <trim prefix="(" suffix=")" suffixOverrides="," > <if test="id != null" > id, </if> <if test="loginname != null" > loginname, </if> <if test="name != null" > name, </if> <if test="password != null" > password, </if> <if test="sex != null" > sex, </if> <if test="age != null" > age, </if> <if test="usertype != null" > usertype, </if> <if test="status != null" > status, </if> <if test="organizationId != null" > organization_id, </if> <if test="createdate != null" > createdate, </if> <if test="phone != null" > phone, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides="," > <if test="id != null" > #{id,jdbcType=BIGINT}, </if> <if test="loginname != null" > #{loginname,jdbcType=VARCHAR}, </if> <if test="name != null" > #{name,jdbcType=VARCHAR}, </if> <if test="password != null" > #{password,jdbcType=VARCHAR}, </if> <if test="sex != null" > #{sex,jdbcType=TINYINT}, </if> <if test="age != null" > #{age,jdbcType=TINYINT}, </if> <if test="usertype != null" > #{usertype,jdbcType=TINYINT}, </if> <if test="status != null" > #{status,jdbcType=TINYINT}, </if> <if test="organizationId != null" > #{organizationId,jdbcType=INTEGER}, </if> <if test="createdate != null" > #{createdate,jdbcType=TIMESTAMP}, </if> <if test="phone != null" > #{phone,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="com.zzg.myboot.domain.User" > update user <set > <if test="loginname != null" > loginname = #{loginname,jdbcType=VARCHAR}, </if> <if test="name != null" > name = #{name,jdbcType=VARCHAR}, </if> <if test="password != null" > password = #{password,jdbcType=VARCHAR}, </if> <if test="sex != null" > sex = #{sex,jdbcType=TINYINT}, </if> <if test="age != null" > age = #{age,jdbcType=TINYINT}, </if> <if test="usertype != null" > usertype = #{usertype,jdbcType=TINYINT}, </if> <if test="status != null" > status = #{status,jdbcType=TINYINT}, </if> <if test="organizationId != null" > organization_id = #{organizationId,jdbcType=INTEGER}, </if> <if test="createdate != null" > createdate = #{createdate,jdbcType=TIMESTAMP}, </if> <if test="phone != null" > phone = #{phone,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=BIGINT} </update> <update id="updateByPrimaryKey" parameterType="com.zzg.myboot.domain.User" > update user set loginname = #{loginname,jdbcType=VARCHAR}, name = #{name,jdbcType=VARCHAR}, password = #{password,jdbcType=VARCHAR}, sex = #{sex,jdbcType=TINYINT}, age = #{age,jdbcType=TINYINT}, usertype = #{usertype,jdbcType=TINYINT}, status = #{status,jdbcType=TINYINT}, organization_id = #{organizationId,jdbcType=INTEGER}, createdate = #{createdate,jdbcType=TIMESTAMP}, phone = #{phone,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} </update> </mapper>
模拟测试数据库切换
第一情况:用户存在master库 select 权限,截图如下:
第二种情况:用户存在master库update 权限,截图如下:
相关文章推荐
- eclipse 使用maven 构建 springboot+mybatis
- 使用Maven搭建ssm(spring+springmvc+mybatis)工程(多数据源)
- Eclipse使用maven搭建简单spring boot application Hello World
- spring boot整合mybatis使用c3p0数据源连接mysql
- 使用Maven搭建Spring+SpringMVC+Mybatis+ehcache项目
- 微服务框架(一)--Eclipse使用maven创建spring boot项目
- eclipse + maven 搭建springMVC+Spring+mybatis 系统
- Eclipse下用Maven搭建SpringMvc+MyBatis
- 使用intellij idea搭建MAVEN+SSM(Spring+SpringMVC+MyBatis)框架
- eclipse 使用maven 构建springboot 读取多个配置文件
- 利用maven/eclipse搭建ssm(spring+spring mvc+mybatis)
- eclipse 使用maven 集成springboot + retrofit
- Eclipse下使用Maven整合SSM(spring springmvc mybatis)
- spring boot+maven+mybatis+IDE+git项目搭建
- eclipse 使用maven 搭建spring + springMVC + spring Data + jpa 框架
- eclipse 使用maven 构建springboot 程序
- 使用intellij idea搭建MAVEN+SSM(Spring+SpringMVC+MyBatis)框架
- Eclipse+Maven+Spring-boot快速搭建
- 使用idea+springboot+Mybatis搭建web项目
- eclipse搭建maven web 工程,和集成springmvc+mybatis的一些问题