mybatis如何配置使用多个数据源(environment)?

转:http://zhangbo-peipei-163-com.iteye.com/blog/2052924

mybatis如何配置使用多个数据源?

一、数据库连接properties配置文件,两个数据源的地址:

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);

=============以下非转摘,个人经验================

可改进的地方

1、MapperProxy中针对每一次的调用都进行close,比较浪费,可以考虑threadlocal来设置
2、对于复杂的事务可以采用如下方法进行控制:

SqlSession sqlSession = MapperFactory.xxxSqlSessionFactory().openSession(false);
//根据业务需要来获得对应的session,手动控制事务
try{
    //业务操作1
    //业务操作2
    sqlSession.commit();
}catch(Exception ex){
    sqlSession.rollback();
    isSucess = false;
}finally{
    if (sqlSession != null) {
        sqlSession.close();
    }
}

3、每个db都在配置文件中,如果涉及到分库和分分表,建议在负载均衡类中启动,从数据库汇总获得对应的分库策略,然后将SqlSessionFactory()设置完成

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页