混合トランザクション
ORM Frameworkのトランザクションマネージャーのトランザクションでは、JDBCTEMPLATEを使用してSQLを実行することは、トランザクション管理に含まれません。
以下は、DataSourcetransactionManagerトランザクション内でJDBCTEmplateを使用する必要がある理由を確認するソースコード分析です。
1。トランザクションを開始します
DataSourcetransactionManager
Protected void dobegin(Object Transaction、TransactionDefinition Definition){dataSourcetransactionObjectTXObject =(dataSourcetransActionObject)Transaction;接続con = null; try {if(txobject.getConneStholder()== null || txobject.getConneStholder()。IssynChronizedWithTransaction()){connectionNewCon = this.datasource.getConnection(); if(logger.isdebugenabled()){logger.debug( "acchiredConnection [" + newcon + "] for JDBCトランザクション"); } txobject.setConneStholder(NewConneStholder(NewCon)、True); } txobject.getConneStholder()。 con = txobject.getConneStholder()。getConnection(); integerpreviousisolationLevel = dataSourceutils.prepareconnectionforTransaction(con、definition); txobject.setPreviousisolationLevel(前IsolationLevel); //必要に応じてManualCommitに切り替えます。これは、一部のJDBCドライバーでは非常に高価です。 if(con.getautocommit()){txobject.setmustrestoreautocommit(true); if(logger.isdebugenabled()){logger.debug( "switchingjdbc connection [" + con + "] to manual commit"); } con.setautocommit(false); } txobject.getConneStholder()。setTransactionActive(true); int timeout = sechinetimeout(definition); if(timeout!= transactionDefinition.timeout_default){txobject.getConneStolder()。setimeoutInseConds(Timeout); } //セッションホルダーをスレッドにバインドします。 if(txobject.isnewconnerceder()){transactionsynchronizationmanager.bindresource(getDataSource()、txobject.getConneStulder()); }} catch(Exception ex){datasourceutils.releaseconnection(con、this.datasource); newcannotcreateTransactionException( "JDBC接続FORTRANSACTIONを開くことができなかった"、ex)を投げる); }}Dobegin()メソッドは、データソース名キーと接続ホルダーを値として使用し、スレッドローカル変数に開かれたデータベース接続をバインドします。
2。接続をバインドします
public static void bindresource(objectkey、object value)throws IllegalStateException {object everalykey = transactionsynchronizationutils.unwrapresourceifn decessary(key); assert.notnull(value、 "valueはnullであってはならない"); map <object、object> map = resources.get(); // setwedlocal map ifnone fund if(map == null){map = newhashmap <object、object>(); Resources.set(Map); } object oldvalue = map.put(everitykey、value); // voidとしてマークされたaresourceholderを透過的に抑制する... if(oldvalue instance ofresourceholder &&((resourceholder)oldvalue).isvoid()){oldvalue = null; } if(oldValue!= null){newillegalstateException( "laly value [" + oldvalue + "] for key [" + eactalkey + "] for thread [" + thread.currentthread()。getName() + "]"); } if(logger.istraceEnabled()){logger.trace( "boundValue [" + value + "] for key [" + eactykey + "] toweth [" + thread.currentthread()。getName() + "]"); }}リソース変数は上記のスレッドローカル変数であるため、後続のJDBCtemplateはデータベース接続を見つけるためのキーとしてDataSourceを使用できます。
3。SQLを実行します
jdbctemplate
public ObjectExecute(preatedStatementcreator PSC、preatedStatementCallbackアクション)throwsDataAccesSexception {assert.notnull(psc、 "preatedStatementcreatorはnullである必要はありません"); assert.notnull(action、 "コールバックオブジェクトはnullでなければならない"); if(logger.isdebugenabled()){string sql = getsql(psc); logger.debug( "exectingprepared sqlステートメント" +(sql!= null? "[" + sql + "]": "")); } connection con = datasourceutils.getConnection(getDataSource()); represedStatement PS = null; try {connection contouse = con; if(this.nativejdbcextractor!= null && this.nativejdbcextractor.isnativeconnectionn disevereryfornativepreparedStatements()){contouse = this.nativejdbcextractor.getNativeConnection(con); } ps = psc.createpreparedStatement(contouse); ApplyStateMentSettings(PS); preatedStatementspstouse = ps; if(this.nativejdbcextractor!= null){pstouse = this.nativejdbcextractor.getNativePreparedStatement(PS); } object result = action.doinpreparedStatement(pstouse); HandleWarnings(PS);返品結果; } catch(sqlexception ex){//潜在的な接続プールのデッドロックを回避するために、例外翻訳者がまだ初期化されていない場合。 if(psc instanceofparameterdisposer){((parameterdisposer)psc).cleanupparameters(); } string sql = getsql(psc); psc = null; JDBCUTILS.CLOSESTATEMENT(PS); ps = null; DataSourceutils.releaseConnection(con、getDataSource()); con = null; throwgetexceptionTranslator()。翻訳( "preatedStatementCallback"、sql、ex); }最後に{if(psc instanceofparameterdisposer){((parameterdisposer)psc).cleanupparameters(); } jdbcutils.closeStatement(ps); DataSourceutils.releaseConnection(con、getDataSource()); }}
4。接続を取得します
DataSourceutils
public static Connection dogetConnection(dataSourcedataSource)はsqlexception {assert.notnull(dataSource、 "dataSource no dataSource"); ConnectionHolder conholder =(connectionholder)transactionsynchronizationmanager.getResource(dataSource); if(conholder!= null &&(conholder.hasconnection()|| conholder.issynchronizedwittransaction())){conholder.requested(); if(!conholder.hasconnection()){logger.debug( "dataSourceからのjdbc接続のfetching resumed jdbc接続"); conholder.setConnection(dataSource.getConnection()); } returnConholder.getConnection(); } //そうでなければ、ここでノーホルダーまたは空のスレッドバウンドホルダーを取得しました。 logger.debug( "dataSourceからのfetchingjdbc接続");接続con = dataSource.getConnection(); if(transactionsynchronizationmanager.issynchronizationactive()){logger.debug( "jdbc connection"); //トランザクション内のさらなるJDBCアクションを使用するには、SameConnectionを使用します。 //トランザクション完了時に同期によりスレッドバウンドオブジェクトが削除されます。 ConnectionHolderHoldertouse = conholder; if(holdertouse == null){holdertouse = new ConnectionHolder(con); } else {holdertouse.setConnection(con); } holdertouse.requested(); TransactionSynchronizationManager.RegisterSynChronization(NewConnectionSynChronization(Holdertouse、DataSource)); holdertouse.setsynchronizedwithtransaction(true); if(holdertouse!= conholder){transactionsynchronizationmanager.bindresource(datasource、holdertouse); }} return con; } DataSourceutilsは、TransactionsynchronizationManagerを介した接続も取得することがわかります。したがって、JDBCTEMPLATEとDataSourcetransactionManagerが同じデータソースを持っている限り、間違いなく同じデータベース接続を取得し、当然トランザクションを正しく送信してロールバックできます。
最初に言及された問題を説明するための例として冬眠を取り、ORMフレームワークのトランザクションマネージャーがJDBCTEMPLATEを管理できない理由を確認しましょう。
5 ORMトランザクションマネージャー
hibernatetransactionManager
if(txobject.isnewsessionholder()){transactionsynchronizationmanager.bindresource(getsessionFactory()、txobject.getSessionHolder()); }ORMフレームワークは、DataSourceを使用するためにTransactionManagerに直接注入するのではなく、上記のHibernate Transaction Managerと同様に、独自のSessionFactoryやその他のオブジェクトを使用してDataSourceを操作します。したがって、TransactionsynchronizationManager(1つはSessionFactory名であり、もう1つはDataSource名です)でバインディングするときに異なるキーが使用されるため、SessionFactoryとJDBCtemplateの基礎となるデータソースは同じかもしれませんが、JDBCTEMPLATEは、ORMトランザクションマネージャーがトランザクションを開始するデータベース接続を取得できません。
豆間の区別
パブリックプロジェクトのSpring構成ファイルは、複数のプロジェクトで参照される場合があります。各プロジェクトには、公開プロジェクトの豆の一部のみが必要な場合があるため、これらのプロジェクトのスプリングコンテナが開始されると、どの豆を作成するかを区別する必要があります。
1。アプリケーションの例
例として、ApacheのオープンソースフレームワークであるJetspeedで構成を取得:page-manager.xml
<bean name = "xmlpagemanager" class = "org.apache.jetspeed.page.page.castorxmlpagemanager" init-method = "init" destroy-method = "destroy"> <meta key = "j2:cat" value = "xmlpagemanager <refbean = "idgenerator"/> </constructor-arg> <constructor-arg index = "1"> <refbean = "xmldocumenthandlerfactory"/> </constructor-arg>…</bean> <bean id = "dbpagemanager" class = "org.apache.jetspeed.impl.databege Destrow-Method = "Destroy"> <Meta key = "J2:cat" value = "dbpagemanager orpageserializer"/> <! - ojb構成ファイルリソースパス - > <constructor-arg index = "0"> <値<constructor-arg index = "1"> <ref bean = "idgenerator"/> </constructor-arg>…</bean>
2.ビーンフィルター
JetSpeedBeanDefinitionFilterがスプリングコンテナの各Bean定義を解析すると、dbpagemanageror pageserializerなどの上記の豆構成のJ2:CATに対応する値を取り出します。この部分は、現在のキー(構成ファイルから読み取る)に正規式として一致します。マッチング上の豆のみがスプリングコンテナによって作成されます。
JetSpeedBeanDefinitionFilter
public boolean match(beandefinition bd){string beancategoriesexpression =(string)bd.getattribute(category_meta_key);ブールマッチ= true; if(beancategoriesexpression!= null){matched =((matcher!= null)&& matcher.match(beancategoriesexpression)); } return matched;} public void RegisterDynamicalias(BeanDefinitionRegistry Registry、String BeanName、BeanDefinition BD){String Aliase =(String)BD.GetAttribute(Alias_Meta_Key); if(aliases!= null){stringtokenizer st = newstringtokenizer(airases、 "、"); while(St.HasmoreTokens()){String Alias = St.NextToken(); if(!alias.equals(beanname)){registry.registeralias(beanname、alias); }}}} Match()メソッドのcategory_meta_keyの値はJ2:catです。現在のキーはマッチャークラスに保存され、現在のキーを各豆の正規表現と一致させる責任があります。
RegisterDynamicaliasの役割は次のとおりです。BeanMatchが正常に一致した後、カスタマイズされたスプリングコンテナはこの方法を呼び出して、Beanのエイリアスを登録します。詳細については、以下の1.3のソースコードを参照してください。
3.スプリングコンテナをカスタマイズします
スプリングコンテナをカスタマイズし、RegisterBeanDefinition()メソッドをオーバーライドし、SpringがBeanを登録するときにインターセプトします。
public class filteringxmlwebapplicationContextends xmlwebapplicationContext {private jetspeedbeandefinitionfilterfilter; publicFilteringXmlWebApplicationContext(JetsPeedBeanDefinitionFilter Filter、String [] configlocations、Properties initProperties、servletContext servletContext){{this(filter、configlocations、initproperies、servletcontext、null); } publicFilteringXmlWebApplicationContext(jetSpeedBeanDefinitionFilterフィルター、string [] configlocations、Properties initproperties、servletContext servletcontext、applicationContext parent){super(); if(parent!= null){this.setParent(parent); } if(initProperties!= null){propertyplaceholderconfigurer ppc = new PropertyPlaceHolderConFigurer(); ppc.setignoreUnResolvable PlaceHolders(true); ppc.setsystempropertiesmode(propertyplaceholderconfigurer.system_properties_mode_fallback); ppc.setProperties(initproperties); AddBeanFactoryPostProcessor(PPC); } setConfiglocations(configlocations); setServletContext(servletcontext); this.filter = filter; } protected DefaultListableBeanFactoryCreateBeanFactory(){return new FilterListableBeanFactory(Filter、GetInternalParentBeanFactory()); }} public classFilteringListableBeanFactory defaultListableBeanFactory {private JetspeedBeandefinitionFilterFilter; public filteringListableBeanFactory(jetSpeedbeandefinitionFilterfilter、BeanFactory ParentBeanFactory){Super(ParentBeanFactory); this.filter = filter; if(this.filter == null){this.filter = newJetsPeedBeedBeanDefinitionFilter(); } this.filter.init(); } / ** * registerBeanDefinitionMethodをオーバーライドして、オプションでbeandefinitionを除外し、 * / public void registerbeandefinition(stringbeanname、beandefinition bd)を動的に登録する場合は、beandefinitionstoreexception {(bd))を投げます。 bd); if(filter!= null){filter.registerdynamicalias(this、beanname、bd); }}}} 4。エイリアスThe Bean
BeanReferenceFactoryBean Factory Beanを使用して、上記で構成された2つの豆を包みます(xmlpagemanager and dbpagemanager)。キーは独自に一致し、実装は現在のキーを構成して2つの実装を切り替えることです。すべてのエイリアスは1つに一致しているため、Beanを指すBeanはエイリアスを直接引用しているだけです。たとえば、以下のPageLayOutComponent。
page-manager.xml
<bean> <meta key = "j2:cat" value = "xmlpagemanager" /> <meta key = "j2:alias" value = "org.apache.jetspeed.page.pagemanager" /> <propertyname = "Targetbeanname" value = "xmlpagemanager" key = "j2:cat" value = "dbpagemanager" /> <meta key = "j2:airas" value = "org.apache.jetspeed.page.pagemanager" /> <propertyname = "targetbeanname" value = "dbpagemanager" />> < /bean> <bean id = "org.apach.pagheted.layt. > <Meta key = "j2:cat" value = "default"/> <constructor-arg index = "0"> <refbean = "org.apache.jetspeed.page.pagemanager"/> </constructor-arg>