真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

springhibernate實(shí)現(xiàn)動態(tài)替換表名(分表)的方法

1.概述

成都創(chuàng)新互聯(lián)作為成都網(wǎng)站建設(shè)公司,專注網(wǎng)站建設(shè)公司、網(wǎng)站設(shè)計(jì),有關(guān)企業(yè)網(wǎng)站設(shè)計(jì)方案、改版、費(fèi)用等問題,行業(yè)涉及電動窗簾等多個(gè)領(lǐng)域,已為上千家企業(yè)服務(wù),得到了客戶的尊重與認(rèn)可。

其實(shí)最簡單的辦法就是使用原生sql,如 session.createSQLQuery("sql"),或者使用jdbcTemplate。但是項(xiàng)目中已經(jīng)使用了hql的方式查詢,修改起來又累,風(fēng)險(xiǎn)又大!所以,必須找到一種比較好的解決方案,實(shí)在不行再改寫吧!經(jīng)過3天的時(shí)間的研究,終于找到一種不錯(cuò)的方法,下面講述之。

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; // 目標(biāo)表名

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

這個(gè)interceptor會攔截所有數(shù)據(jù)庫操作,在發(fā)送sql語句之前,替換掉其中的表名。

2.2 配置到sessionFactory去

先看一下sessionFactory是個(gè)啥東西。


    
    
      
        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;

那其實(shí)呢,sessionFactory是LocalSessionFactoryBean對象的一個(gè)屬性,這點(diǎn)可以在LocalSessionFactoryBean類中可以看到,至于bean的注入為何是class的屬性而非class本身,那是因?yàn)樗鼘?shí)現(xiàn)了 FactoryBean 接口。sessionFacotry是由LocalSessionFactoryBean對象配置后生成的。生成后將sessionFactory對象注入到了spring容器,且僅此一個(gè)而已,默認(rèn)單例嘛。

我們對數(shù)據(jù)庫的操作都是用session對象,它是由sessionFactory對象生成的。下面是sessionFactory對象的兩個(gè)方法:

/**
   * 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;

那我們的項(xiàng)目使用getCurrentSession()獲取session對象的。

hibernate interceptor怎么配置呢?

LocalSessionFactoryBean對象的entityInterceptor屬性可以配置,你可以在xml中配置它,加到sessionFactory這個(gè)bean的xml配置中去。


   

那,它只能配置一個(gè)。因?yàn)閟essionFactory是單例,他也只能是單例,引用sessionFactory的Dao對像也是單例,service,controller通通都是單例。那么有個(gè)問題就是,動態(tài)替換表名,如何動態(tài)?動態(tài)多例這條路已經(jīng)封死了。那只剩下,動態(tài)修改interceptor對象的值。聽起來像是不錯(cuò)的建議。我嘗試后只能以失敗告終,無法解決線程安全問題!待會兒描述原因。

所以配置到xml中無法實(shí)現(xiàn)我的需求。那么就只能在代碼中設(shè)置了,還好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;
    }
}
/**
* 線程域變量,高效實(shí)現(xiàn)線程安全(一個(gè)請求對應(yīng)一個(gè)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) {
    。。。。

    //定制表名攔截器,設(shè)置到線程域
    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; 
}

紅色標(biāo)識的代碼就是核心代碼,核心說明。意思是,在DAO層對象中,注入sessionFactory對象創(chuàng)建session就可以操作數(shù)據(jù)庫了,我們改變了session的獲取方式。當(dāng)需要改變表名的時(shí)候,我們定義線程域變量,在需要interceptor的時(shí)候?qū)nterceptor對象保存到線程域中去,然后你操作的時(shí)候再拿到這個(gè)配置有攔截器的session去操作數(shù)據(jù)庫,這個(gè)時(shí)候interceptor就生效了。

不用線程域變量保存,直接定義對象成員變量肯定是不行的,因?yàn)闀胁l(fā)問題(多個(gè)請求(線程)同時(shí)調(diào)用dao方法,dao方法執(zhí)行的時(shí)候又調(diào)用getSession()方法,可能當(dāng)你getSession的時(shí)候,別的請求,已經(jīng)把interceptor給換掉了。),當(dāng)然用synchronized也可以解決。線程域的使用,比synchronized同步鎖高效得多。線程域的使用,保證了interceptor對象和請求(線程)是綁在一起的,dao方法的執(zhí)行,只要執(zhí)行語句在同一個(gè)線程內(nèi),線程所共享的對象信息肯定一致的,所以不存在并發(fā)問題。

上面曾說過,單例interceptor不行,原因是:無法解決線程安全問題。 AutoTableNameInterceptor是一個(gè)單例,你在dao層可以修改他的值,比如新增set操作,沒問題??墒悄鉺et的同時(shí),別的請求也在set,就會導(dǎo)致destName,srcName的值一直在變動,除非你的請求是串行的(排隊(duì)的,一個(gè)一個(gè)來的)。而且可能n個(gè)dao實(shí)例都會調(diào)用interceptor, 你怎么實(shí)現(xiàn)線程同步?除非你在dao操作的時(shí)候鎖住整個(gè)interceptor對象,這個(gè)多影響性能! 使用線程域,沒法實(shí)現(xiàn),經(jīng)過測試,發(fā)現(xiàn)hibernate底層會有多個(gè)線程調(diào)用interceptor方法,而不是我們的請求線程!所以,從dao到interceptor已經(jīng)不是一個(gè)線程。interceptor的onPrepareStatement回調(diào)方法又是如此的單調(diào),功能有限,哎。再說了,使用單例,是sessionFactory的全局配置,影響效率,通過代碼添加是臨時(shí)性的。

以上這篇spring hibernate實(shí)現(xiàn)動態(tài)替換表名(分表)的方法就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持創(chuàng)新互聯(lián)。


文章題目:springhibernate實(shí)現(xiàn)動態(tài)替換表名(分表)的方法
網(wǎng)頁地址:http://weahome.cn/article/jcjseo.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部