혼합 거래
ORM Framework의 트랜잭션 관리자의 거래에서 JDBCTEMPLATE를 사용하여 SQL을 실행하는 것은 트랜잭션 관리에 포함되지 않습니다.
다음은 DataSourCetransactionManager 트랜잭션 내에서 JDBCTemPlate을 사용해야하는 이유를 확인하는 소스 코드 분석입니다.
1. 트랜잭션을 시작하십시오
DataSourcetransactionManager
보호 된 void dobegin (객체 트랜잭션, TransactionDefinition 정의) {dataSourCetransactionObjectTXObject = (DataSourCetransactionObject) 트랜잭션; 연결 con = null; try {if (txObject.getConnectionHolder () == null || txObject.getConnectionHolder (). issynchronizedwithTransaction ()) {ConnectionNewCon = this.datasource.getConnection (); if (logger.isdebugenabled ()) {logger.debug ( "JDBC 트랜잭션을위한 획득 [" + newcon + "]); } txObject.setConnectionHolder (NewConnectionHolder (NewCon), True); } txObject.getConnectionHolder (). setSynchRonizedWithTransAction (true); con = txobject.getConnectionHolder (). getConnection (); integerpreviousolationlevel = dataSourceutils.prepareConnectionForTransaction (con, 정의); txObject.setPreviousOlationLevel (previous -IsolationLevel); // 필요한 경우 수동 커밋으로 전환합니다. 이것은 일부 jdbc 드라이버에서 매우 비싸기 때문에 // 우리는 불필요하게 수행하고 싶지 않습니다 (예 : 명시 적으로 theConnection 풀을 구성한 경우 이미 설정하도록 구성된 경우). if (con.getAutocommit ()) {txObject.setMustrestoreautocommit (true); if (logger.isdebugenabled ()) {logger.debug ( "switchingjdbc connection [" + con + "]에 수동 커밋"); } con.setAutocommit (false); } txObject.getConnectionHolder (). setTransActionActive (true); int timeout = dectiminetimeout (정의); if (timeout! = transactionDefinition.timeout_default) {txobject.getConnectionHolder (). settimeoutInseconds (timeout); } // 세션 홀더를 스레드에 바인딩합니다. if (txObject.isNewConnectionHolder ()) {TransActionSynchronizationManager.bindResource (getDatasource (), txObject.getConnectionHolder ()); }} catch (예외 예) {dataSourceutils.releaseConnection (con, this.datasource); NewCannotCreateTransactionException을 던지십시오 ( "JDBC Connection ForTransaction을 열 수 없음", Ex); }}dobegin () 메소드는 데이터 소스 이름 키 및 ConnectionHolder를 값으로 사용하고 orthlocal 변수에 열린 데이터베이스 연결을 바인딩합니다.
2. 연결을 바인딩하십시오
public static void bindresource (ObjectKey, Object Value)는 불법 상태를 던졌습니다. Assert.notnull (값, "값은 널 넌무 가치가 없어야한다"); Map <Object, Object> Map = resources.get (); // struledlocal map 설정 ifnone 찾았습니다. resources.set (지도); } 오브젝트 OldValue = map.put (실제 키, 값); // void로 표시된 AresourceHolder를 투명하게 억제합니다 ... if (OldValue instance ofResourceHolder && ((리소스 홀더) OldValue) .isvoid ()) {OldValue = null; } if (OldValue! = null) {key [ " + allykey +"]에 대한 value [ " + oldValue +"] 스레드에 대한 bound [ " + stread.currentThread (). getName () +"] "); } if (logger.istraceEnabled ()) {logger.trace ( "" + 스레드. }}리소스 변수는 위에서 언급 한 ThreadLocal 변수이므로 후속 JDBCTEMPLATE는 데이터베이스 연결을 찾기위한 키로 DataSource를 사용할 수 있습니다.
3. SQL을 실행하십시오
jdbctemplate
Public ObjectExecute (PropertStatementCreator PSC, ProadesTatementCallback Action) ThrowsDataAccessException {Assert.NOTNULL (PSC, PSC, PRESEDSTATEMERCREATOR가 무효가되어서는 안됩니다 "); assert.notnull (action, "콜백 객체는 무효가되어서는 안됩니다"); if (logger.isdebugenabled ()) {String sql = getSql (psc); logger.debug ( "executepreprepared sql statement" + (sql! = null? "[" + SQL + "]": ""); } Connection con = dataSourceutils.getConnection (getDatasource ()); 준비된 상태 ps = null; try {Connection contouse = con; if (this.nativejdbcextractor! = null && this.nativejdbcextractor.isnativeConnectionnecessaryFornativePreparedStatements ()) {contouse = this.nativejdbcextractor.getNativeConnection (con); } ps = psc.createPreparedStatement (contouse); ApplyStatementSettings (PS); preparedstatementspstouse = ps; if (this.nativejdbcextractor! = null) {pstouse = this.nativejdbcextractor.getnativePreparedStatement (ps); } 객체 결과 = action.DoinPreparedStatement (PSTOUSE); 핸들 와이 닝 (ps); 반환 결과; } catch (sqlexception ex) {// 릴리스 앤 컨칭 일찍, 잠재적 연결 풀 교착 상태를 피하기 위해 예외 번역기가 아직 초기화되지 않은 경우. if (psc instanceofparameterDisposer) {((ParameterDisposer) psc) .cleanupparameters (); } 문자열 sql = getSql (psc); psc = null; jdbcutils.closestatement (ps); ps = null; DataSourceutils.ReleaseConnection (con, getDatasource ()); con = null; ThrowGeteXceptionTranslator (). Translate ( "PreperStatementCallback", SQL, Ex); } 마지막으로 {if (psc instanceofparameterDisposer) {((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) TransactionSynchronizationManager.getResource (DataSource); if (conholder! = null && (conholder.hasconnection () || conholder.issynchronizedwithTransaction ())) {conholder.requested (); if (! conholder.hasconnection ()) {logger.debug ( "DataSource에서"FetchingResumed JDBC 연결 "); conholder.setConnection (dataSource.getConnection ()); } returnConholder.getConnection (); } // 그렇지 않으면 우리는 여기에 No Holder 또는 빈 스레드 바운드 홀더가 있습니다. logger.debug ( "DataSource의 Fetchingjdbc 연결"); Connection Con = DataSource.getConnection (); if (transactionSynchronizationManager.issynchronizationActive ()) {logger.debug ( "JDBC 연결에 대한 RegisteringTransaction Synchronization"); // 트랜잭션 내에서 추가 JDBC 작업을 위해 SameConnection을 사용합니다. // 트랜잭션 완료시 동기화로 스레드-바운버젝트가 제거됩니다. ConnectionHolderHolderTouse = 상담자; if (holderTouse == null) {holderTouse = new ConnectionHolder (Con); } else {holderTouse.setConnection (con); } HolderTouse.Requested (); TransactionSynchronizationManager.registersynchronization (NewConnectionsYnchronization (HolderTouse, DataSource)); holdertouse.setsynchronizedwithTransaction (true); if (HolderTouse! = Conholder) {TransactionSynchronizationManager.BindResource (DataSource, HolderTouse); }} return con; } DataSourceutils는 또한 TransactionSynchronizationManager를 통해 연결을 얻는다는 것을 알 수 있습니다. 따라서 jdbctemplate 및 dataSourcetransactionManager가 동일한 데이터 소스를 갖는 한, 동일한 데이터베이스 연결을 얻을 수 있으며 당연히 트랜잭션을 올바르게 제출하고 롤백 할 수 있습니다.
처음에 언급 된 문제를 설명하고 ORM 프레임 워크의 트랜잭션 관리자가 JDBCTemplate을 관리 할 수없는 이유를 살펴 보겠습니다.
5 ORM 트랜잭션 관리자
HibernateTransactionManager
if (txObject.isNewSessionHolder ()) {TransActionSynchronizationManager.bindResource (getSessionFactory (), txObject.getSessionHolder ()); }ORM 프레임 워크는 사용하기 위해 DataSource를 TransactionManager에 직접 주입하지는 않지만 위의 최대 절전 모드 트랜잭션 관리자와 마찬가지로 자체 SessionFactory 및 기타 객체를 사용합니다. 따라서 SessionFactory 및 JDBCTemplate의 기본 데이터 소스가 동일 할 수 있지만 TransactionSynchronizationManager (하나는 SessionFactory Name이고 다른 하나는 DataSource 이름 임)에 다른 키가 사용되기 때문에 JDBCTemplate은 ORM Transaction Manager가 거래를 시작할 때 데이터베이스 연결을 시작할 수 없습니다.
콩의 구별
공개 프로젝트의 스프링 구성 파일은 여러 프로젝트에서 참조 할 수 있습니다. 각 프로젝트는 공개 프로젝트에서 콩의 일부만 필요할 수 있으므로이 프로젝트의 스프링 컨테이너가 시작되면 생성 할 콩을 구별해야합니다.
1. 응용 프로그램 예제
예를 들어 Apache의 오픈 소스 프레임 워크 인 Jetspeed에서 구성을 찍습니다 : Page-Manager.xml
<bean name = "xmlpagemanager"class = "org.apache.jetspeed.page.page.psml.castorxmlpagemanager"init-method = "init"destroy-method = "destroy"> <meta key = "j2 : cat"value = "xmlpagemanager orpageserializer" />> index = <refbean " /"0 "0"0 "0"0. </constructor-arg> <생성자-arg index = "1"> <refbean = "xmldocumentHandlerFactory"/> </prossuctor-arg>… </bean> <bean id = "dbpagemanager"class = "org.apache.jetspeed.page.impl.databasepagemanager"init init init init init-method = "init-method" 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.Bean 필터
JetSpeedbeanDefinitionFilter가 스프링 컨테이너의 각 Bean 정의를 파서 잡으면 DBPagemanAgeror PageSerializer와 같은 위의 Bean 구성에서 J2 : CAT에 해당하는 값을 제거합니다. 이 부분은 현재 키에 대한 정규 표현식으로 일치합니다 (구성 파일에서 읽으십시오). 일치하는 콩 만 스프링 컨테이너에 의해 만들어집니다.
JetSpeedBeanDefinitionFilter
Public Boolean Match (BeanDefinition Bd) {String Beancategoriesexpression = (String) Bd.getAttribute (Category_Meta_Key); 부울 일치 = true; if (beancategoriesexpression! = null) {matched = ((matcher! = null) && matcher.match (beancategoriesexpression)); } 반환 일치;} public void registerDynamicalias (beanDefinitionRegistry 레지스트리, String BeanName, BeanDefinition Bd) {String aliases = (string) bd.getAttribute (alias_meta_key); if (aliases! = null) {StringTokenizer st = NewStringTokenizer (별명, ",); while (st.hasmoretokens ()) {String alias = st.nextToken (); if (! alias.equals (beanname)) {registry.registeralias (beanname, alias); }}}} match () 메소드에서 category_meta_key의 값은 j2 : cat입니다. 현재 키는 경기자 클래스에 저장되며 현재 키를 각 Bean의 정규 표현과 일치시킬 책임이 있습니다.
RegisterDynamicalias의 역할은 다음과 같습니다. Bean 일치가 성공적으로 일치하면 맞춤형 스프링 컨테이너 가이 방법을 호출하여 Bean에 대한 별명을 등록합니다. 자세한 내용은 아래 1.3의 소스 코드를 참조하십시오.
3. 스프링 컨테이너를 사용자 정의하십시오
스프링 컨테이너를 사용자 정의하고 RegisterBeanDefinition () 메소드를 무시하고 스프링이 콩을 등록 할 때 차단하십시오.
공개 클래스 필터링 XMLWEBAPPLICATIONCONTEXTENDS XMLWEBAPPLICATIONCONTEXT {private jetSpeedBeanDefinitionFilterFilter; publicFilteringXMLWebApplicationContext (jetSpeedBeanDefinitionFilter 필터, 문자열 [] 구성, 속성 시작 프로그램, ServletContext ServletContext) {this (필터, 구성, 시작 분류, ServletContext, Null); } publicFilteringXMlWebApplicationContext (jetSpeedBeanDefinitionFilter 필터, 문자열 [] 구성, 속성 시작 프로그램, ServletContext ServletContext, ApplicationContext Parent) {super (); if (parent! = null) {this.setparent (parent); } if (initProperties! = null) {PropertyPlaceHolderConfigurer ppc = new PropertyPlaceHolderConfigurer (); ppc.setignoreunresolvable placeholders (true); ppc.setsystempropertiesmode (propertyplaceholderconfigurer.system_properties_mode_fallback); ppc.setproperties (initproperties); AddBeanFactoryPostProcessor (PPC); } setConfigLocations (configlocations); setServletContext (ServletContext); this.filter = 필터; } 보호 된 defaultListableBeanCreateBeanFactory () {return new FilteringListableBeanFactory (필터, GetInternalParentBeanFactory ()); }} public classFilteringListableBeanFactory 확장 기본 정보를 확장합니다. public filteringlistablebeanfactory (jetspeedbeandefinitionfilterfilter, beanfactory parentbeanfactory) {super (parentbeanfactory); this.filter = 필터; if (this.filter == null) {this.filter = newJetSpeedBeanDefinitionFilter (); } this.filter.init (); } / ** * RegisterBeanDefinitionMethod를 선택적으로 Beandefinition을 필터링하고 * Beandefinition을 필터링하고 * 동적으로 Register alias * / public void registerBeanDefinition (StringBeanName, BeanDefinition BD)이 요청한 경우 BeanDefinitionStoreException {if (filter.match (bd)) {super -regerfericer -beandefinition (beandefinition, beandefinition bd). BD); if (filter! = null) {filter.registerdynamicalias (this, beanname, bd); }}}} 4. 콩 별칭
BeanReferenceFactoryBean Factory Bean을 사용하여 위에 구성된 두 개의 Bean을 감싸십시오 (XMLPAGEMANAGER 및 DBPAGEMANAGER). 키는 자체적으로 일치하며 구현은 현재 키를 구성하여 두 구현을 전환하는 것입니다. 모든 별칭은 하나로 일치하여 콩을 참조하는 콩이 별명을 직접 인용하는 것입니다. 예를 들어 아래의 pagelayoutcomponent입니다.
page-manager.xml
<bean> <meta key = "j2 : cat"value = "xmlpagemanager" /> <meta key = "j2 : alias"value = "org.apache.jetspeed.page.pagemanager" /> <propertyname = "value ="xmlpagemanager "/> < /bean> <meta key ="j2 : cat "j2 : cat"j2 : cat. <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 : "j2 :" <생성자-arg index = "0"> <refbean = "org.apache.jetspeed.page.pagemanager"/> </prossuctor-arg> <constructor-arg index = "1"> <value> jetspeed-layouts :: VelocityOnecolumn </value> </bean> </bean>