博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring3 整合Hibernate3.5 动态切换SessionFactory (切换数据库方言)
阅读量:5944 次
发布时间:2019-06-19

本文共 12967 字,大约阅读时间需要 43 分钟。

一、缘由

上一篇文章介绍到了怎么样在Sping、MyBatis、Hibernate整合的应用中动态切换DataSource数据源的方法,但最终遗留下一个问题:不能切换数据库方言。数据库方言可能在当前应用的架构中意义不是很大,但是如果单纯用MyBatis或Hibernate做数据库持久化操作,还是要处理这一问题。

 

那么下面将介绍怎么样动态切换SessionFactory,为什么要切换SessionFactory?

因为这里切换SessionFactory就可以实现多数据源和多个SessionFactory,每个SessionFactory有自己独立的数据库配置和SessionFactory的相关配置。我们的数据库方言就配置在SessionFactory这里,所以实现了切换SessionFactory也就实现了切换数据库方言的问题。这个主要是针对Hibernate来操作的,而MyBatis则需要动态切换SqlSessionFactory才行。

 

二、实现代码

1、定义全局切换SessionFactory的工具

package com.hoo.framework.spring.support;
 
/**
* function: 多数据源
* @author hoojo
* @createDate 2013-9-27 上午11:36:57
* @file CustomerContextHolder.java
* @package com.hoo.framework.spring.support
* @project SHMB
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
public abstract class CustomerContextHolder {
 
public final static String SESSION_FACTORY_MYSQL = "mysql";
public final static String SESSION_FACTORY_ORACLE = "oracle";
 
private static final ThreadLocal
contextHolder = new ThreadLocal
();
 
public static void setCustomerType(String customerType) {
contextHolder.set(customerType);
}
 
public static String getCustomerType() {
return contextHolder.get();
}
 
public static void clearCustomerType() {
contextHolder.remove();
}
}

同样上面的静态变量和前一文章中介绍的一致,它需要和下面配置文件中的SessionFactory的key对应。

2、实现自己的SessionFactory

定义好接口

package com.hoo.framework.spring.support.core;
 
import org.hibernate.SessionFactory;
 
/**
* function: 动态SessionFactory接口
* @author hoojo
* @createDate 2013-10-12 下午03:29:52
* @file DynamicSessionFactory.java
* @package com.hoo.framework.spring.support.core
* @project SHMB
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
public interface DynamicSessionFactory extends SessionFactory {
 
public SessionFactory getHibernateSessionFactory();
}

 

实现接口

package com.hoo.framework.spring.support.core;
 
import java.io.Serializable;
import java.sql.Connection;
import java.util.Map;
import java.util.Set;
import javax.naming.NamingException;
import javax.naming.Reference;
import org.hibernate.Cache;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.TypeHelper;
import org.hibernate.classic.Session;
import org.hibernate.engine.FilterDefinition;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.stat.Statistics;
import com.hoo.framework.spring.support.CustomerContextHolder;
 
/**
* function: 动态数据源实现
* @author hoojo
* @createDate 2013-10-12 下午03:31:31
* @file DynamicSessionFactoryImpl.java
* @package com.hoo.framework.spring.support.core
* @project SHMB
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
@SuppressWarnings({ "unchecked", "deprecation" })
public class DynamicSessionFactoryImpl implements DynamicSessionFactory {
 
private static final long serialVersionUID = 5384069312247414885L;
 
private Map
targetSessionFactorys;
private SessionFactory defaultTargetSessionFactory;
 
/**
* @see com.hoo.framework.spring.support.core.DynamicSessionFactory#getHibernateSessionFactory()
* function: 重写这个方法,这里最关键
* @author hoojo
* @createDate 2013-10-18 上午10:45:25
*/
@Override
public SessionFactory getHibernateSessionFactory() {
SessionFactory targetSessionFactory = targetSessionFactorys.get(CustomerContextHolder.getCustomerType());
if (targetSessionFactory != null) {
return targetSessionFactory;
} else if (defaultTargetSessionFactory != null) {
return defaultTargetSessionFactory;
}
return null;
}
 
 
@Override
public void close() throws HibernateException {
this.getHibernateSessionFactory().close();
}
 
@Override
public boolean containsFetchProfileDefinition(String s) {
return this.getHibernateSessionFactory().containsFetchProfileDefinition(s);
}
 
@Override
public void evict(Class clazz) throws HibernateException {
this.getHibernateSessionFactory().evict(clazz);
}
 
@Override
public void evict(Class clazz, Serializable serializable) throws HibernateException {
this.getHibernateSessionFactory().evict(clazz, serializable);
}
 
@Override
public void evictCollection(String s) throws HibernateException {
this.getHibernateSessionFactory().evictCollection(s);
}
 
@Override
public void evictCollection(String s, Serializable serializable) throws HibernateException {
this.getHibernateSessionFactory().evictCollection(s, serializable);
}
 
@Override
public void evictEntity(String entity) throws HibernateException {
this.getHibernateSessionFactory().evictEntity(entity);
}
 
@Override
public void evictEntity(String entity, Serializable serializable) throws HibernateException {
this.getHibernateSessionFactory().evictEntity(entity, serializable);
}
 
@Override
public void evictQueries() throws HibernateException {
this.getHibernateSessionFactory().evictQueries();
}
 
@Override
public void evictQueries(String queries) throws HibernateException {
this.getHibernateSessionFactory().evictQueries(queries);
}
 
@Override
public Map
getAllClassMetadata() {
return this.getHibernateSessionFactory().getAllClassMetadata();
}
 
@Override
public Map getAllCollectionMetadata() {
return this.getHibernateSessionFactory().getAllClassMetadata();
}
 
@Override
public Cache getCache() {
return this.getHibernateSessionFactory().getCache();
}
 
@Override
public ClassMetadata getClassMetadata(Class clazz) {
return this.getHibernateSessionFactory().getClassMetadata(clazz);
}
 
@Override
public ClassMetadata getClassMetadata(String classMetadata) {
return this.getHibernateSessionFactory().getClassMetadata(classMetadata);
}
 
@Override
public CollectionMetadata getCollectionMetadata(String collectionMetadata) {
return this.getHibernateSessionFactory().getCollectionMetadata(collectionMetadata);
}
 
@Override
public Session getCurrentSession() throws HibernateException {
return this.getHibernateSessionFactory().getCurrentSession();
}
 
@Override
public Set getDefinedFilterNames() {
return this.getHibernateSessionFactory().getDefinedFilterNames();
}
 
@Override
public FilterDefinition getFilterDefinition(String definition) throws HibernateException {
return this.getHibernateSessionFactory().getFilterDefinition(definition);
}
 
@Override
public Statistics getStatistics() {
return this.getHibernateSessionFactory().getStatistics();
}
 
@Override
public TypeHelper getTypeHelper() {
return this.getHibernateSessionFactory().getTypeHelper();
}
 
@Override
public boolean isClosed() {
return this.getHibernateSessionFactory().isClosed();
}
 
@Override
public Session openSession() throws HibernateException {
return this.getHibernateSessionFactory().openSession();
}
 
@Override
public Session openSession(Interceptor interceptor) throws HibernateException {
return this.getHibernateSessionFactory().openSession(interceptor);
}
 
@Override
public Session openSession(Connection connection) {
return this.getHibernateSessionFactory().openSession(connection);
}
 
@Override
public Session openSession(Connection connection, Interceptor interceptor) {
return this.getHibernateSessionFactory().openSession(connection, interceptor);
}
 
@Override
public StatelessSession openStatelessSession() {
return this.getHibernateSessionFactory().openStatelessSession();
}
 
@Override
public StatelessSession openStatelessSession(Connection connection) {
return this.getHibernateSessionFactory().openStatelessSession(connection);
}
 
@Override
public Reference getReference() throws NamingException {
return this.getHibernateSessionFactory().getReference();
}
 
public void setTargetSessionFactorys(Map
targetSessionFactorys) {
this.targetSessionFactorys = targetSessionFactorys;
}
 
public void setDefaultTargetSessionFactory(SessionFactory defaultTargetSessionFactory) {
this.defaultTargetSessionFactory = defaultTargetSessionFactory;
}
 
}

上面最重要的就是getHibernateSessionFactory重写这个方法,其他方法和原来实现的无异。重写这个方法后利用CustomerContextHolder动态设置SessionFactory类型就可以动态的切换SessionFactory。

3、动态的事务管理器,因为我们这里是动态切换SessionFactory,所以事务这块也需要动态切换SessionFactory来完成事务的操作。

package com.hoo.framework.spring.support.tx;
 
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import com.hoo.framework.spring.support.core.DynamicSessionFactory;
 
/**
* function: 重写HibernateTransactionManager事务管理器,实现自己的动态的事务管理器
* @author hoojo
* @createDate 2013-10-12 下午03:54:02
* @file DynamicTransactionManager.java
* @package com.hoo.framework.spring.support.tx
* @project SHMB
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
public class DynamicTransactionManager extends HibernateTransactionManager {
 
private static final long serialVersionUID = -4655721479296819154L;
 
/**
* @see org.springframework.orm.hibernate4.HibernateTransactionManager#getDataSource()
* function: 重写
* @author hoojo
* @createDate 2013-10-12 下午03:55:24
*/
@Override
public DataSource getDataSource() {
return SessionFactoryUtils.getDataSource(getSessionFactory());
}
 
/**
* @see org.springframework.orm.hibernate4.HibernateTransactionManager#getSessionFactory()
* function: 重写
* @author hoojo
* @createDate 2013-10-12 下午03:55:24
*/
@Override
public SessionFactory getSessionFactory() {
DynamicSessionFactory dynamicSessionFactory = (DynamicSessionFactory) super.getSessionFactory();
SessionFactory hibernateSessionFactory = dynamicSessionFactory.getHibernateSessionFactory();
return hibernateSessionFactory;
}
}

这里主要重写getDataSource()/getSessionFactory()这两个方法,getSessionFactory方法是利用我们上面定义的接口来动态获取我们在上下文(CustomerContextHolder)中定义切换的SessionFactory对象。而getDataSource则是获得动态SessionFactory的DataSource,这里也不难理解。

4、至此,重写的接口和实现都完成,下面开始配置相关的代码

 
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
 
 
 
 
 
 
 
com.hoo.common.entity.IDGenerator
 
 
org.hibernate.dialect.MySQLDialect
after_transaction
true
true
 
 
 
 
org.hibernate.dialect.OracleDialect
after_transaction
true
true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

配置也和我们之前的配置差不多,就是事务管理器部分注入的SessionFactory是我们自己定义的。

5、简单测试

@Test
public void testAdd() {
// 主要就是这行代码 它才是完成SessionFactory的切换
CustomerContextHolder.setCustomerType(CustomerContextHolder.SESSION_FACTORY_MYSQL);
 
DeviceInfo entity = new DeviceInfo();
entity.setSbbh(System.currentTimeMillis() + "");
entity.setIpdz("my ip address2");
entity.setJd(1234);
try {
service.add(entity);
} catch (Exception e) {
e.printStackTrace();
}
}

经过测试发现可以查询数据,利用hibernate查询分页也可以生成对应数据库的分页语句。同样在服务层的方法中添加完操作后,特意抛出一个异常,数据也能正常回滚操作,所以DynamicTransactionManager也是起到了该有的作用!

这里我的测试用例是手动切换CustomerContextHolder.setCustomerType的,但实际开发中我们还是得用Spring的Aop中的Interceptor进行切面编程,完成动态切换SessionFactory。上一篇文章已经提到了(读者可以参考该篇博文中的第二节的3、7小节DataSourceMethodInterceptor MultipleDataSourceInterceptor),这里就不再赘述!

 

三、引发的问题

上述的实现如果在使用不当的情况下,在实际开发中很可能存在一些问题!

问题1是这样的,通常事务是在Service这层完成,这个应该是没有异议。倘若是这样的话,问题便出现了。而通常我们在MVC中的视图层中仅仅会调用Service中一个方法来完成所有当前业务的处理。如果这个Service中同时操作dbA、dbB两个数据库,事务提交的时候是用哪个数据库的事务呢?所以我们把不同数据库的操作放在一个方法,就会出现事务的问题的,除非两个数据库的事务能够同时回滚!

大致情景是:Service中的add4Oracle操作Oracle数据库,Service中的add4MySQL操作MySQL数据库,最后把Service中的add4Oracle、add4MySQL方法放到一个operation方法中,MVC视图层的业务控制调用Service中的operation。像这样的情况,如果add4Oracle或add4MySQL方法中的某一个方法出现异常,就需要把两个数据库事务都回滚。

解决办法就是在Service或Dao中的一个方法中同时操作两个数据库,手动完成事务控制。出现异常就全部回滚,没有异常就全部提交,只能这样牺牲下。

问题2是利用拦截器不能同时切换一个方法操作两个数据库的可能,例如一个service中的query方法即需要用query MySQL,也需要query Oracle。那么解决办法就是在query方法中调用当前service的query4Oracle和query4Oracle,那样绕过去还是能利用拦截器进行动态切换的。

四、总结

要完成数据库方言的切换,我们就需要配置多个SessionFactory利用自己实现的DynamicSessionFactory和CustomerContextHolder完成SessionFactory的切换。利用DynamicTransactionManager完成当前Session的事务操作。通过对这些对象的操作和配置,最终可以完成SessionFactory的动态切换。实际使用中虽然有些问题出现,但是最终还是有解决方案,尽管有些不完美。所谓的完美的东西总有它不完美的地方,这样才算完美,比如断臂的维纳斯女神~!

本文转自hoojo博客园博客,原文链接:http://www.cnblogs.com/hoojo/p/dynamic_switch_sessionfactory_muliteSessionFactory.html,如需转载请自行联系原作者
你可能感兴趣的文章
微信公众平台图文教程(二) 群发功能和素材管理
查看>>
Centos下基于Hadoop安装Spark(分布式)
查看>>
wdcp 安装
查看>>
asterisk配置
查看>>
GA操作步骤和技巧(二)——用户行为分析
查看>>
shell中while循环里使用ssh的注意事项
查看>>
SHELL获取计算机外网ip的几种写法
查看>>
博客正在搬迁中
查看>>
触发器与存储过程的区别
查看>>
我的友情链接
查看>>
centos搭建supervisor
查看>>
linux日志分割
查看>>
我的友情链接
查看>>
Spring学习资料之 依赖注入(一)
查看>>
安装win7提示安装程序无法创建新的系统分区和定位现有系统分区
查看>>
快递查询接口的调用与解析案例
查看>>
服务器性能优化配置建议
查看>>
oracle sql语句实现累加、累减、累乘、累除
查看>>
3D地图的定时高亮和点击事件(基于echarts)
查看>>
接口由40秒到200ms优化记录
查看>>