mybatis如何配置使用多个数据源?
一、数据库连接properties配置文件,两个数据源的地址:class="java" name="code">
hd.jdbc.driverClassName=com.mysql.jdbc.Driver
hd.jdbc.url=jdbc:mysql://127.0.0.1::3306/hd?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
hd.jdbc.username=root
hd.jdbc.password=root
ho.jdbc.driverClassName=com.mysql.jdbc.Driver
ho.jdbc.url=jdbc:mysql://127.0.0.1:3306/ho?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
ho.jdbc.username=root
ho.jdbc.password=root
二、mybatis配置文件,配置两个environment:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="mybatis/jdbc.properties"/>
<environments default="HO">
<environment id="HD">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${hd.jdbc.driverClassName}" />
<property name="url" value="${hd.jdbc.url}" />
<property name="username" value="${hd.jdbc.username}" />
<property name="password" value="${hd.jdbc.password}" />
</dataSource>
</environment>
<environment id="HO">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${ho.jdbc.driverClassName}" />
<property name="url" value="${ho.jdbc.url}" />
<property name="username" value="${ho.jdbc.username}" />
<property name="password" value="${ho.jdbc.password}" />
</dataSource>
</environment>
</environments>
</configuration>
三、获取SqlSessionFactory,获取Mapper代理,便于在执行完Mapper方法后关闭SqlSession。
SqlSessionFactory的获取:
/**
* 根据mybatis.xml中配置的不同的environment创建对应的SqlSessionFactory
* @author boyce
* @version 2014-3-27
*/
public final class DataSourceSqlSessionFactory {
private static final String CONFIGURATION_PATH = "mybatis/mybatis.xml";
private static final Map<DataSourceEnvironment, SqlSessionFactory> SQLSESSIONFACTORYS
= new HashMap<DataSourceEnvironment, SqlSessionFactory>();
/**
* 根据指定的DataSourceEnvironment获取对应的SqlSessionFactory
* @param environment 数据源environment
* @return SqlSessionFactory
*/
public static SqlSessionFactory getSqlSessionFactory(DataSourceEnvironment environment) {
SqlSessionFactory sqlSessionFactory = SQLSESSIONFACTORYS.get(environment);
if (ObjectUtils.isNotNull(sqlSessionFactory))
return sqlSessionFactory;
else {
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(CONFIGURATION_PATH);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, environment.name());
logger.info("Get {} SqlSessionFactory successfully.", environment.name());
} catch (IOException e) {
logger.warn("Get {} SqlSessionFactory error.", environment.name());
logger.error(e.getMessage(), e);
}
finally {
IOUtils.closeQuietly(inputStream);
}
SQLSESSIONFACTORYS.put(environment, sqlSessionFactory);
return sqlSessionFactory;
}
}
/**
* 配置到Configuration.xml文件中的数据源的environment的枚举描述
* @author boyce
* @version 2014-3-27
*/
public static enum DataSourceEnvironment {
HD,
HO;
}
}
定义一个统一的Mapper标识
接口,每一个具体的Mapper接口继承该接口:
/**
* Mapper Interface
* @author boyce
* @version 2014-3-28
*/
public interface Mapper {
}
定义一个Mapper代理工厂:
/**
* Mapper Factory
* @author boyce
* @version 2014-3-28
*/
public final class MapperFactory {
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MapperFactory.class);
/**
* Create a mapper of environment by Mapper class
* @param clazz Mapper class
* @param environment A datasource environment
* @return a Mapper instance
*/
@SuppressWarnings("unchecked")
public static <T> T createMapper(Class<? extends Mapper> clazz, DataSourceEnvironment environment) {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(environment);
SqlSession sqlSession = sqlSessionFactory.openSession();
Mapper mapper = sqlSession.getMapper(clazz);
return (T)MapperProxy.bind(mapper, sqlSession);
}
/**
* Mapper Proxy
* executing mapper method and close sqlsession
* @author boyce
* @version 2014-4-9
*/
private static class MapperProxy implements InvocationHandler {
private Mapper mapper;
private SqlSession sqlSession;
private MapperProxy(Mapper mapper, SqlSession sqlSession) {
this.mapper = mapper;
this.sqlSession = sqlSession;
}
@SuppressWarnings("unchecked")
private static Mapper bind(Mapper mapper, SqlSession sqlSession) {
return (Mapper) Proxy.newProxyInstance(mapper.getClass().getClassLoader(),
mapper.getClass().getInterfaces(), new MapperProxy(mapper, sqlSession));
}
/**
* execute mapper method and finally close sqlSession
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = null;
try {
object = method.invoke(mapper, args);
} catch(Exception e) {
e.printStackTrace();
logger.error(e.getMessage(), e);
} finally {
sqlSession.close();
}
return object;
}
}
//Get a SqlSessionFactory of environment
private static SqlSessionFactory getSqlSessionFactory(DataSourceEnvironment environment) {
return DataSourceSqlSessionFactory.getSqlSessionFactory(environment);
}
大功告成,客户端使用:
UserMapper mapper = MapperFactory.createMapper(UserMapper.class, DataSourceEnvironment.HD);
User user = mapper.getUserById(162L);
System.out.println(user);
OK,到此基本上所有的工作就完成了,以上的方式就可以支持多个数据源了。
但是代码还不够优雅,以上代码我们
发现DataSourceEnvironment这个枚举变量在客户端,MapperFactory以及DataSourceEnvironment
中传来传去,我们应该尽量减少一个类对外界类的耦合,也就是不符合Java编程原则中的迪米特法则。
好了,那我们来改良一下。
将MapperFactory设计成枚举策略模式:
/**
* Mapper Creator
* @author boyce
* @version 2014-3-28
*/
public enum MapperFactory {
HD {
private SqlSessionFactory sqlSessionFactory;
@Override
public <T> T createMapper(Class<? extends Mapper> clazz) {
return createMapper(clazz, this);
}
@Override
protected void createSqlSessionFactory() {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, this.name());
}
@Override
public SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
},
HO {
private SqlSessionFactory sqlSessionFactory;
@Override
public <T> T createMapper(Class<? extends Mapper> clazz) {
return createMapper(clazz, this);
}
@Override
protected void createSqlSessionFactory() {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, this.name());
}
@Override
public SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
};
/**
* Create a mapper of environment by Mapper class
* @param clazz Mapper class
* @param environment A datasource environment
* @return a Mapper instance
*/
public abstract <T> T createMapper(Class<? extends Mapper> clazz);
/**
* Create SqlSessionFactory of environment
*/
protected abstract void createSqlSessionFactory();
/**
* get SqlSessionFactory
*/
public abstract SqlSessionFactory getSqlSessionFactory();
private static InputStream inputStream = null;
static {
try {
inputStream = Resources.getResourceAsStream("mybatis/mybatis.xml");
HO.createSqlSessionFactory();
HD.createSqlSessionFactory();
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(inputStream);
}
}
@SuppressWarnings("unchecked")
private static <T> T createMapper(Class<? extends Mapper> clazz, MapperFactory MapperFactory) {
SqlSession sqlSession = MapperFactory.getSqlSessionFactory().openSession();
Mapper mapper = sqlSession.getMapper(clazz);
return (T)MapperProxy.bind(mapper, sqlSession);
}
/**
* Mapper Proxy
* executing mapper method and close sqlsession
* @author boyce
* @version 2014-4-9
*/
private static class MapperProxy implements InvocationHandler {
private Mapper mapper;
private SqlSession sqlSession;
private MapperProxy(Mapper mapper, SqlSession sqlSession) {
this.mapper = mapper;
this.sqlSession = sqlSession;
}
private static Mapper bind(Mapper mapper, SqlSession sqlSession) {
return (Mapper) Proxy.newProxyInstance(mapper.getClass().getClassLoader(),
mapper.getClass().getInterfaces(), new MapperProxy(mapper, sqlSession));
}
/**
* execute mapper method and finally close sqlSession
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = null;
try {
object = method.invoke(mapper, args);
} catch(Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
return object;
}
}
}
客户端使用场景:
UserMapper mapper = MapperFactory.HO.createMapper(UserMapper.class);
User user = mapper.getUserById(162L);
System.out.println(user);
ok,如果大家有什么更优雅的设计方案请不吝分享分享。