1.概述

其实最简单的办法就是使用原生sql,如 session.createSQLQuery("sql"),或者使用jdbcTemplate。但是项目中已经使用了hql的方式查询,修改起来又累,风险又大!所以,必须找到一种比较好的解决方案,实在不行再改写吧!经过3天的时间的研究,终于找到一种不错的方法,下面讲述之。
2.步骤
2.1 新建hibernate interceptor类
/**
* Created by hdwang on 2017/8/7.
*
* hibernate拦截器:表名替换
*/
public class AutoTableNameInterceptor extends EmptyInterceptor {
private String srcName = StringUtils.EMPTY; //源表名
private String destName = StringUtils.EMPTY; // 目标表名
public AutoTableNameInterceptor() {}
public AutoTableNameInterceptor(String srcName,String destName){
this.srcName = srcName;
this.destName = destName;
}
@Override
public String onPrepareStatement(String sql) {
if(srcName.equals(StringUtils.EMPTY) || destName.equals(StringUtils.EMPTY)){
return sql;
}
sql = sql.replaceAll(srcName, destName);
return sql;
}
}
这个interceptor会拦截所有数据库操作,在发送sql语句之前,替换掉其中的表名。
2.2 配置到sessionFactory去
先看一下sessionFactory是个啥东西。
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" >
<property name="dataSource" ref="defaultDataSource"></property>
<property name="packagesToScan">
<list>
<value>com.my.pay.task.entity</value>
<value>com.my.pay.paycms.entity</value>
<value>com.my.pay.data.entity.payincome</value>
</list>
</property>
<property name="mappingLocations">
<list>
<value>classpath*:/hibernate/hibernate-sql.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.hbm2ddl.auto">none</prop>
<!-- 开启查询缓存 -->
<prop key="hibernate.cache.use_query_cache">false</prop>
<!-- 配置二级缓存 -->
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<!-- 强制Hibernate以更人性化的格式将数据存入二级缓存 -->
<prop key="hibernate.cache.use_structured_entries">true</prop>
<!-- Hibernate将收集有助于性能调节的统计数据 -->
<prop key="hibernate.generate_statistics">false</prop>
<!-- 指定缓存配置文件位置 -->
<prop key="hibernate.cache.provider_configuration_file_resource_path">/spring/ehcache.xml</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="hibernate.current_session_context_class">jta</prop>
<prop key="hibernate.transaction.factory_class">org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory</prop>
<prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</prop>
</props>
</property>
</bean>
public class LocalSessionFactoryBean extends HibernateExceptionTranslator
implements FactoryBean<SessionFactory>, ResourceLoaderAware, InitializingBean, DisposableBean {
private DataSource dataSource;
private Resource[] configLocations;
private String[] mappingResources;
private Resource[] mappingLocations;
private Resource[] cacheableMappingLocations;
private Resource[] mappingJarLocations;
private Resource[] mappingDirectoryLocations;
private Interceptor entityInterceptor;
private NamingStrategy namingStrategy;
private Object jtaTransactionManager;
private Object multiTenantConnectionProvider;
private Object currentTenantIdentifierResolver;
private RegionFactory cacheRegionFactory;
private Properties hibernateProperties;
private Class<?>[] annotatedClasses;
private String[] annotatedPackages;
private String[] packagesToScan;
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
private Configuration configuration;
private SessionFactory sessionFactory;
那其实呢,sessionFactory是LocalSessionFactoryBean对象的一个属性,这点可以在LocalSessionFactoryBean类中可以看到,至于bean的注入为何是class的属性而非class本身,那是因为它实现了 FactoryBean<SessionFactory> 接口。sessionFacotry是由LocalSessionFactoryBean对象配置后生成的。生成后将sessionFactory对象注入到了spring容器,且仅此一个而已,默认单例嘛。
我们对数据库的操作都是用session对象,它是由sessionFactory对象生成的。下面是sessionFactory对象的两个方法:
/**
* Open a {@link Session}.
* <p/>
* JDBC {@link Connection connection(s} will be obtained from the
* configured {@link org.hibernate.service.jdbc.connections.spi.ConnectionProvider} as needed
* to perform requested work.
*
* @return The created session.
*
* @throws HibernateException Indicates a problem opening the session; pretty rare here.
*/
public Session openSession() throws HibernateException;
/**
* Obtains the current session. The definition of what exactly "current"
* means controlled by the {@link org.hibernate.context.spi.CurrentSessionContext} impl configured
* for use.
* <p/>
* Note that for backwards compatibility, if a {@link org.hibernate.context.spi.CurrentSessionContext}
* is not configured but JTA is configured this will default to the {@link org.hibernate.context.internal.JTASessionContext}
* impl.
*
* @return The current session.
*
* @throws HibernateException Indicates an issue locating a suitable current session.
*/
public Session getCurrentSession() throws HibernateException;
那我们的项目使用getCurrentSession()获取session对象的。
hibernate interceptor怎么配置呢?
LocalSessionFactoryBean对象的entityInterceptor属性可以配置,你可以在xml中配置它,加到sessionFactory这个bean的xml配置中去。
<property name="entityInterceptor"> <bean class="com.my.pay.common.AutoTableNameInterceptor"/> </property>
那,它只能配置一个。因为sessionFactory是单例,他也只能是单例,引用sessionFactory的Dao对像也是单例,service,controller通通都是单例。那么有个问题就是,动态替换表名,如何动态?动态多例这条路已经封死了。那只剩下,动态修改interceptor对象的值。听起来像是不错的建议。我尝试后只能以失败告终,无法解决线程安全问题!待会儿描述原因。
所以配置到xml中无法实现我的需求。那么就只能在代码中设置了,还好sessionFactory对象提供了我们修改它的入口。
@Resource(name = "sessionFactory")
private SessionFactory sessionFactory;
protected Session getSession(){
if(autoTableNameInterceptorThreadLocal.get() == null){
return this.sessionFactory.getCurrentSession();
}else{
SessionBuilder builder = this.sessionFactory.withOptions().interceptor(autoTableNameInterceptorThreadLocal.get());
Session session = builder.openSession();
return session;
}
}
/**
* 线程域变量,高效实现线程安全(一个请求对应一个thread)
*/
private ThreadLocal<AutoTableNameInterceptor> autoTableNameInterceptorThreadLocal = new ThreadLocal<>();
public List<WfPayLog> find(Long merchantId, Long poolId,String sdk, Long appId,String province,
Integer price,
String serverOrder, String imsi,Integer iscallback,String state,
Date start, Date end, Paging paging) {
。。。。
//定制表名拦截器,设置到线程域
autoTableNameInterceptorThreadLocal.set(new AutoTableNameInterceptor("wf_pay_log","wf_pay_log_"+ DateUtil.formatDate(start,DateUtil.YEARMONTH_PATTERN)));
List<WfPayLog> wfPayLogs;
if (paging == null) {
wfPayLogs = (List<WfPayLog>) find(hql.toString(), params); //find方法里面有 this.getSession().createQuery("hql") 等方法
} else {
wfPayLogs = (List<WfPayLog>) findPaging(hql.toString(), "select count(*) " + hql.toString(), params, paging);
}
return wfPayLogs;
}
红色标识的代码就是核心代码,核心说明。意思是,在DAO层对象中,注入sessionFactory对象创建session就可以操作数据库了,我们改变了session的获取方式。当需要改变表名的时候,我们定义线程域变量,在需要interceptor的时候将interceptor对象保存到线程域中去,然后你操作的时候再拿到这个配置有拦截器的session去操作数据库,这个时候interceptor就生效了。
不用线程域变量保存,直接定义对象成员变量肯定是不行的,因为会有并发问题(多个请求(线程)同时调用dao方法,dao方法执行的时候又调用getSession()方法,可能当你getSession的时候,别的请求,已经把interceptor给换掉了。),当然用synchronized也可以解决。线程域的使用,比synchronized同步锁高效得多。线程域的使用,保证了interceptor对象和请求(线程)是绑在一起的,dao方法的执行,只要执行语句在同一个线程内,线程所共享的对象信息肯定一致的,所以不存在并发问题。
上面曾说过,单例interceptor不行,原因是:无法解决线程安全问题。 AutoTableNameInterceptor是一个单例,你在dao层可以修改他的值,比如新增set操作,没问题。可是你set的同时,别的请求也在set,就会导致destName,srcName的值一直在变动,除非你的请求是串行的(排队的,一个一个来的)。而且可能n个dao实例都会调用interceptor, 你怎么实现线程同步?除非你在dao操作的时候锁住整个interceptor对象,这个多影响性能! 使用线程域,没法实现,经过测试,发现hibernate底层会有多个线程调用interceptor方法,而不是我们的请求线程!所以,从dao到interceptor已经不是一个线程。interceptor的onPrepareStatement回调方法又是如此的单调,功能有限,哎。再说了,使用单例,是sessionFactory的全局配置,影响效率,通过代码添加是临时性的。
以上这篇spring hibernate实现动态替换表名(分表)的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
# spring
# hibernate
# 动态替换表名
# 利用Sharding-Jdbc组件实现分表
# 都是
# 会有
# 多个
# 是由
# 你在
# 给大家
# 中去
# 拦截器
# 是一个
# 就会
# 是个
# 有个
# 你可以
# 又是
# 那是
# 他也
# 死了
# 也在
# 你怎么
# 当你
相关文章:
上海网站制作网站建设公司,建筑电工证网上查询系统入口?
如何快速配置高效服务器建站软件?
怀化网站制作公司,怀化新生儿上户网上办理流程?
,巨量百应是干嘛的?
如何通过商城免费建站系统源码自定义网站主题?
制作国外网站的软件,国外有哪些比较优质的网站推荐?
制作宣传网站的软件,小红书可以宣传网站吗?
c# await 一个已经完成的Task会发生什么
如何选择网络建站服务器?高效建站必看指南
太平洋网站制作公司,网络用语太平洋是什么意思?
制作营销网站公司,淘特是干什么用的?
香港服务器建站指南:外贸独立站搭建与跨境电商配置流程
济南专业网站制作公司,济南信息工程学校怎么样?
天津个人网站制作公司,天津网约车驾驶员从业资格证官网?
如何快速搭建个人网站并优化SEO?
如何确认建站备案号应放置的具体位置?
可靠的网站设计制作软件,做网站设计需要什么样的电脑配置?
安云自助建站系统如何快速提升SEO排名?
宿州网站制作公司兴策,安徽省低保查询网站?
如何在建站之星绑定自定义域名?
如何快速重置建站主机并恢复默认配置?
简易网站制作视频教程,使用记事本编写一个简单的网页html文件?
学校建站服务器如何选型才能满足性能需求?
香港服务器网站测试全流程:性能评估、SEO加载与移动适配优化
行程制作网站有哪些,第三方机票电子行程单怎么开?
网站制作公司排行榜,抖音怎样做个人官方网站
图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?
建站之星免费版是否永久可用?
成都响应式网站开发,dw怎么把手机适应页面变成网页?
如何在IIS7上新建站点并设置安全权限?
建站之星后台密码遗忘如何找回?
如何在Golang中使用encoding/gob序列化对象_存储和传输数据
如何配置支付宝与微信支付功能?
如何高效完成自助建站业务培训?
活动邀请函制作网站有哪些,活动邀请函文案?
如何通过万网虚拟主机快速搭建网站?
宁波自助建站系统如何快速打造专业企业网站?
兔展官网 在线制作,怎样制作微信请帖?
php8.4新语法match怎么用_php8.4match表达式替代switch【方法】
零服务器AI建站解决方案:快速部署与云端平台低成本实践
如何用5美元大硬盘VPS安全高效搭建个人网站?
实例解析angularjs的filter过滤器
建站主机选择指南:服务器配置与SEO优化实战技巧
Python如何创建带属性的XML节点
如何在云主机上快速搭建网站?
如何选择建站程序?包含哪些必备功能与类型?
建站主机与服务器功能差异如何区分?
Android自定义控件实现温度旋转按钮效果
建站之星手机一键生成:多端自适应+小程序开发快速建站指南
如何在阿里云香港服务器快速搭建网站?
*请认真填写需求信息,我们会在24小时内与您取得联系。