eclipse 使用maven 搭建 springboot+mybatis + 多数据源

本文转载至: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

#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 权限,截图如下:



































  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值