Transações mistas
Nas transações do gerenciador de transações do ORM Framework, o uso do JDBCTemplate para executar o SQL não será incluído no gerenciamento de transações.
A seguir, é apresentada uma análise de código -fonte para ver por que o JDBCTemplate deve ser usado na transação do DataSourCetransactionManager.
1. Iniciar transações
DataSourCetransactionManager
vazio protegido DOBEGIN (Transação de Objeto, Definição de TransactionDefinition) {DataSourCetransactionObjectTxObject = (DataSourCetransactionObject) Transação; Conexão con = null; tente {if (txObject.getConnectionHolder () == null || txObject.getConnectionHolder (). IssynchronizedWithTransaction ()) {ConnectionNewCon = this.dataSource.getConnection (); if (logger.isdebugenabled ()) {Logger.debug ("adquiriuConnection [" + newcon + "] para transação JDBC"); } txobject.setConnectionHolder (NewConnectionHolder (newCon), true); } txObject.getConnectionHolder (). SetsynchronizedWithTransaction (true); con = txObject.getConnectionHolder (). getConnection (); IntegerPreviousisolationLEvel = DataSourceutils.PreparEConnectionForTransaction (Con, definição); txObject.SetPreviousisolationLEvel (anteriorisolationLEvel); // Mude para ManualComit, se necessário. Isso é muito caro em alguns drivers do JDBC, //, então não queremos fazê -lo desnecessariamente (por exemplo, se já estamos explicitamente // configuramos o pool de reconhecimento para defini -lo). if (CO.GETAUTOCOMIT ()) {TXOBJET.SETMUSTERSTOREAUTOCOMIT (true); if (logger.isdebugenabled ()) {logger.debug ("switchingjdbc conexão [" + con + "] para comprometimento manual"); } Con.SetAutocomit (false); } txObject.getConnectionHolder (). SettransactionActive (true); int timeout = determineTimeout (definição); if (timeout! = transactionDefinition.Timeout_Default) {txobject.getConnectionholder (). setTimeoutInsEconds (timeout); } // Ligue o titular ao thread. if (txObject.isNewConnectionHolder ()) {transactionsyChronizationManager.bindResource (getDataSource (), txObject.getConnectionHolder ()); }} catch (Exceção ex) {DataSourceUtils.ReleasEConnection (con, this.DataSource); lançar newCannotCreateTransactionException ("não pôde abrir a conexão JDBC forTransaction", ex); }}O método DOBEGIN () usará a chave do nome da fonte de dados e o conexão como valor e vinculará a conexão do banco de dados que foi aberta a uma variável Threadlocal.
2. Ligue a conexão
public static void bindResource (objectKey, valor do objeto) lança ilegalStateException {object realKey = transactionsynchronizationutils.unwresResourceifneCessary (key); Assert.NotNull (valor, "valor não deve ser nulo"); Mapa <objeto, objeto> map = Resources.get (); // Definir mapa Threadlocal ifnone encontrado if (map == null) {map = newHashmap <object, object> (); Resources.Set (mapa); } Objeto antigoValue = map.put (realKey, valor); // suprime de forma transparente a Aresourceholder que foi marcada como vazia ... if (instância antiga de valor do resultante de origem && ((resourceholder) OldValue) .isvoid ()) {OldValue = null; } if (OldValue! = NULL) {lança newilleGalStateException ("já valor [" + OldValue + "] para key [" + realKey + "] vinculado a thread [" + Thread.currentThread (). getName () + "]"); } if (logger.istraceEnabled ()) {Logger.Trace ("BoundValue [" + value + "] para key [" + realKey + "] para Thread [" + Thread.currentThread (). getName () + "]"); }}A variável de recursos é a variável Threadlocal mencionada acima, para que o JDBCTemplate subsequente possa usar o DataSource como a chave para encontrar a conexão do banco de dados.
3. Execute sql
JDBCTemplate
Public ObjectExecute (preparado PSC PSCCORACOR, Ação Preparado de EstatementCallback) ThrowsDataAccessException {Assert.NotNull (PSC, "PreparadostatementCreator não deve ser nulo"); Assert.NotNull (Action, "Objeto de retorno de chamada não deve ser nulo"); if (logger.isdebugenabled ()) {string sql = getsql (psc); Logger.debug ("ExecutingPrepared SQL Declarent" + (sql! = null? "[" + sql + "]": "")); } Conexão con = datasourceutils.getConnection (getDataSource ()); Preparado estatement ps = null; tente {conexão contouse = con; if (this.nativejdbcextractor! = null && this.nativejdbcextractor.isnativeConnectionnectionNeCesaryFornativePreparedStatements ()) {contouse = this.nativejdbcextractor.getnativeConnection (con); } ps = psc.createpreeparedStatement (contouse); aplicar estatamentos (ps); PreparedStatementsPstouse = ps; if (this.nativejdbcextractor! = null) {pstouse = this.nativejdbcextractor.getnativepreparedStatement (ps); } Resultado do objeto = action.doinpresedStatement (Pstouse); Handlewarnings (ps); resultado de retorno; } Catch (SqLException Ex) {// RELEASECONNECTION EXCENDIMENTO, para evitar possíveis impulsos de conexão // no caso em que o tradutor de exceção ainda não foi inicializado. if (PSC InstânciaofparameterDisposer) {((parameterDisposer) PSc) .cleanuppparameters (); } String sql = getsql (psc); PSC = NULL; Jdbcutils.cloSestatement (ps); ps = null; Dados con = null; throwGetexceptionTranslator (). Tradlate ("preparadostatementCallback", sql, ex); } finalmente {if (PSC InstanceofparameterDisposer) {((parameterDisposer) PSc) .CleanupPparameters (); } Jdbcutils.cloSestatement (ps); Dados }}
4. Obtenha uma conexão
DataSourceutils
A conexão estática pública DogetConnection (DataSourcedataSource) lança SQLEXCIPCECCETION {Assert.NotNull (DataSource, "Nenhum DataSource especificado"); Conectionsholder Condholder = (ConnectionHolder) TransactionsynchronizationManager.GetResource (DataSource); if (CONHOINTER! = NULL && (CONHOINTER.HASCONNECTION () || CONHOINTER.ISSYNCHRONIZEDWITHTRANSACION ())) {CONHOINTER.Requested (); if (! CONHOINTER.HASCONNECTION ()) {LOGGER.DEBUG ("FetchingResumed JDBC Connection from DataSource"); CONHOINTER.SETCONNECTION (DataSource.getConnection ()); } returnCoholder.getConnection (); } // Caso contrário, não possuímos um titular ou um suporte vazio de roscas aqui. Logger.debug ("Fetchingjdbc conexão do DataSource"); Conexão con = DataSource.getConnection (); if (transactionsyChronizationManager.issynchronizationActive ()) {Logger.debug ("Sincronização do registroTransaction para conexão JDBC"); // Use o sameConnection para mais ações JDBC dentro da transação. // Thread-BoundObject será removido por sincronização na conclusão da transação. ConnectionHolderHolderouse = CONHOINTER; if (holderTouse == null) {holderTouse = new ConnectionHolder (con); } else {holderTouse.setConnection (con); } holderTouse.requested (); Transactionsynchronizationmanager.registersynchronization (newConnectionSynchronization (HolderTouse, DataSource)); HolderTouse.SetSynchronizedWithTransaction (true); if (holderTouse! = CONHOINTER) {transactionsynchronizationManager.bindResource (DataSource, HolderTouse); }} retornar con; } Pode -se observar que o DataSourceutils também obtém a conexão através do TransactionsyChronizationManager. Portanto, desde que o JDBCTemplate e o DataSourCetransActionManager tivessem o mesmo DataSource, você definitivamente obterá a mesma conexão de banco de dados e, naturalmente, poderá enviar e reverter as transações corretamente.
Vamos tomar o Hibernate como um exemplo para ilustrar o problema mencionado no início e ver por que o gerente de transação da estrutura ORM não pode gerenciar o JDBCTemplate.
5 ORM Transaction Manager
HibernateTransactionManager
if (txObject.isNewSessionHolder ()) {transactionsynchronizationManager.bindResource (getSessionFactory (), txObject.getSessionHolder ()); }Como a estrutura do ORM não injeta diretamente o DataSource no TransactionManager para uso, mas usa seu próprio sessão e outros objetos para operar o DataSource, assim como o Hibernate Transaction Manager acima. Portanto, embora a fonte de dados subjacente de SessionFactory e JDBCTemplate possa ser a mesma, porque as teclas diferentes são usadas quando se vincula no TransactionsyChronizationManager (um é o nome da SessionFactory e o outro é o nome da fonte de dados), o JDBCTemplate não pode obter a conexão do banco de dados que o gerente de transação ORM inicia a transação durante a execução.
A distinção entre feijões
O arquivo de configuração da primavera em um projeto público pode ser referenciado por vários projetos. Como cada projeto pode exigir apenas uma parte do feijão no projeto público, quando o recipiente da mola desses projetos é iniciado, é necessário distinguir quais grãos a serem criados.
1. Exemplos de aplicação
Tomando uma configuração no Jetspeed, uma estrutura de código aberto do Apache, como exemplo: página-manager.xml
<bean name = "xmlpagemanager" class = "org.apache.jetspeed.page.psml.castorxmlpagemanager" init-method = "init" de destruir-method = "destruir"> <meta key = "j2: cat" value = "xmlpagemanager"> <meta key " /" construtor "" </construtor-arg> <construtor-arg index = "1"> <refbean = "xmldocumentHandlerFactory"/> </construtor-arg>… </shean> <bean id = "dbpagemanager" class = "org.apache.jetspeed.page.iSPL.DATASEPAGANGER" " key = "j2: cat" value = "dbpagemanager orpageSerializer"/> <!-OJB Configuration File Resourcepath-> <construtor-arg index = "0"> <Value> jetspeed-inf/ojb/page-manager-repositiony.XML </value> </construtor-arg> <! <ref bean = "idGenerator"/> </construtor-arg>… </sien>
2. Filtro da faixa
Quando o JetspeedBeandEfinitionfilter analisar cada definição de feijão no recipiente da mola, ele retirará o valor correspondente a J2: CAT na configuração do feijão acima, como o DBPAGEMANAGEROR PAGESERILIZER. Esta parte é então correspondente como uma expressão regular à chave atual (leia -se no arquivo de configuração). Somente o feijão na correspondência será criado pelo recipiente da mola.
JetspeedbeanDefinitionFilter
Public Boolean Match (BeandEfinition bd) {String beancategoriesExpression = (String) bd.getAttribute (category_meta_key); booleano correspondido = true; if (beanscategoriesExpression! = null) {correspondente = ((matcher! = null) && Matcher.match (beancategoriesExpression)); } return correspondido;} public void Registerdynamicias (Registro de Registro de Registro BeandEfinition, String Beanname, BeandEfinition bd) {Aliases da String = (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); }}}} O valor do category_meta_key no método Match () é J2: CAT. A chave atual é salva na classe Matcher e é responsável por corresponder a chave atual com a expressão regular de cada feijão.
O papel do Registerdynamicalias é: Após a partida do feijão com sucesso, o contêiner de mola personalizado chamará esse método para registrar um alias para o feijão. Para detalhes, consulte o código -fonte em 1.3 abaixo.
3. Personalize o recipiente de mola
Personalize um contêiner de mola, substitua o método RegisterBeanDefinition () e interceptá -lo quando a mola registrar um feijão.
classe pública filteringxmlwebApplicationContextends xmlwebApplicationContext {private jetspeedBeandEfinitionFilterFilter; publicFilteringxmlWebApplicationContext (filtro JetSpeedBeanDefinitionFilter, String [] Configlocations, Propriedades initProperties, servletContext servletContext) {this (filtra, configlocações, initroperties, servletContext) {this (filtra, configlocações, initroperties, servletContext, null); } publicFilteringxmlWebApplicationContext (filtro JetSpeedBeanDefinitionFilter, String [] Configlocations, Propriedades initProperties, ServletContext servletContext, ApplicationContext parent) {super (); if (pai! = null) {this.setParent (pai); } if (initProperties! = null) {PropertyPlaceHoldConfigurer ppc = new PropertyPlaceHoldConfigurer (); ppc.setignoreunResolvable place shols (true); ppc.SetSystemPropertiesMode (PropertyPlaceHoldConfigurer.system_properties_mode_fallback); ppc.setProperties (initProperties); addBeanFactoryPostProcessor (PPC); } setConfiglocations (configlocations); setServletContext (ServletContext); this.filter = filtro; } protegido defaultListableBeanFactoryCreateBeanFactory () {return New FilterLeTListableBeanFactory (filtro, getInternalParentBeanFactory ()); }} public ClassFilteringListableBeanFactory estende o DefaultListableBeanFactory {private jetspeedBeandEfinitionFilterFilter; public filteringListableBeanFactory (JetspeedBeanDefinitionFilterFilter, BeanFactory ParentBeanFactory) {super (ParentBeanFactory); this.filter = filtro; if (this.Filter == null) {this.filter = newJetspeedBeandEfinitionFilter (); } this.filter.init (); } / ** * Substituição do registroBeandEfinitionMethod para filtrar opcionalmente uma beandefinição e * se solicitado registrar dinamicamente alias de anbean * / public void RegisterBeandEfinition (StringBeanName, beandEfinition bd) lança beandefination {{if (filter.match (filter.match (bd) bd) bd) bd) bd) bd) bdenTeAnTeAnTeAnTeAnDen (BDOTENDENDEN) (BD) Bd) BdenTeEnTeEnTeEnTeEnTeAnDen (BDOTENDENDEN) (BDEMTENDENDENDENDEN) bd); if (filtro! = null) {filter.RegisterDynamicalias (this, beanname, bd); }}}} 4. Alias do feijão
Use o BeanReferenceFactoryBean Factory Bean para embrulhar os dois feijões configurados acima (XMLPAGEMANAGER e DBPAGEMANAGER). As chaves são combinadas por conta própria, e a implementação é alternar entre as duas implementações configurando a chave atual. Todos os alias são comparados a um, de modo que o feijão que se refere ao seu feijão está apenas citando o pseudônimo diretamente. Por exemplo, o PageLayoutComponent abaixo.
página-manager.xml
<Bean> <meta key = "j2: gat" value = "xmlpagemanager" /> <meta key = "j2: alias" value = "org.apache.jetspeed.page.pageManager" /> < /beanname = "TargetBeanname" Value "xmlpageManager" /> </rient key = "j2: cat" value = "dbpageManager" /> <meta key = "j2: alias" value = "org.apache.jetspeed.page.pagemanager" /> <er propertName = "TargetBeanName" Value = "dbpagemanager" /> </40) <meta key = "j2: cat" value = "default"/> <construtor-arg index = "0"> <refbean = "org.apache.jetpeed.page.pagemanager"/> </construtor-arg> <construtor-arg Índice = 1 "> <tal-> jetsPeed-layoutS: VELUCTIDADE> </ Bean>