Смешанные транзакции
В транзакциях менеджера транзакций ORM Framework, использование JDBctemplate для выполнения SQL не будет включено в управление транзакциями.
Ниже приведен анализ исходного кода, чтобы понять, почему JDBCTemplate должен использоваться в рамках транзакции DataSourCetransActionManager.
1. Начать транзакции
DataSourcetransactionManager
Защищенная void dobegin (объектная транзакция, определение транзакции) {dataSourCetransActionObjectTxObject = (dataSourCetransActionObject) транзакция; Соединение con = null; try {if (txobject.getConnectionholder () == null || txobject.getConnectionholder (). IssynchronizedWithTransaction ()) {connectionNewcon = this.datasource.getConnection (); if (logger.isdebugenabled ()) {logger.debug ("upedConnection [" + newCon + "] для транзакции JDBC"); } txobject.setConnectionholder (NewConnection Holder (NewCon), True); } txobject.getConnectionholder (). SetSynchronizedWithTransaction (true); con = txobject.getConnectionholder (). getConnection (); IntegerPreviousisolationLevel = dataSourceutils.prepareConnectionForTransaction (con, определение); txobject.setPreviousisolationLevel (предыдущий isolationLevel); // Переключиться на ManualCommit, если это необходимо. Это очень дорого у некоторых драйверов JDBC, //, поэтому мы не хотим делать это без необходимости (например, если мы явно // сконфигурировали пул TheConnection, чтобы установить его уже). if (con.getautocommit ()) {txobject.setmustrestoreAutocommit (true); if (logger.isdebugenabled ()) {logger.debug ("ClackingJdbc Connection [" + con + "] к ручному коммиту"); } con.setautocommit (false); } txobject.getConnectionholder (). SetTransactionActive (true); int timeout = detrinetimeout (определение); if (timeout! = transactionDefinition.timeout_default) {txobject.getConnectionholder (). SetTimeOutInseconds (Timeout); } // Связывайте владельца сеанса с потоком. if (txobject.isnewConnectionholder ()) {TransactionSynchronizationManager.Bindresource (getDataSource (), txobject.getConnectionholder ()); }} catch (Exception ex) {dataSourceUtils.ReleAseConnection (con, this.datasource); бросить NewCannotCreateTransactionException («Не удалось открыть JDBC Connection Fortransaction», Ex); }}Метод dobegin () будет использовать ключ имени источника данных и подключение к значению и связывать соединение базы данных, которое было открыто с переменной потока.
2. Свяжите соединение
public static void bindresource (ObjectKey, значение объекта) бросает allodalStateException {object actualKey = transactionSynchronizationUtils.UnWrapResourceIfnecessary (key); Assert.notnull (значение, «значение не должно быть нулевым»); Map <объект, объект> map = resources.get (); // Установить Threadlocal MAP IFNONE найден IF (map == null) {map = newhashmap <object, object> (); resources.set (map); } Object OldValue = map.put (actualKey, значение); // Прозрачно подавляет Aresourceholder, который был помечен как void ... if (oldValue Encament ofResourceholder && (((ресурс -адвокат) OldValue) .isvoid ()) {oldValue = null; } if (oldValue! = null) {throw newillegalStateException ("уже значение [" + OldValue + "] для ключа [" + actualKey + "] связан с потоком [" + thread.currentThread (). getName () + "]"); } if (logger.istraceenabled ()) {logger.trace ("boundValue [" + value + "] для ключа [" + actactKey + "] к потоку [" + think.currentThread (). getName () + "]"); }}Переменная ресурсов - это указанная выше переменная Threadlocal, так что последующий JDBCTemplate может использовать DataSource в качестве ключа для поиска подключения к базе данных.
3. Выполнить SQL
Jdbctemplate
public objectExecute (PreditStatementCreator PSC, подготовленное действие CallCallback) throwsDataAccessException {assert.notnull (psc, «PrediveStatementCreator не должен быть нулевым»); Assert.notnull (действие, «объект обратного вызова не должен быть нулевым»); if (logger.isdebugenabled ()) {string sql = getsql (psc); logger.debug ("executivingPrepared SQL -оператор" + (sql! = null? "[" + sql + "]": "" ")); } Connection con = dataSourceutils.getConnection (getDataSource ()); Подготовленное предприятие PS = NULL; try {connection contouse = con; if (this.nativejdbcextractor! = null && this.nativejdbcextractor.isnativeconnectionnecessaryfornativePreparedStatements ()) {intouse = this.nativejdbcextractor.getNativeConnection (con); } ps = psc.createpreparedStatement (intouse); ApplyStatementsettings (PS); PreditStatementsPstouse = PS; if (this.nativejdbcextractor! = null) {pstouse = this.nativejdbcextractor.getnativePreparedStatement (ps); } Object result = action.doinpreparedStatement (pStouse); Руководителя (PS); результат возврата; } catch (sqlexception ex) {// ReleaseConnection Раннее, чтобы избежать потенциального тупика пула подключения // в случае, когда переводчик исключений еще не был инициализирован. if (psc ancessionofparameterdisposer) {((parameterdisposer) psc) .cleanupanupparameters (); } String sql = getSql (psc); psc = null; Jdbcutils.closeStatement (PS); ps = null; DataSourceutils.ReleAseConnection (con, getDataSource ()); con = null; ThrowgetExceptionTranslator (). Translate («PrediveStatementCallback», SQL, Ex); } наконец {if (psc ancessionOfParameterDisposer) {((parameterDisposer) psc) .cleanupparameters (); } Jdbcutils.closeStatement (ps); DataSourceutils.ReleAseConnection (con, getDataSource ()); }}
4. Получите соединение
DataSourceutils
Public Static Connection DogetConnection (dataSourceDatasource) выбрасывает SQLexception {assert.notnull (dataSource, «DataSource не указан»); Connectionholder Conholder = (Connectionholder) TransactionSynchronization manage.getresource (dataSource); if (conholder! = null && (conholder.hasconnection () || conholder.issynchronizedwithTransaction ())) {conholder.request (); if (! conholder.hasconnection ()) {logger.debug ("FetchingResumbed JDBC Connection из DataSource"); conholder.setConnection (dataSource.getConnection ()); } returnConholder.getConnection (); } // else у нас здесь либо нет владельца, либо пустого держателя, связанного с резьбой. logger.debug ("Подключение fetchingjdbc из DataSource"); Connection con = dataSource.getConnection (); if (TransactionSynchronization manage.issynchronizationActive ()) {logger.debug ("Синхронизация транзакции регистрации для соединения JDBC"); // Использование SameConnection для дальнейших действий JDBC в рамках транзакции. // Thread-BoundObject будет удален путем синхронизации при завершении транзакции. ConnectionHolderTortouse = CONTHOLTER; if (holdertouse == null) {holdertouse = new Connectionholder (con); } else {holdertouse.setConnection (con); } holdertouse.request (); TransactionSynchronizationManager.RegisterSynchronication (newConnectionSynchronization (holdertouse, DataSource)); holdertouse.setsynchronizedwithtransaction (true); if (holdertouse! = conholder) {transactionSynchronization manage.bindresource (dataSource, holdertouse); }} return con; } Можно видеть, что DataSourceutils также получает соединение через TransactionSynchronizationManager. Следовательно, до тех пор, пока у JDBCTEMPLATE и DataSourCetransActionManager есть такой же данные, вы обязательно получите одно и то же соединение с базой данных, и, естественно, вы можете правильно отправлять транзакции.
Давайте возьмем Hibernate в качестве примера, чтобы проиллюстрировать проблему, упомянутую в начале, и посмотрим, почему менеджер транзакций ORM Framework не может управлять JDBCTEMPLATE.
5 Manager транзакции ORM
HibernateTransactionManager
if (txobject.isnewsessionholder ()) {transactionSynchronization manage.bindresource (getSesionFactory (), txobject.getSessionHolder ()); }Поскольку структура ORM не вводит DataSource в TransactionManager для использования, но использует свои собственные сеансфакторные и другие объекты для эксплуатации данных данных, как и менеджер транзакций Hibernate выше. Таким образом, хотя основополагающий источник данных SessionFactory и JDBCTemplate может быть одинаковым, потому что различные ключи используются при привязке в TransactionSynchronizationManager (одно из них является именем SessionFactory, а другой - это имя DataSource), JDBCTEMPLATE не может получить подключение к базе данных, которое менеджер транзакции ORM начинает транзакцию при выполнении.
Различие между бобами
Файл конфигурации пружины в публичном проекте может быть направлен на несколько проектов. Поскольку каждому проекту может потребоваться только часть бобов в публичном проекте, когда начинается весенний контейнер этих проектов, необходимо отличить, какие бобы будут созданы.
1. Примеры применения
Взяв конфигурацию в Jetspeed, в качестве примера: Page-manager.xml с открытым исходным кодом.
<bean name = "xmlpagemanager" class = "org.apache.jetspeed.page.psml.castorxmlpagemanager" init-method = "init" dissome-method = "destry"> <meta key = "j2: cat" value = "xmlpagemanager orpageserizer" /> <constructor-arg index = "0" 0 "0". <refbean = "idGenerator"/> </constructor-arg> <constructor-arg index = "1"> <Refbean = "xmldocumentHandlerFactory"/> </constructor-arg>… </bean> <bean id = "dbpagemanager" class = "org.apache.jetspeed.page.impl.databasepagemanager" init-method = "init" destry-method = "destress"> <meta = "j2: cat" value = "dbpagemanager orpageserializ <dulch> jetspeed-inf/ojb/page-manager-repository.xml </value> </constructor-arg> <!-генератор идентификатора фрагмента-> <Конструктор-arg index = "1"> <ref bean = "idgenerator"/> </contructor-arg>… </bean>
2. Фильтр
Когда JetSpeedBeanDefinitionFilter анализирует каждое определение бобов в контейнере Spring, он извлечет значение, соответствующее J2: CAT в конфигурации вышеупомянутого бона, такого как DBPageManageror PageSerializer. Эта часть затем соответствует регулярному выражению с текущим ключом (считывается из файла конфигурации). Только бобы на соответствующем контейнере будут созданы пружинным контейнером.
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 registermynamicalias (реестр BeandefinitionRegistry, String Beanname, Beandefinition Bd) {String Aliases = (String) bd.getattribute (alias_meta_key); if (псевдоним! = null) {stringTokenizer st = NewstringTokenizer (псевдоним, ","); while (st.hasmoretokens ()) {string alias = st.nexttoken (); if (! alias.equals (beanname)) {Registry.registeralias (Beanname, псевдоним); }}}} Значение Category_meta_key в методе Match () J2: Cat. Текущий ключ сохраняется в классе Matchter, и он отвечает за сопоставление текущего ключа с регулярным выражением каждого фасоля.
Роль RegisterDynamicalias заключается в том, что после успешного совпадения бобов, индивидуальный пружинный контейнер вызвет этот метод, чтобы зарегистрировать псевдоним для бобов. Для получения подробной информации см. Исходный код в 1.3 ниже.
3. Настройка пружинного контейнера
Настройте пружинный контейнер, переопределите метод RegisterBeandefinition () и перехватите его, когда пружина регистрирует боб.
открытый класс filteringxmlwebapplicationContextends xmlwebapplicationContext {Private JetSpeedBeanDefinitionFilterFilter; publicfilteringxmlwebapplicationcontext (jetspeedbeandefinitionfilter, String [] configlocations, Properties initProperties, ServletContext ServletContext) {This (Filter, Configlocations, initProperties, 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.setignoreunresolvableplaceholders (true); ppc.setsystempropertiesmode (Propertyplaceholderconfigurer.system_properties_mode_fallback); ppc.setproperties (initproperties); addbeanfactorypostprocessor (PPC); } setConfiglocations (configlocations); SetServletContext (ServletContext); this.filter = filter; } Защищенный default -lectableBeanFactoryCreateBeanFactory () {return New FilteringListableBeanFactory (Filter, GetInternalParentBeanFactory ()); }} public classFilteringListableBeanFactory Extends defaultBeableBeanFactory {private jetspeedbeandefinitionfilterfilter; Public FilteringListableBeanFactory (jetspeedbeandefinitionfilterfilter, beanfactory parentbeanfactory) {super (parentbeanfactory); this.filter = filter; if (this.filter == null) {this.filter = newjetspeedbeandefinitionfilter (); } this.filter.init (); } / ** * Переопределение RegisterBeanDefinitionMethod, чтобы необязательно отфильтровать BeAndefinition и * Если запрошен динамически регистрационный псевдоним * / public void RegisterbeAndefinition (StringBeanName, Beandefinition BD) Throws BeandefinitionStoreException {if (beanteRexception {if (beAneMister {if (iffermath. BD); if (filter! = null) {filter.registerdynamicalias (this, beanname, bd); }}}} 4. псевдоним бобов
Используйте фабрику BeanReferenceFactorybean Factory, чтобы обернуть два боба, настроенные выше (XMLPageManager и DBPageManager). Ключи сочетаются в их собственной, и реализация заключается в переключении между двумя реализациями путем настройки текущего ключа. Все псевдоним сочетаются в одном, так что бобы, который ссылается на их бобов, просто цитирует псевдоним напрямую. Например, 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" /> < /bean> <bean> <meta " key = "j2: cat" value = "dbpagemanager" /> <meta Key = "j2: alias" value = "org.apache.jetspeed.page.pagemanager" /> <propertyname = "targetbeanname" value = "dbpagemanager" /> < /beantemount "org.apache. <Meta Key = "j2: cat" value = "default"/> <constructor-arg index = "0"> <refbean = "org.apache.jetspeed.page.pagemanager"/> </constructor-arg> <constructor-arg index = "1"> <detspeed-layouts :: velocolumon </"1" </bean>