0

0

Java中spring hibernate如何实现动态替换表名

黄舟

黄舟

发布时间:2017-08-09 10:05:14

|

2580人浏览过

|

来源于php中文网

原创

1.概述

其实最简单的办法就是使用原生sql,如 session.createSQLQuery("sql"),或者使用jdbcTemplate。但是项目中已经使用了hql的方式查询,修改起来又累,风险又大!所以,必须找到一种比较好的解决方案,实在不行再改写吧!经过3天的时间的研究,终于找到一种不错的方法,下面讲述之。

 

2.步骤

2.1 新建hibernate interceptor类

立即学习Java免费学习笔记(深入)”;

/**
 * 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是个啥东西。



        
        
            
                com.my.pay.task.entity
                com.my.pay.paycms.entity
                com.my.pay.data.entity.payincome
            
        
        
             
                 classpath*:/hibernate/hibernate-sql.xml
             
        
        
            
                org.hibernate.dialect.MySQL5Dialect
                false
                false
                none
                
                false
                 
                true
                  
                  true 
                    
                  false
                  
                  /spring/ehcache.xml
                org.hibernate.cache.ehcache.EhCacheRegionFactory
                jta
                org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory
                com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup
            
        
    
public class LocalSessionFactoryBean extends HibernateExceptionTranslator
        implements FactoryBean, 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 接口。sessionFacotry是由LocalSessionFactoryBean对象配置后生成的。生成后将sessionFactory对象注入到了spring容器,且仅此一个而已,默认单例嘛。
我们对数据库的操作都是用session对象,它是由sessionFactory对象生成的。下面是sessionFactory对象的两个方法:

    /**
     * Open a {@link Session}.
     * 

* 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. *

* 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对象的。

HaiSnap
HaiSnap

一站式AI应用开发和部署工具

下载

hibernate interceptor怎么配置呢?

LocalSessionFactoryBean对象的entityInterceptor属性可以配置,你可以在xml中配置它,加到sessionFactory这个bean的xml配置中去。



      
      

那,它只能配置一个。因为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 autoTableNameInterceptorThreadLocal = new ThreadLocal<>();

public List 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 wfPayLogs;
        if (paging == null) {
            wfPayLogs = (List) find(hql.toString(), params); //find方法里面有 this.getSession().createQuery("hql") 等方法
    } else { 
       wfPayLogs = (List) 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的全局配置,影响效率,通过代码添加是临时性的。

 

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

14

2026.01.30

c++ 字符串格式化
c++ 字符串格式化

本专题整合了c++字符串格式化用法、输出技巧、实践等等内容,阅读专题下面的文章了解更多详细内容。

9

2026.01.30

java 字符串格式化
java 字符串格式化

本专题整合了java如何进行字符串格式化相关教程、使用解析、方法详解等等内容。阅读专题下面的文章了解更多详细教程。

12

2026.01.30

python 字符串格式化
python 字符串格式化

本专题整合了python字符串格式化教程、实践、方法、进阶等等相关内容,阅读专题下面的文章了解更多详细操作。

4

2026.01.30

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

20

2026.01.29

java配置环境变量教程合集
java配置环境变量教程合集

本专题整合了java配置环境变量设置、步骤、安装jdk、避免冲突等等相关内容,阅读专题下面的文章了解更多详细操作。

18

2026.01.29

java成品学习网站推荐大全
java成品学习网站推荐大全

本专题整合了java成品网站、在线成品网站源码、源码入口等等相关内容,阅读专题下面的文章了解更多详细推荐内容。

19

2026.01.29

Java字符串处理使用教程合集
Java字符串处理使用教程合集

本专题整合了Java字符串截取、处理、使用、实战等等教程内容,阅读专题下面的文章了解详细操作教程。

3

2026.01.29

Java空对象相关教程合集
Java空对象相关教程合集

本专题整合了Java空对象相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Spring中文手册
Spring中文手册

共0课时 | 0人学习

马士兵spring视频教程
马士兵spring视频教程

共25课时 | 9.1万人学习

Kotlin 教程
Kotlin 教程

共23课时 | 3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号