做项目,有时候需要一个工程里配置多个数据源。网上也有很多啦。我这里写一个比较全,实现相对优雅的方式吧。
使用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
到这里,基本配置已经完成了。使用非常简单,直接在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);
}
}
完。