Transacciones mixtas
En las transacciones del administrador de transacciones del marco de ORM, el uso de JDBCTemplate para ejecutar SQL no se incluirá en la gestión de transacciones.
El siguiente es un análisis del código fuente para ver por qué JDBCTemplate debe usarse dentro de la transacción DataSourCetransactionManager.
1. Iniciar transacciones
DataSourCetransactionManager
Dobegin vacío protegido (transacción de objeto, definición de transacciónDefinition) {dataSourCetransactionObjectTXObject = (DataSourCetransactionObject) transacción; Conexión con = nulo; Pruebe {if (txObject.getConnectionHolder () == NULL || txObject.getConnectionHolder (). IsSynChronizedWithTransaction ()) {ConnectionNewCon = this.datasource.getConnection (); if (logger.isDebugeNabled ()) {logger.debug ("adquiredConnection [" + newcon + "] para la transacción JDBC"); } txObject.SetConnectionHolder (NewConnectionHolder (NewCon), verdadero); } txObject.getConnectionHolder (). SetSynChronizedWithTransaction (true); con = txObject.getConnectionHolder (). getConnection (); IntegerPreviousIsolationLevel = DataSourceutil.prepareConnectionForTransaction (Con, Definición); txObject.setPreviousIsolationLevel (previoisolationLevel); // Cambie a ManualCommit si es necesario. Esto es muy costoso en algunos controladores JDBC, // por lo que no queremos hacerlo innecesariamente (por ejemplo, si hemos configurado explícitamente // el grupo de ECONNECE para configurarlo). if (con.getAutOcomMit ()) {txObject.setMustrestoreaUtOcommit (true); if (logger.isDebugeNabled ()) {logger.debug ("Conexión SwitchingJDBC [" + Con + "] para confirmar manual"); } con.setAutOcommit (falso); } txObject.getConnectionHolder (). SetTransactionActive (true); int timeout = determinEttimeOut (definición); if (timeOut! = TransActionDefinition.TimeOut_Default) {txObject.getConnectionHolder (). SetTimeOutInSeconds (Timeout); } // vincule el titular de la sesión al hilo. if (txObject.ISNewConnectionHolder ()) {TransactionSynChronizationManager.Bindresource (getDataSource (), txObject.getConnectionHolder ()); }} Catch (Exception Ex) {dataSourceUtilss.ReleaseConnection (con, this.dataSource); tirar newCannotCreateTransactionException ("no pudo abrir la conexión JDBC Forttransaction", ex); }}El método Dobegin () utilizará la clave de nombre de origen de datos y el tolde de conexión como valor, y vinculará la conexión de la base de datos que se ha abierto a una variable de ThreadLocal.
2. Ate la conexión
public static void bindResource (ObjectKey, valor de objeto) arroja ilegalStateException {object realkey = transaccionesynchronizationUtilss.unwraPresourceifneceSary (clave); Afirmar.notnull (valor, "el valor no debe ser nulo"); Map <objeto, objeto> map = recursos.get (); // Establecer mapa ThreadLocal ifnone encontrado if (map == null) {map = newhashmap <object, object> (); recursos.set (mapa); } Objeto OldValue = map.put (realKey, valor); // suprime transparentemente a AreSourceholder que estaba marcado como void ... if (OldValue Instance OfResourceHolder && ((ResourceHolder) OldValue) .ISVoid ()) {OldValue = null; } if (OldValue! = NULL) {Throw NewILLegalStateException ("ya valor [" + OldValue + "] para la clave [" + realKey + "] Bound to Thread [" + Thread.CurrentThread (). GetName () + "]"); } if (logger.istraceEnabled ()) {logger.trace ("boundValue [" + value + "] para la clave [" + realkey + "] a hilo [" + thread.currentThread (). getName () + "]"); }}La variable de recursos es la variable de ThreadLocal mencionada anteriormente, de modo que el JDBCTemplate posterior puede usar DataSource como la clave para encontrar la conexión de la base de datos.
3. Ejecutar SQL
JDBCTemplate
Public ObjectExecute (PrepareStatementCreator PSC, PrepareStatementCallback Action) ShowsDataAccessException {ASSERT.NOTNULL (PSC, "PrepareStatementCreator no debe ser nulo"); Afirmar.notnull (acción, "El objeto de devolución de llamada no debe ser nulo"); if (logger.isDebugeNabled ()) {String sql = getsql (psc); logger.debug ("Ejecutación de la instrucción SQL SQL" + (SQL! = NULL? "[" + SQL + "]": "")); } Connection Con = DataSourceUtils.getConnection (getDataSource ()); Preparado PS = NULL; intente {Connection contouse = con; if (this.nativejdbcextractor! = null && this.nativejdbcextractor.isnativeconnectionNecessaryFornativePreparedStatements ()) {contouse = this.nativejdbcextractor.getnateVeconnection (con); } PS = PSC.CreatePreparedStatement (controuse); Aplicar StatementSettings (PS); PrepareStatementspstouse = ps; if (this.nativeJDBCExtractor! = null) {pstouse = this.nativeJDBCExtractor.getNativePreparedStatement (PS); } Objeto resultado = Action.DoInPreparedStatement (pstouse); Handlewarnings (PS); resultado de retorno; } Catch (SQLException ex) {// Releaseconnection temprano, para evitar un posible punto muerto del grupo de conexión // en el caso de que el traductor de excepción aún no se haya inicializado. if (PSC instanceOfParameterDisposer) {((ParameterDisposer) PSC) .CleanupparAmeters (); } String sql = getsql (psc); PSC = nulo; Jdbcutils.Closestatement (PS); ps = nulo; DataSourceUtils.RelEASECOnnection (con, getDataSource ()); con = nulo; throwGetExceptionTranslator (). Traduce ("PrepareStatementCallback", SQL, Ex); } Finalmente {if (PSC InstaleOfParameterDisposer) {((ParameterDisposer) PSC) .CleanupparAmeters (); } Jdbcutils.cloSestatement (ps); DataSourceUtils.RelEASECOnnection (con, getDataSource ()); }}
4. Obtenga una conexión
DataSourceutils
Public Static Connection dogetConnection (DataSourcedataSource) lanza SQLException {Afirman.NotNull (DataSource, "No se especificó DataSource"); ConnectionHolder Conholder = (ConnectionHolder) TransactionsynCronizationManager.getResource (DataSource); if (conholder! = null && (conholder.hasconnection () || conholder.issynchronizedWithTransaction ())) {conholder.requested (); if (! conholder.hasconnection ()) {logger.debug ("conexión JDBC de fetchingResumed desde dataSource"); conholder.setConnection (dataSource.getConnection ()); } returnConholder.getConnection (); } // De lo contrario, no tenemos titular o un titular de hilo vacío aquí. logger.debug ("FetchingJDBC conexión desde DataSource"); Conexión con = dataSource.getConnection (); if (TransactionSynChronizationManager.issynChronizationActive ()) {logger.debug ("RegistringTransaction Syncronization para la conexión JDBC"); // Use sameconnection para nuevas acciones de JDBC dentro de la transacción. // hilo-boundObject se eliminará por sincronización al finalizar la transacción. ConnectionHolderHolderTouse = conholder; if (holdenTouse == null) {holderTouse = new ConnectionHolder (Con); } else {holderTouse.setConnection (con); } holdenTouse.Requested (); TransactionSynCronizationManager.Registroyncronization (NewConnectionSynchronization (holderTouse, DataSource)); holdenTouse.setsynchronizedWithTransaction (verdadero); if (holderTouse! = conholder) {TransactionSynChronizationManager.bindresource (DataSource, HoldTouse); }} return Con; } Se puede ver que los datos de datos también obtienen conexión a través de TransactionSynCronizationManager. Por lo tanto, siempre que JDBCTemplate y DataSourCetransactionManager tengan la misma fuente de datos, definitivamente obtendrá la misma conexión de la base de datos y, naturalmente, puede enviar y retirar las transacciones correctamente.
Tomemos Hibernate como un ejemplo para ilustrar el problema mencionado al principio, y ver por qué el administrador de transacciones del marco ORM no puede administrar JDBCTemplate.
5 Manager de transacción ORM
HibernatetransactionManager
if (txObject.ISNeWSessionHolder ()) {TransactionSynChronizationManager.Bindresource (getSessionFactory (), txObject.getSessionHolder ()); }Debido a que el marco de ORM no inyecta directamente los datos de datos en TransactionManager para su uso, pero usa su propia sesión de SessionFactory y otros objetos para operar DataSource, al igual que el Administrador de transacciones Hibernate anterior. Entonces, aunque la fuente de datos subyacente de SessionFactory y JDBCTemplate puede ser la misma, debido a que se usan diferentes claves al enlace en TransactionSynCronizationManager (uno es el nombre de SessionFactory y el otro es el nombre de DataSource), JDBCTEMPLATE no puede obtener la conexión de base de datos que el administrador de transacciones ORM comienza la transacción cuando se ejecuta.
La distinción entre frijoles
El archivo de configuración de primavera en un proyecto público puede ser referenciado por múltiples proyectos. Debido a que cada proyecto solo puede requerir una parte de los frijoles en el proyecto público, cuando se inicia el contenedor de primavera de estos proyectos, es necesario distinguir qué frijoles se crean.
1. Ejemplos de aplicación
Tomar una configuración en JetSpeed, un marco de código abierto de Apache, como ejemplo: Page-Manager.xml
<bean name = "xmlpageManager" class = "org.apache.jetspeed.page.psml.castorxmlpagemanager" init-method = "init" destruye-method = "destruye"> <meta key = "j2: cat" value = "xmlpagemanager opagesserializer" /> <constructor-arg índice = "0"> <reflexererererererererererer </constructor-arg> <constructor-arg index = "1"> <refbean = "xmlDocumentHandlerFactory"/> </ constructor-arg> ... </bean> <bean id = "dbpageManager" class = "org.apache.jetspeed.page.imppl.dataBasepageMager" initMethod = "inity" destruye "destruye" destruye "destruye" destruye "destruye" destruye "destruye" destruye "destruye" destruye "destruye" destruye "destruye" destruye "destruye" destruye "destruye" destruye "destruye" destruye "destruye". Key = "J2: Cat" Value = "DBPAGEMAGER ORPAGESERIALIZER"/> <!-OJB FILE DE CONFIGURACIÓN ResourcePath-> <Constructor-Arg index = "0"> <value> jetspeedinf/ojb/page-manager-Repository.xml </valor> </structor-arg> <!-fragment id-> <constructor- "Índice- 1" Índice- "1". <ref Bean = "IdGenerator"/> </Constructor-Arg> ... </bean>
2. Filtro de abogado
Cuando el JetSpeedBeanDefinitionFilter analiza cada definición de frijoles en el contenedor de resorte, eliminará el valor correspondiente a J2: CAT en la configuración de frijoles anterior, como DBPageManager PageSerializer. Esta parte se combina como una expresión regular con la clave actual (lea desde el archivo de configuración). Solo los frijoles en la coincidencia serán creados por el contenedor de resorte.
JetSpeedBeanDefinitionFilter
Public Boolean Match (beandefinition bd) {string beanCategoriesExpression = (string) bd.getAttribute (category_meta_key); boolean coincidente = verdadero; if (beanCategoriesExpression! = NULL) {Matched = ((Matcher! = NULL) && Matcher.Match (BeanCategoriesExpression)); } 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); }}}} El valor de category_meta_key en el método Match () es J2: Cat. La clave actual se guarda en la clase Matcher, y es responsable de hacer coincidir la clave actual con la expresión regular de cada frijol.
El papel de RegistroDynamicalias es: después de la coincidencia de frijoles con éxito, el contenedor de resorte personalizado llamará a este método para registrar un alias para el bean. Para más detalles, consulte el código fuente en 1.3 a continuación.
3. Personalizar el contenedor de primavera
Personalice un contenedor de resorte, anule el método de registroBeanDefinition () e intercepte cuando Spring registra un frijol.
Public Class FilteringXMlWebApplicationContextends xmlwebApplicationContext {private jetspeedBeanDefinitionFilterFilter; PublicFilteringXmlWebApplicationContext (JetSpeedBeanDefinitionFilter Filter, String [] configLocations, Propiedades initProperties, servletContext servletContext) {this (filtre, configlocations, initProperties, servletcontext, null); } publicfilteringxmlWebApplicationContext (JetSpeedBeanDefinitionFilter Filter, String [] configLocations, Propiedades initProperties, servletContext ServletContext, ApplicationContext Parent) {super (); if (parent! = null) {this.setParent (parent); } if (initProperties! = NULL) {PropertyPlaceHolderConfigurer ppc = new PropertyPlaceHolderConfigurer (); ppc.setigneunresolvablePlaceHolders (verdadero); ppc.setsystemPropertiesMode (PropertyPlaceHolderConfigurer.system_properties_mode_fallback); ppc.setProperties (initProperties); addBeanFactoryPostProcessor (PPC); } setConfigLocations (configlocations); setServletContext (servletContext); this.filter = filtro; } protegido defaultListableBeanFactoryCreateBeanFactory () {return New FilterInsListableBeanFactory (filtro, getInternalParentBeanFactory ()); }} public classFilteringListableBeanFactory extiende defaultListableBeanFactory {private jetSpeedBeanDefinitionFilterFilter; Public FilteringListableBeanFactory (JetSpeedBeanDefinitionFilterFilter, BeanFactory ParentBeanFactory) {super (ParentBeanFactory); this.filter = filtro; if (this.filter == null) {this.filter = newJetspeedBeanDefinitionFilter (); } this.filter.init (); } / ** * anulación del registroBeanDefinitionMethod para filtrar opcionalmente un beandefinition y * si se solicita dinámicamente anias alias * / public void registreBeanDefinition (stringBeanName, beandefinition bd) lanza beandefinitionstoreException {if (filtre.match (bd)) {super.regeNdefinition (beay, beanException, beheexception, beheexception (bd)) if (filtre! = null) {filter.registerDynamicalias (this, beanName, bd); }}}} 4. Alias el frijol
Use el frijol de fábrica BeanReferenceFactoryBean para envolver los dos frijoles configurados anteriormente (XMLPAGEMAGERAGER y DBPAGEMAGERAGER). Las claves coinciden con la suya, y la implementación es cambiar entre las dos implementaciones configurando la clave actual. Todos los alias coinciden con uno, de modo que el frijol que se refiere a su frijol solo cita el alias directamente. Por ejemplo, el PagelayoutComponent a continuación.
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" /> < /bean> <bean id = "org.apache.JetSpeed.Layout.pageLayoutComponent"> <meta key = "j2: j2:" value " /" value " /" value " /" <constructor-arg index = "0"> <refbean = "org.apache.jetspeed.page.pagemanager"/> </ constructor-arg> <constructor-arg index = "1"> <value> jetspeed-layouts :: velocityOnecolumn </al valor> </ constructor-arg> </Bean>