Gemischte Transaktionen
In Transaktionen des Transaktionsmanagers des ORM Frameworks wird die Verwendung von JDBCTEMplate zur Ausführung von SQL nicht in die Transaktionsverwaltung aufgenommen.
Das Folgende ist eine Quellcodeanalyse, um zu sehen, warum JDBCTEMplate in der Transaktion von DataSourcetransactionManager verwendet werden muss.
1. Transaktionen starten
DataSourcetransactionManager
Protected void doBegin (Objekttransaktion, Transaktiondefinitionsdefinition) {DataSourcetRansActionObjectTxObject = (DataSourcetRansactionObject) -Transaktion; Verbindungs con = null; try {if (txObject.getConnectionHolder () == null || txObject.getConnectionHolder (). IsynchronizedWithTransaction ()) {ConnectionNewcon = thataSource.getConnection (); if (logger.isdebugenabled ()) {logger.debug ("AcciredConnection [" + newcon + "] für JDBC -Transaktion"); } txObject.setConnectioneholder (NewConnectionHolds (NewCon), True); } txObject.getConnectionHolder (). setSynchronizedWithTransaction (true); con = txObject.getConnectionHolder (). getConnection (); IntegerPreviousisolationLevel = DataSourceutils.PreperArnectionFortransaction (con, Definition); txObject.setPreviousisolationLevel (vorherisolationlevel); // Bei Bedarf zum Handbuch wechseln. Dies ist bei einigen JDBC -Treibern sehr teuer, //, daher wollen wir es nicht unnötig (zum Beispiel, wenn wir explizit // den Vorfeld -Pool so konfiguriert haben, dass es bereits festgelegt ist). if (con.getAutocommit ()) {txObject.setMustRestoreAutoCommit (true); if (logger.isdebugenabled ()) {logger.debug ("SwitchingJdbc Connection [" + con + "] zu manuellem Commit"); } con.setAutocommit (false); } txObject.getConnectionHolder (). settransactionactive (true); int timeout = deterImetimeout (Definition); if (timeout! } // Binden Sie den Sitzungsinhaber an den Thread. if (txObject.isNewConnectionHolder ()) {TransactionsynchronizationManager.BindResource (getDataSource (), txObject.getConnectionHoldspolter ()); }} catch (Ausnahme ex) {dataSourceutils.releaseconnection (con, this.dataSource); throw newcannotcreatetransactionException ("konnte die JDBC -Verbindungsverbindung nicht öffnen", Ex); }}Die Methode DOBEGIN () verwendet den Datenquellennamenschlüssel und den Anbindungslimbetrieb der Datenquelle als Wert und binden die Datenbankverbindung, die an eine ThreadLocal -Variable geöffnet wurde.
2. Binden Sie die Verbindung
public static void bindresource (ObjectKey, Objektwert) löst illegalStateException aus {Object trupleKey = transactionsynchronizationUtils.unwraPresourceifnegary (Schlüssel); Assert.notnull (Wert, "Wert darf nicht null sein"); MAP <Objekt, Objekt> map = ressourcen.get (); // ThreadLocal Map ifnone gefunden if (map == null) {map = newasMasMap <Objekt, Objekt> (); Ressourcen.Set (MAP); } Objekt oldValue = map.put (tatsächliche Key, Wert); // Unterdrückung von Aresourceholder, der als void gekennzeichnet war ... if (OldValue -Instanz des Resourceholders && ((Ressourcenhalter) OldValue) .isvoid ()) {OldValue = null; } if (oldValue! = null) {throw newillegalStateException ("bereits value [" + oldValue + "] für Key [" + trupleKey + "] an Thread [" + thread.current thread (). getName () + "]"); } if (logger.istaceEnabled ()) {logger.trace ("boundValue [" + value + "] für key [" + trupleKey + "] to Thread [" + thread.currentThread (). getName () + "]"); }}Die Ressourcenvariable ist die oben erwähnte ThreadLocal -Variable, sodass die nachfolgende JDBCTEMplate DataSource als Schlüssel zum Ermitteln der Datenbankverbindung verwenden kann.
3. Ausführen von SQL
JDBCTEMPLATE
public Objectexecute (PrepedStatementCreator PSC, PreparedStatementCallback -Aktion) ThrowsDataAccessException {assert.notnull (PSC, "PreparedStatementCreator darf nicht null sein"); Assert.notnull (Aktion, "Callback -Objekt darf nicht null sein"); if (logger.isdebugenabled ()) {string sql = getQl (psc); logger.debug ("executingPrepared SQL Anweisung" + (SQL! = NULL? "[" + SQL + "]": ""); } Verbindung con = dataSourceutils.getConnection (getDataSource ()); PrepedStatement ps = null; try {Connection contouse = con; if (this.NativejdbCextractor! } ps = psc.creatreprepreprepreprepreprepeatement (contouse); applyStatementsSettings (PS); PreparedStatementSpstouse = ps; if (this.NativejdbCextractor! } Objekt Ergebnis = action.doInPrepararedStatement (pSTOUSE); Handlewarnings (PS); Rückgabeergebnis; } catch (SQLEXception ex) {// releaseconnection früh, um potenzielle Verbindungspool -Deadlock // zu vermeiden, wenn der Ausnahmeübersetzer noch nicht initialisiert wurde. if (pSC InstanceOfParameterDisposer) {((parameterDisposer) pSC) .Cleanupparameter (); } String sql = getQl (psc); psc = null; Jdbcutils.closestatement (ps); ps = null; DataSourceutils.releaseconnection (con, getDataSource ()); con = null; thringGetExceptionTranslator (). Translate ("prepedStatementCallback", SQL, Ex); } endlich {if (pSC InstanceOfParameterDisposer) {((parameterDisposer) psc) .Cleanupparameter (); } Jdbcutils.closestatement (ps); DataSourceutils.releaseconnection (con, getDataSource ()); }}
4. Erhalten Sie eine Verbindung
DataSourceutils
public statische Verbindung degetConnection (DataSourcedataSource) löst SQLEXception (Assert.notnull (DataSource, "No DataSource angegeben") aus; Anbieter conholder = (Anbieter) TransactionsynchronizationManager.getResource (DataSource); if (conholders! if (! conholder.hasconnection ()) {logger.debug ("fetchingResumed JDBC -Verbindung von DataSource"); conholder.setConnection (DataSource.getConnection ()); } returnConholder.getConnection (); } // sonst haben wir hier entweder einen Noholder oder einen leeren Thread-Bound-Halter. logger.debug ("fetchingjdbc -Verbindung von DataSource"); Connection con = dataSource.getConnection (); if (transactionsynchronizationManager // Verwenden Sie die SameConnection für weitere JDBC -Aktionen innerhalb der Transaktion. // Thread-BoundObject wird durch Synchronisation nach Abschluss der Transaktion entfernt. Anbieterhalterung = conholder; if (holdertouse == null) {holdertouse = new Anbieter (con); } else {holdertouse.setConnection (con); } holdertouse.requested (); TransactionsynchronizationManager.regitersynchronization (NewConnectionSynchronization (Holdertouse, DataSource)); holderTouse.setsynchronized withTransaction (true); if (holdertouse! = conholders) {transactionsynchronizationManager.bindResource (DataSource, Holdertouse); }} return con; } Es ist zu sehen, dass DataSourceutils auch eine Verbindung durch TransactionsynchronizationManager erhalten. Solange JDBCTEMplate und DataSourcetRansActionManager dieselbe Datenquelle haben, erhalten Sie daher definitiv die gleiche Datenbankverbindung und können natürlich Transaktionen ordnungsgemäß senden und zurückrollen.
Nehmen wir Hibernate als Beispiel, um das am Anfang erwähnte Problem zu veranschaulichen, und herauszufinden, warum der Transaktionsmanager des ORM -Frameworks JDBCTEMplate nicht verwalten kann.
5 ORM -Transaktionsleiter
HibernatetransactionManager
if (txObject.isNewSessionHolder ()) {TransactionsynchronizationManager.BindResource (GetSessionFactory (), TxObject.GetSessioneholder ()); }Da das ORM -Framework für die Verwendung nicht direkt in die Transaktionsmanager injiziert wird, sondern auch seine eigene SessionFactory- und andere Objekte verwendet, um DataSource zu betreiben, genau wie der Hibernate -Transaktionsmanager oben. Obwohl die zugrunde liegende Datenquelle von SessionFactory und JDBCtemplate gleich sein kann, da bei der Bindung in TransactionsynchronizationManager unterschiedliche Schlüssel verwendet werden (der eine SessionFactory -Name ist und der andere ist der DataSource -Name), kann JDBCTEMplate die Datenbankverbindung, die der ORM -Transaktionsmanager beim Durchführen der Transaktion startet, nicht abrufen.
Die Unterscheidung zwischen Bohnen
Die Frühlingskonfigurationsdatei in einem öffentlichen Projekt kann durch mehrere Projekte verwiesen werden. Da jedes Projekt möglicherweise nur einen Teil der Bohnen im öffentlichen Projekt erfordern, ist es notwendig, zu unterscheiden, welche Bohnen erstellt werden sollen.
1. Anwendungsbeispiele
Einen Konfiguration in JetSpeed, ein Open-Source-Framework von Apache, als Beispiel: Page-Manager.xml
<bean name = "xmlpageManager" class = "org.apache.jetspeed.page.psml.castorxmlpageManager" init-method = "init" destroy-method = "destroy"> <meta key = "j2: cat" value = "xmlpagemanager oder PageSerialIner" /> <constructor-arrerator " /> <constructor-° CGener-" /> <constructor-° CGener-" /> <constructor-° Cg" /> <" /> <" /> <constructor-" /> <-Constructor-" /> <-Elstructor-" /> <-Constructor-Idruteor- </constructor-arg> <constructor-arg Index = "1"> <refbean = "xmldocumentHandlerfactory"/> </constructor-arg> ... </bean> <bean id = "dbpagemanager" class = "org.apache.jetspeed.impl.DatabaBaSepaGeManager" Init.-Method "Init.imPage =" Init.imPage = "Init.Impl.DatabaBaSepaGeManager" Init.-Method "Init.Impl.DatabaStaBaSepaGeManager" Init.-Method = "Initor. key="j2:cat" value="dbPageManager orpageSerializer" /> <!-- OJB configuration file resourcepath --> <constructor-arg index="0"> <value>JETSPEED-INF/ojb/page-manager-repository.xml</value> </constructor-arg> <!-- fragment id generator --> <constructor-arg index="1"> <ref bean = "idgenerator"/> </constructor-arg>… </bean>
2.Bohnenfilter
Wenn die JetSpeedbeanDefinitionFilter jede Bean -Definition im Federcontainer analysiert, wird der Wert, der J2: CAT in der obigen Bean -Konfiguration entspricht, wie dbpaGeManageroror -PageSerializer heraus. Dieser Teil wird dann als regulärer Ausdruck mit dem aktuellen Schlüssel (aus der Konfigurationsdatei gelesen) abgestimmt. Nur die Bohnen am Matching werden vom Federbehälter erstellt.
JetSpeedbeanDefinitionFilter
public boolean Match (BeanDefinition bd) {String beancategoriesExpression = (String) bd.getAttribute (category_meta_key); boolean Matched = true; if (beancategoriesExpression! } return Matched;} public void RegisterDynamicalias (BeanDefinitionRegistry Registry, String Beanname, BeanDefinition bd) {String aliases = (String) bd.getAttribute (alias_meta_key); if (aliase! while (St.Hasmoretokens ()) {String alias = St.NextToken (); if (! }}}} Der Wert von category_meta_key in der match () -Methode lautet j2: cat. Der aktuelle Schlüssel wird in der Matcher -Klasse gespeichert und ist dafür verantwortlich, den aktuellen Schlüssel mit dem regulären Ausdruck jeder Bean abzustimmen.
Die Rolle von RegisterDynamicalias lautet: Nach erfolgreicher Bean -Match nennt der angepasste Spring Container diese Methode, um einen Alias für die Bean zu registrieren. Weitere Informationen finden Sie im Quellcode in 1.3 unten.
3.. Federbehälter anpassen
Passen Sie einen Frühlingscontainer an, überschreiben Sie die RegisterBeANdeFinition () -Methode und nehmen Sie sie ab, wenn die Spring eine Bohne registriert.
öffentliche Klasse filteringxmlWebApplicationContextends xmlWebApplicationContext {private jetSpeedbeanDeFinitionFilterFilter; publicFilteringxmlWebApplicationContext (JetSpeedbeanDeFinitionFilter -Filter, String [] configLocations, Eigenschaften initProperties, servletContext servletContext) {this (Filter, Konfiguration, initProperties, servletContext, null); } publicFilteringxmlWebApplicationContext (JetSpeedbeanDeFinitionFilter -Filter, String [] configLocations, Eigenschaften initproperties, servletContext servletContext, ApplicationContext Parent) {Super (); if (parent! = null) {this.setParent (Eltern); } if (initproperties! ppc.setignoreUnResolvablePlacePlacePlace (true); ppc.setSystemPropertiesMode (PropertyPlaPleholderConFigurer.System_Properties_Mode_Fallback); ppc.setProperties (initProperties); addBeanFactoryPostProcessor (PPC); } setConfiglocations (configLocations); setServletContext (ServletContext); this.filter = filter; } Protected DefaultListableBeanFactoryCreatEnfactory () {Neue FilteringlistableBeanFactory zurückgeben (Filter, GetInternalParentBeArfactory ()); }} public classFilteringListableBeanFactory erweitert die Standard -ListableBeanFactory {private jetSpeedbeanDeFinitionFilterFilter; public filteringListableBeanFactory (JetSpeedbeanDefinitionFilterFilter, beanfactory parentBeanFactory) {Super (parentBeanFactory); this.filter = filter; if (this.filter == null) {this.filter = newJetSpeedbeanDefinitionFilter (); } this.filter.init (); } / ** * Überschreibung des RegistersbeanDeFinitionMethod, um optional eine BeanDefinition herauszufiltern und bei dynamischer Registrierung von Anbean alias * / public void RegisterBeandefinition (StringbeAnname, BeanDefinition BD) beandefinitionStoreException (filter.Match) (BDEFINGITIONS (FILTER.MATCH) (BANN.MATCH) (BDEFINGITIONS (FILTER.MATTER) (BANN.MATCH (BDDEFIN.) (BDEFINGITIONS (BEGISTERBAGE (BANN.MATCH)) {SUPERDEFINGITUTION (BEGISTEFINGUTION (BEGISTERBEUT) ({super. Bd); if (filter! }}}} 4. Alias die Bohne
Verwenden Sie die BeancreferenceFactoryBean Factory Bean, um die beiden oben konfigurierten Bohnen zu wickeln (XMLPAGEMANAGE und DBPAGEMANAGER). Die Tasten werden in ihre eigene angepasst, und die Implementierung besteht darin, zwischen den beiden Implementierungen zu wechseln, indem der aktuelle Schlüssel konfiguriert wird. Alle Alias werden zu einem übereinstimmen, so dass die Bohne, die sich auf ihre Bohne bezieht, den Alias nur direkt zitiert. Zum Beispiel das Pagelayoutcomponent unten.
Page-Manager.xml
<bean> <meta key="j2:cat"value="xmlPageManager" /> <meta key="j2:alias"value="org.apache.jetspeed.page.PageManager" /> <propertyname="targetBeanName" value="xmlPageManager" /> </bean> <bean> <meta key = "j2: cat" value = "dbpaGeManager" /> <meta key = "j2: alias" value = "org.apache.jetspeed.page.pageManager" /> <propertyName = "targetBeAnname" value = "dbpagemanager" /> < /ban> <bean id = "oderg.Apache.jetspeed.layout.Pagelay.Pagelay.-Pagelay.-Pagelay.-Pagelay.-Pagelay.-Pagelay.-Pagelay -" -Pagelay -"-Pagel -Id -Id -Id -Id -Id -Jet -Speed. -Layout.-Pagelay.-Pagelay. <meta key = "j2: cat" value = "default"/> <constructor-arg Index = "0"> <refbean = "org.apache.jetspeed.page.pageManager"/> </constructor-arg> <constructor-arg Index = "1"> <Wert> JetSpeed-Layouts :: VELOCITITÄT: </bean>