java mybatis 多数据源,动态数据源 使用

做项目,有时候需要一个工程里配置多个数据源。网上也有很多啦。我这里写一个比较全,实现相对优雅的方式吧。

使用mybatis的时候,可以不用多个DAO。一个DAO全部搞定

直接上代码:

1、先添加需要的数据源配置

# 数据源,默认配置Druid
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driverClassName=com.mysql.jdbc.Driver

#多数据源配置
test.datasource.url=jdbc:mysql://192.168.1.2:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true
test.datasource.username=root
test.datasource.password=123456

#数据库2
test2.datasource.url=jdbc:mysql://192.168.1.3:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true
test2.datasource.username=root
test2.datasource.password=123456

2、添加一个数据库枚举

/**
 * 列出所有的数据源key(常用数据库名称来命名),可以改为你自己的数据源名称 
 * 注意:
 * 1)这里数据源与数据库是一对一的 
 * 2)DatabaseType中的变量名称是数据库的名称
 */
public enum DatabaseType {
    TEST_DB("TEST_DB"), TEST_DB2("TEST_DB2");

    private String code;

    private DatabaseType(String code)
    {
        this.code = code;
    }

    public String getCode()
    {
        return code;
    }

    public void setCode(String code)
    {
        this.code = code;
    }
}

3、添加数据库标识类,不用改,直接用

/**
 * @Title: DataSourceContextHolder
 * @Description: 关键的一步,一个标识,在设置动态数据源的时候,连接了两个库,但是在怎 么确定每次连接都是需要连接的数据库呢,那就要这个标识了。
 * 
 * @author gogym
 * @date 下午12:21:31
 */
public class DataSourceContextHolder
{
    private static final ThreadLocal contextHolder = new ThreadLocal();

    public static void setDbType(DatabaseType dbType)
    {
        contextHolder.set(dbType);
    }

    public static DatabaseType getDbType()
    {
        return contextHolder.get();
    }

    public static void clearDbType()
    {
        contextHolder.remove();
    }
}

4、添加动态数据源。不用改,直接用

/**
 * 
 * 动态数据源
 * @author gogym
 * @version 2018年4月27日
 * @see DynamicDataSource
 * @since
 */
public class DynamicDataSource extends AbstractRoutingDataSource
{
    @Override
    protected Object determineCurrentLookupKey()
    {
        return DataSourceContextHolder.getDbType();
    }
}

5、添加一个自定义注解,用来指定数据源,不用改,可以直接套用

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 
 * 自定义注解,用来指定数据源
 * @author gogym
 * @version 2018年4月27日
 * @see DS
 * @since
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DS {
    DatabaseType value();
}

6、添加一个AOP,用来解析注解。直接套用,不用改

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

/**
 * 
 * 自定义AOP切面,用来解析注解
 * @author gogym
 * @version 2018年4月27日
 * @see DynamicDataSourceAspect
 * @since
 */
@Aspect
@Component
public class DynamicDataSourceAspect
{
    @Before("@annotation(DS)")
    public void beforeSwitchDS(JoinPoint point)
    {

        // 获得当前访问的class
        Class className = point.getTarget().getClass();

        // 获得访问的方法名
        String methodName = point.getSignature().getName();
        // 得到方法的参数的类型
        Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
        DatabaseType dataSource = DataSourceContextHolder.getDbType();
        try
        {
            // 得到访问的方法对象
            Method method = className.getMethod(methodName, argClass);

            // 判断是否存在@DS注解
            if (method.isAnnotationPresent(DS.class))
            {
                DS annotation = method.getAnnotation(DS.class);
                // 取出注解中的数据源名
                dataSource = annotation.value();
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        // 切换数据源
        DataSourceContextHolder.setDbType(dataSource);

    }

    @After("@annotation(DS)")
    public void afterSwitchDS(JoinPoint point)
    {

        DataSourceContextHolder.clearDbType();

    }
}

7、添加一个数据库DAO,操作数据库的方法基本满足。不需要修改

/**
 * 数据库操作DAO 写这个是为了使用myBatis时可以少写一些mapper映射类
 * 
 * @author gogym
 * @version 2017年8月30日
 * @see CommonDao
 * @since
 */
public interface CommonDao
{

    /**
     * 保存对象
     * 
     * @param str
     * @param obj
     * @return
     * @throws Exception
     */
    public Object save(String str, Object obj)
        throws Exception;

    /**
     * 修改对象
     * 
     * @param str
     * @param obj
     * @return
     * @throws Exception
     */
    public Object update(String str, Object obj)
        throws Exception;

    /**
     * 删除对象
     * 
     * @param str
     * @param obj
     * @return
     * @throws Exception
     */
    public Object delete(String str, Object obj)
        throws Exception;

    /**
     * 查找对象
     * 
     * @param str
     * @param obj
     * @return
     * @throws Exception
     */
    public Object findForObject(String str, Object obj)
        throws Exception;

    /**
     * 查找对象返回List
     * 
     * @param str
     * @param obj
     * @return
     * @throws Exception
     */
    public Object findForList(String str, Object obj)
        throws Exception;

    /**
     * 查找对象封装成Map
     * 
     * @param s
     * @param obj
     * @return
     * @throws Exception
     */
    public Object findForMap(String sql, Object obj, String key, String value)
        throws Exception;

}

8、添加DAO实现类。不需要修改

import org.mybatis.spring.support.SqlSessionDaoSupport;

public class CommonDaoImpl extends SqlSessionDaoSupport implements CommonDao
{

    @Override
    public Object save(String str, Object obj)
        throws Exception
    {

        return getSqlSession().insert(str, obj);
    }

    @Override
    public Object update(String str, Object obj)
        throws Exception
    {
        return getSqlSession().update(str, obj);
    }

    @Override
    public Object delete(String str, Object obj)
        throws Exception
    {
        return getSqlSession().delete(str, obj);
    }

    @Override
    public Object findForObject(String str, Object obj)
        throws Exception
    {
        return getSqlSession().selectOne(str, obj);
    }

    @Override
    public Object findForList(String str, Object obj)
        throws Exception
    {
        return getSqlSession().selectList(str, obj);
    }

    @Override
    public Object findForMap(String sql, Object obj, String key, String value)
        throws Exception
    {
        return getSqlSession().selectMap(sql, key, value);
    }

}

9、添加工程配置,根据你自己的需要修改

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.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.ClassPathResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.poly.rbl.dao.CommonDao;
import com.poly.rbl.dao.impl.CommonDaoImpl;
import com.poly.rbl.datasource.DatabaseType;
import com.poly.rbl.datasource.DynamicDataSource;


@Configuration
public class ApplicationConfig extends WebMvcConfigurerAdapter
{

    // mapper文件路径,你自己存放mapper文件的路径
    static final String MAPPER_LOCATION = "classpath:mapper/**/*.xml";

    // mybatis 配置文件路径
    static final String MYBATIS_CONFIG = "xml/myBatisSetting.xml";

    @Autowired
    private Environment env;

    /**
     * 创建2个数据源(数据源的名称:方法名可以取为XXXDataSource(),XXX为数据库名称,该名称也就是数据源的名称)
     */

    private DataSource testDBdataSource()
        throws Exception
    {

        Properties props = new Properties();
        props.put("driverClassName", env.getProperty("spring.datasource.driverClassName"));
        props.put("url", env.getProperty("test.datasource.url"));
        props.put("username", env.getProperty("test.datasource.username"));
        props.put("password", env.getProperty("test.datasource.password"));
        return DruidDataSourceFactory.createDataSource(props);
    }

    private DataSource testDBdataSource2()
        throws Exception
    {

        Properties props = new Properties();
        props.put("driverClassName", env.getProperty("spring.datasource.driverClassName"));
        props.put("url", env.getProperty("test2.datasource.url"));
        props.put("username", env.getProperty("test2.datasource.username"));
        props.put("password", env.getProperty("test2.datasource.password"));
        return DruidDataSourceFactory.createDataSource(props);
    }

    /**
     * @throws Exception
     * @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
     * @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
     */
    @Bean("dynamicDataSource")
    @Primary
    public DynamicDataSource dynamicDataSource()
        throws Exception
    {

        DataSource testDBdataSource = testDBdataSource();
        DataSource testDBdataSource2 = testDBdataSource2();

        // 可以添加多个数据源,如果需要的话
        Map targetDataSources = new HashMap<>();
        targetDataSources.put(DatabaseType.TEST_DB, testDBdataSource);
        targetDataSources.put(DatabaseType.TEST_DB2, testDBdataSource2);

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
        dynamicDataSource.setDefaultTargetDataSource(testDBdataSource);// 默认的datasource设置为myTestDbDataSource
        return dynamicDataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource)
        throws Exception
    {

        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();

        /** 设置mybatis configuration 扫描路径 */
        sessionFactory.setConfigLocation(new ClassPathResource(MYBATIS_CONFIG));
        /** 设置datasource */
        sessionFactory.setDataSource(dynamicDataSource);
        /** 添加mapper 扫描路径 */
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(ApplicationConfig.MAPPER_LOCATION));

        /** 设置typeAlias 包扫描路径 */
        // sessionFactory.setTypeAliasesPackage("com....");

        return sessionFactory.getObject();
    }

    /**
     * 配置事务管理器
     */
    @Bean
    public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource)
        throws Exception
    {
        return new DataSourceTransactionManager(dynamicDataSource);
    }

    /**
     * 这里注入一个dao。目的是为了不想写太多的mapper类,只需要一个dao就能执行所有的mybatis sql
     * 
     * @param sqlSessionFactory
     * @return
     * @throws Exception
     */
    @Bean("commonDao")
    // @Lazy// 注意,这里可能要添加延迟加载注解,否则spring不知先加载哪一个,很可能造成循环依赖
    public CommonDao commonDao(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory)
        throws Exception
    {
        CommonDaoImpl cd = new CommonDaoImpl();
        cd.setSqlSessionFactory(sqlSessionFactory);
        return cd;
    }

}

 

到这里,基本配置已经完成了。使用非常简单,直接在service方法里面添加一个注解即可

 

@Service
public class OrderDeliveryServiceImpl implements OrderDeliveryService
{

    @Autowired
    private CommonDao commonDao;

    @Override
    @DS(DatabaseType.TEST_DB2) //指定使用test2数据源,不指定默认使用test
    public OrderDelivery findByOrderNo(String orderNo)
    {

        try
        {
            return (OrderDelivery)commonDao.findForObject(
                MyBatisUtils.getSqlKey(OrderDelivery.class, "selectByOrderNo"), orderNo);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

}
/**
 * 映射mapper文件
 * @author gogym
 * @version 1.0
 */
public class MyBatisUtils
{

    private static String getPackageName(Class clzss)
    {
        return clzss.getPackage().getName();
    }

    public static String getSqlKey(Class clzss, String shortKey)
    {
        return getPackageName(clzss).concat(".").concat(clzss.getSimpleName()).concat(".").concat(
            shortKey);
    }
}


完。

 

 

 

 

 

你可能感兴趣的:(JAVA,spring,cloud)