Heim >Java >javaLernprogramm >So ersetzen Sie Tabellennamen im Frühlingsschlaf in Java dynamisch
1. Übersicht
Tatsächlich ist es am einfachsten, natives SQL zu verwenden, z. B. session.createSQLQuery("sql"), oder jdbcTemplate zu verwenden. Allerdings wurde im Projekt eine HQL-Abfrage verwendet, deren Änderung ermüdend und riskant ist! Deshalb müssen wir eine bessere Lösung finden. Wenn es nicht funktioniert, schreiben Sie es neu! Nach drei Tagen Recherche habe ich endlich eine gute Methode gefunden, die im Folgenden beschrieben wird.
2. Schritte
2.1 Erstellen Sie eine neue Hibernate-Interceptor-Klasse
/** * 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; } }
Dieser Interceptor fängt alle Datenbankoperationen ab und ersetzt sie, bevor er sie sendet SQL-Anweisung. Entfernen Sie den Tabellennamen.
2.2 Für sessionFactory konfigurieren
Werfen wir zunächst einen Blick darauf, was sessionFactory ist.
<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;
Tatsächlich ist sessionFactory ein Attribut des LocalSessionFactoryBean-Objekts. Dies ist in der LocalSessionFactoryBean-Klasse zu sehen Warum die Bean-Injektion eine Eigenschaft der Klasse und nicht der Klasse selbst ist, liegt daran, dass sie die FactoryBeana7bd7c0154d420bde695d24770c97980-Schnittstelle implementiert. sessionFacotry wird durch Konfigurieren des LocalSessionFactoryBean-Objekts generiert. Nach der Generierung wird das sessionFactory-Objekt in den Spring-Container eingefügt, und dies ist standardmäßig ein Singleton.
Wir alle betreiben die Datenbank mithilfe des Sitzungsobjekts, das vom sessionFactory-Objekt generiert wird. Im Folgenden sind zwei Methoden des sessionFactory-Objekts aufgeführt:
/** * 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;
Dann verwendet unser Projekt getCurrentSession(), um das Sitzungsobjekt abzurufen.
Wie konfiguriere ich den Hibernate-Interceptor?
LocalSessionFactoryBean对象的entityInterceptor属性可以配置,你可以在xml中配置它,加到sessionFactory这个bean的xml配置中去。
<property name="entityInterceptor"> <bean class="com.my.pay.common.AutoTableNameInterceptor"/> </property>
Dann kann nur eine konfiguriert werden. Da sessionFactory ein Singleton ist, kann es sich nur um ein Singleton handeln. Das Dao-Objekt, das auf sessionFactory verweist, ist ebenfalls ein Singleton. Es stellt sich also die Frage: Wie kann der Tabellenname dynamisch ersetzt werden? Der Weg zu dynamischen Mehrfachfällen ist versperrt. Jetzt müssen Sie nur noch den Wert des Interceptor-Objekts dynamisch ändern. Klingt nach einem guten Rat. Meine Versuche scheiterten nur und konnten das Thread-Sicherheitsproblem nicht lösen! Die Gründe werden später beschrieben.
Die Konfiguration in XML kann meine Anforderungen also nicht erfüllen. Dann kann es nur im Code festgelegt werden. Glücklicherweise bietet uns das sessionFactory-Objekt einen Einstiegspunkt zum Ändern.
@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; }
Der rot markierte Code ist der Kerncode und die Kernbeschreibung. Dies bedeutet, dass im DAO-Schichtobjekt die Datenbank durch Einfügen des sessionFactory-Objekts betrieben werden kann, um eine Sitzung zu erstellen. Wir haben die Methode zum Abrufen der Sitzung geändert. Wenn wir den Tabellennamen ändern müssen, definieren wir Thread-Domänenvariablen und speichern das Interceptor-Objekt in der Thread-Domäne. Wenn Sie dann arbeiten, erhalten Sie die Sitzung, die mit dem Interceptor konfiguriert ist, um die Datenbank zu betreiben Zeit Der Abfangjäger wird wirksam.
Es ist definitiv nicht möglich, Objektmitgliedsvariablen ohne Thread-Domänenvariablen direkt zu definieren, da es zu Parallelitätsproblemen kommt (mehrere Anforderungen (Threads) rufen gleichzeitig die Dao-Methode auf und die getSession()-Methode ist es). Wird aufgerufen, wenn die Dao-Methode ausgeführt wird. Möglicherweise wurde bei getSession der Interceptor durch andere Anforderungen ersetzt. Dies kann natürlich auch mit synchronisiert gelöst werden. Die Verwendung von Thread-Domänen ist wesentlich effizienter als synchronisierte Synchronisationssperren. Durch die Verwendung der Thread-Domäne wird sichergestellt, dass das Interceptor-Objekt und die Anforderung (Thread) miteinander verknüpft sind. Wenn die Dao-Methode ausgeführt wird, müssen die vom Thread gemeinsam genutzten Objektinformationen konsistent sein, solange sich die Ausführungsanweisung im selben Thread befindet Es gibt also kein Parallelitätsproblem.
Wie oben erwähnt, funktioniert der Singleton-Interceptor nicht, da er das Thread-Sicherheitsproblem nicht lösen kann. AutoTableNameInterceptor ist ein Singleton. Sie können seinen Wert auf der Dao-Ebene problemlos ändern, z. B. durch das Hinzufügen einer neuen Mengenoperation. Aber während Sie festlegen, werden auch andere Anforderungen festgelegt, was dazu führt, dass sich die Werte von destName und srcName ständig ändern, es sei denn, Ihre Anforderungen sind seriell (eine nach der anderen in der Warteschlange). Und vielleicht rufen n Dao-Instanzen den Interceptor auf. Wie erreichen Sie die Thread-Synchronisierung? Sofern Sie während des Dao-Betriebs nicht das gesamte Interceptor-Objekt sperren, wirkt sich dies auf die Leistung aus! Dies kann nicht mit der Thread-Domäne erreicht werden. Nach dem Test haben wir festgestellt, dass es am Ende des Ruhezustands mehrere Threads gibt, die die Interceptor-Methode aufrufen, nicht unseren Anforderungsthread! Daher ist von Dao bis Interceptor kein Thread mehr. Die onPrepareStatement-Rückrufmethode des Interceptors ist so eintönig und hat nur begrenzte Funktionen, hey. Außerdem handelt es sich bei der Verwendung eines Singletons um eine globale Konfiguration der sessionFactory, die sich auf die Effizienz auswirkt. Das Hinzufügen über Code ist vorübergehend.
Das obige ist der detaillierte Inhalt vonSo ersetzen Sie Tabellennamen im Frühlingsschlaf in Java dynamisch. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!