Mixed transactions
within the transactions of the ORM Framework's transaction manager, executing SQL using JdbcTemplate is not included in the transaction management.
The following is a source analysis to see why the JdbcTemplate must be used within the Datasourcetransactionmanager transaction.
1. Open the Business
Datasourcetransactionmanager
protected void Dobegin (Object transaction,transactiondefinition definition) {DATASOURCETRANSACTIONOBJECTT
Xobject = (datasourcetransactionobject) transaction;
Connection con = null;
try {if (txobject.getconnectionholder () = NULL | | Txobject.getconnectionholder (). Issynchronizedwithtransaction ()) {Connectionnewcon = This.dataSource.get
Connection (); if (logger.isdebugenabled ()) {Logger.debug ("acquiredconnection [+ Newcon +"] for JDBC transacti
On ");
} txobject.setconnectionholder (Newconnectionholder (Newcon), true);
} txobject.getconnectionholder (). Setsynchronizedwithtransaction (True);
Con =txobject.getconnectionholder (). getconnection ();
Integerpreviousisolationlevel = Datasourceutils.prepareconnectionfortransaction (con,definition); Txobject. Setpreviousisolationlevel (Previousisolationlevel); Switch to Manualcommit if necessary. This is very expensive into some JDBC drivers,//So we don ' t Wantto does it unnecessarily (for example if we ' ve
Explicitly//configured theconnection pool to set it already).
if (Con.getautocommit ()) {Txobject.setmustrestoreautocommit (true); if (logger.isdebugenabled ()) {Logger.debug ("switchingjdbc Connection [+ con +"] to manual Commi
T ");
} con.setautocommit (FALSE);
} txobject.getconnectionholder (). Settransactionactive (True);
int timeout =determinetimeout (definition); if (timeout!=transactiondefinition.timeout_default) {Txobject.getconnectionholder (). Settimeoutinsecon
DS (Timeout);
}//Bind the Sessionholder to the thread. if (Txobject.isnewconnectionholder ()) {Transactionsynchronizationmanager.bindresource (GETDATASOURC
E (), Txobject.getconnectionholder ());
} catch (Exception ex) {datasourceutils.releaseconnection (Con,this.datasource);
Throw Newcannotcreatetransactionexception ("Could not open JDBC Connection fortransaction", ex);
}
}
The Dobegin () method binds the database connection that opened the transaction to a threadlocal variable with the value of the data source named Key,connectionholder (Save the connection).
2. Bound connection
public static void Bindresource (Objectkey, Object value) throws IllegalStateException {Object Actualkey =
Transactionsynchronizationutils.unwrapresourceifnecessary (key);
Assert.notnull (value, "value must not being null");
Map<object, object> map = Resources.get ();
Set ThreadLocal map Ifnone found if (map = = null) {map = Newhashmap<object, object> ();
Resources.set (map);
Object OldValue = Map.put (Actualkey, value); Transparently suppress aresourceholder that is marked as void ... if (oldValue Instanceofresourceholder &
& ((Resourceholder) oldValue). Isvoid ()) {oldValue = null; } if (OldValue!= null) {throw newillegalstateexception ("already value [" + OldValue +]] for key
["+ actualkey+"] bound to thread ["+ Thread.CurrentThread (). GetName () +"]);
} if (logger.istraceenabled ()) {logger.trace ("BoundValue [+ value +"] for key ["+ Actualkey +"] to Thre
ad["+ thread.currentthread (). GetName () +"] ");
}
}
The resources variable is the threadlocal variable mentioned above, so that subsequent jdbctemplate can use DataSource as key to find the database connection.
3. Execute SQL
JdbcTemplate
Public Objectexecute (PreparedStatementCreator psc, PreparedStatementCallback action) Throwsdataaccesse
xception {assert.notnull (PSC, "PreparedStatementCreator must not to be null");
Assert.notnull (Action, "Callback object must not being null");
if (logger.isdebugenabled ()) {String SQL =getsql (PSC); Logger.debug ("executingprepared SQL statement" + (SQL!= null?)
"[+ SQL +"] ":"));
Connection con = datasourceutils.getconnection (Getdatasource ());
PreparedStatement PS = null;
try {Connection contouse= con; if (this.nativejdbcextractor!= null && This.nativeJdbcExtractor.isNativeConnectionNecessar
Yfornativepreparedstatements ()) {contouse =this.nativejdbcextractor.getnativeconnection (con);
PS =psc.createpreparedstatement (contouse); ApplystatementsettinGS (PS);
Preparedstatementpstouse = PS; if (this.nativejdbcextractor!= null) {Pstouse =this.nativejdbcextractor.getnativepreparedstatement (PS)
;
Object result =action.doinpreparedstatement (pstouse);
Handlewarnings (PS);
return result; catch (SQLException ex) {//releaseconnection early, to avoid potential connection pool Deadloc
K//In the case Whenthe exception translator hasn ' t been initialized.
if (PSC instanceofparameterdisposer) {(parameterdisposer) PSC). Cleanupparameters ();
} String SQL =getsql (PSC);
PSC = NULL;
Jdbcutils.closestatement (PS);
PS = null;
Datasourceutils.releaseconnection (Con,getdatasource ());
con = null; Throwgetexceptiontranslator (). Translate ("preparedstatementcallbAck ", SQL,EX); finally {if (PSC instanceofparameterdisposer) {(parameterdisposer) PSC). CLE
Anupparameters ();
} jdbcutils.closestatement (PS);
Datasourceutils.releaseconnection (Con,getdatasource ());
}
}
4. Get connection
datasourceutils
public static Connection Dogetconnection (Datasourcedatasource) throws SQLException {Assert.notnull (Datasou
Rce, "No DataSource specified");
Connectionholder Conholder = (connectionholder) transactionsynchronizationmanager.getresource (DataSource); if (Conholder!= null&& (conholder.hasconnection) | |
Conholder.issynchronizedwithtransaction ()) {conholder.requested ());
if (!conholder.hasconnection ()) {Logger.debug ("fetchingresumed JDBC Connection from DataSource");
Conholder.setconnection (Datasource.getconnection ());
} returnconholder.getconnection ();
/Else We either got Noholder or a empty thread-bound here.
Logger.debug ("Fetchingjdbc Connection from DataSource");
Connection con =datasource.getconnection (); if (transactionsynchronizationmanager.issynchronizationactive ()) {LoGger.debug ("Registeringtransaction Synchronization for JDBC Connection");
Use sameconnection for further JDBC actions within the transaction.
Thread-boundobject'll get removed by synchronization at transaction completion.
Connectionholderholdertouse = Conholder;
if (Holdertouse ==null) {holdertouse= new Connectionholder (con);
else {holdertouse.setconnection (con);
} holdertouse.requested (); Transactionsynchronizationmanager.registersynchronization (Newconnectionsynchronization (holderToUs
E, DataSource));
Holdertouse.setsynchronizedwithtransaction (TRUE);
if (Holdertouse!=conholder) {transactionsynchronizationmanager.bindresource (datasource,holdertouse);
} return con;
}
This shows that Datasourceutils is also through the Transactionsynchronizationmanager to get connected. So as long as JdbcTemplate and Datasourcetransactionmanager have the same datasource, you will be able to get the same database connection, nature can correctly submit, rollback TRANSACTION.
Take hibernate as an example to illustrate the problems mentioned at the beginning, and see why the ORM Framework's transaction manager cannot manage JdbcTemplate.
5 ORM Transaction Manager
Hibernatetransactionmanager
if (Txobject.isnewsessionholder ()) {Transactionsynchronizationmanager.bindresource (Getsessionfactory ())
, Txobject.getsessionholder ());
}
Because the ORM framework does not directly inject datasource into the TransactionManager, it does the same things as the Hibernate transaction manager above, using its own sessionfactory objects to manipulate datasource. So while it may be that the sessionfactory and JdbcTemplate are the same data sources, the different keys are used in the transactionsynchronizationmanager because of the binding One is the sessionfactory name, the other is the DataSource name), so JdbcTemplate does not get the connection to the database on which the ORM transaction manager opened the transaction when executing.
Bean distinction
A spring configuration file in a public project that may be referenced by multiple projects. Because each project may require only a subset of the beans in the public works, the spring container for these projects is started with the need to separate which beans are created.
1. Application instance
in an example configuration in the Apache open source Framework jetspeed: 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 orpageserializer"/> <constructor-arg index= "0" > <ref bean= "idgenerator"/> </constructor-arg> <constructor-arg index= "1" > <ref bean= "Xmldocumenthandlerfactory"/> </constructor-arg> ... </bean> <bean id= "Dbpagemanager" class= "Org.apache.jetspeed.page.impl.DatabasePageManager" init-method= "Init" destroy-method= "destroy" > <meta key= " J2:cat "value=" Dbpagemanager orpageserializer "/> <!--ojb configuration file ResourcePath--> <constructo R-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>
When the
2.Bean filter
jetspeedbeandefinitionfilter resolves each bean definition in the spring container, it takes out the j2:cat corresponding value in the above bean configuration. such as Dbpagemanageror Pageserializer. This section is then matched as a regular expression with the current key (read out from the configuration file). Only the matching bean will be created by the spring container.
Jetspeedbeandefinitionfilter
public boolean match (beandefinition BD) {String beancategoriesexpression = (Str
ing) bd.getattribute (category_meta_key);
Boolean matched = true; if (beancategoriesexpression!= null) {matched = (Matcher!= null) && Matcher.match (beancategoriesexpre
Ssion));
return matched; public void Registerdynamicalias (Beandefinitionregistry registry, string beanname,beandefinition BD) {string
aliases = (String) bd.getattribute (Alias_meta_key);
if (aliases!= null) {StringTokenizer st = Newstringtokenizer (aliases, ",");
while (St.hasmoretokens ()) {String alias = St.nexttoken ();
if (!alias.equals (Beanname)) {Registry.registeralias (beanname, alias); }
}
}
}
The value of the Category_meta_key in the match () method is that the current key is saved in the J2:cat,matcher class and is responsible for matching the current key with the regular expression for each bean.
The Registerdynamicalias function is that after the bean match succeeds, the custom spring container calls this method to register the alias for the Bean. See the source code in the 1.3 below.
3. Custom Spring Container
Customize a spring container, override the Registerbeandefinition () method, and intercept when spring registers the bean.
public class Filteringxmlwebapplicationcontextextends Xmlwebapplicationcontext {private
Jetspeedbeandefinitionfilterfilter; Publicfilteringxmlwebapplicationcontext (jetspeedbeandefinitionfilter filter, string[]configlocations, Properties
InitProperties, ServletContext ServletContext) {This (filter, configlocations,initproperties, ServletContext, NULL); Publicfilteringxmlwebapplicationcontext (jetspeedbeandefinitionfilter filter, string[]configlocations, Properties
InitProperties, ServletContext servletcontext,applicationcontext parent) {super ();
if (parent!= null) {this.setparent (parent);
} if (InitProperties!= null) {propertyplaceholderconfigurer PPC =new propertyplaceholderconfigurer ();
Ppc.setignoreunresolvableplaceholders (TRUE);
Ppc.setsystempropertiesmode (Propertyplaceholderconfigurer.system_properties_mode_fallback);
Ppc.setproperties (InitProperties);
Addbeanfactorypostprocessor (PPC);
} Setconfiglocations (configlocations);
Setservletcontext (ServletContext);
This.filter = filter; } protected Defaultlistablebeanfactorycreatebeanfactory () {return new filteringlistablebeanfactory (Filter,geti
Nternalparentbeanfactory ()); } public Classfilteringlistablebeanfactory extends Defaultlistablebeanfactory {private Jetspeedbeandefinitionfilter
Filter Public Filteringlistablebeanfactory (Jetspeedbeandefinitionfilterfilter, Beanfactory parentbeanfactory) {super (pare
Ntbeanfactory);
This.filter = filter;
if (This.filter = = null) {This.filter = Newjetspeedbeandefinitionfilter ();
} this.filter.init (); }/** * Override of the Registerbeandefinitionmethod to optionally filter out a beandefinition and * if Requeste D Dynamically register Anbean alias */public void registerbeandefinition (Stringbeanname, beandefinition BD) th
Rows Beandefinitionstoreexception {if (Filter.match (BD)) {Super.registerbeandefinition (Beanname, BD);
if (filter!= null) {Filter.registerdynamicalias (this, Beanname, BD);
}
}
}
}
4. Alias for Bean
using the Beanreferencefactorybean factory bean, the two beans (Xmlpagemanager and Dbpagemanager) configured above Wrap it up. The key is assigned to each of the two implementations by configuring the current key. Aliases are all in one, so referencing their bean directly refers to the alias. For example, the following pagelayoutcomponent.
Page-manager.xml
<bean class= "Org.springframework.beans.factory.config.BeanReferenceFactoryBean" > <meta key= "j2:cat" value = "Xmlpagemanager"/> <meta key= "J2:alias" value= "Org.apache.jetspeed.page.PageManager"/> <propertyname= "Targetbeanname" value= "Xmlpagemanager"/> </bean> <bean class= " Org.springframework.beans.factory.config.BeanReferenceFactoryBean "> <meta key=" j2:cat "value=" Dbpagemanager "/> <meta key=" J2:alias value= "Org.apache.jetspeed.page.PageManager"/> <propertyname= "Targetbeanname" Value= "Dbpagemanager"/> </bean> <bean id= "org.apache.jetspeed.layout.PageLayoutComponent" class= "ORG.A" Pache.jetspeed.layout.impl.PageLayoutComponentImpl "> <meta key=" j2:cat "value=" Default "/> < Constructor-arg index= "0" > <refbean= "Org.apache.jetspeed.page.PageManager"/> </constructor-arg> ; Constructor-arg index= "1" > <value>jetspeed-layouts::VelocityOneColumn</value> </constructor-arg> </bean>