ธุรกรรมผสม
ในการทำธุรกรรมของผู้จัดการธุรกรรมของ ORM Framework โดยใช้ JDBCTEMPLATE เพื่อดำเนินการ SQL จะไม่รวมอยู่ในการจัดการธุรกรรม
ต่อไปนี้คือการวิเคราะห์ซอร์สโค้ดเพื่อดูว่าเหตุใดจึงต้องใช้ JDBCTEMPLATE ภายในธุรกรรม DataSourCetransactionManager
1. เริ่มธุรกรรม
DataSourceTransactionManager
Void Dobegin ที่ได้รับการป้องกัน (การทำธุรกรรมของวัตถุ, คำจำกัดความของ TransactionDefinition) {DataSourceTransactionObjectTxObject = (DataSourceTransactionObject) การทำธุรกรรม; การเชื่อมต่อ con = null; ลอง {ถ้า (txobject.getConnectionholder () == null || txobject.getConnectionholder (). issynchronizedWithTransaction ()) {connectionNewCon = this.datasource.getConnection (); if (logger.isdebugenabled ()) {logger.debug ("AcquiredConnection [" + newCon + "] สำหรับธุรกรรม JDBC"); } txObject.setConnectionholder (newConnectionholder (newCon), จริง); } txObject.getConnectionholder (). SetSynchronizedWithTransaction (จริง); con = txobject.getConnectionholder (). getConnection (); IntegerPreviousIsolationLevel = dataSourceUtils.prepareconnectionForTransaction (con, คำจำกัดความ); txObject.setPreviousIsolationLevel (ก่อนหน้า isolationLevel); // เปลี่ยนเป็น ManualCommit หากจำเป็น สิ่งนี้มีราคาแพงมากในไดรเวอร์ JDBC บางตัว // ดังนั้นเราจึงไม่ต้องการทำโดยไม่จำเป็น (ตัวอย่างเช่นถ้าเราได้กำหนดค่าพูลการเชื่อมต่ออย่างชัดเจนเพื่อตั้งค่าไว้แล้ว) if (con.getautocommit ()) {txobject.setMustRestoreAutocommit (จริง); if (logger.isdebugenabled ()) {logger.debug ("switchingjdbc การเชื่อมต่อ [" + con + "] เพื่อใช้งานด้วยตนเอง"); } con.setautocommit (เท็จ); } txobject.getConnectionholder (). setTransactionactive (จริง); int timeout = deCinetimeout (นิยาม); if (หมดเวลา! = transactionDefinition.timeout_default) {txobject.getConnectionholder (). settimeoutinseconds (หมดเวลา); } // ผูกเซสชันสำหรับเธรด if (txobject.isNewConnectionholder ()) {transactionSynchronizationManager.bindresource (getDataSource (), txObject.getConnectionholder ()); }} catch (exception ex) {dataSourceutils.releaseconnection (con, this.datasource); โยน newCannotCreateTransactionException ("ไม่สามารถเปิดการเชื่อมต่อ JDBC fortransaction", ex); -วิธี Dobegin () จะใช้คีย์ชื่อแหล่งข้อมูลและการเชื่อมต่อเป็นค่าและผูกการเชื่อมต่อฐานข้อมูลที่เปิดไปยังตัวแปร ThreadLocal
2. ผูกการเชื่อมต่อ
โมฆะสาธารณะคงที่สาธารณะ bindResource (ObjectKey, ค่าวัตถุ) พ่น ungelStateException {Object realkey = transactionSynChronizationUts.unWrapResourceifnentary (คีย์); assert.notnull (ค่า "ค่าจะต้องไม่เป็นโมฆะ"); แผนที่ <วัตถุวัตถุ> map = resources.get (); // set threadlocal map ifnone พบถ้า (map == null) {map = newhashmap <object, object> (); Resources.set (แผนที่); } Object OldValue = map.put (จริง key, value); // อย่างโปร่งใสยับยั้งผู้ถือหุ้นที่ถูกทำเครื่องหมายว่าเป็นโมฆะ ... ถ้า (อินสแตนซ์ OldValue ของผู้ถือหุ้น && ((ผู้ถือทรัพยากร) OldValue) .isvoid ()) {oldValue = null; } if (oldValue! = null) {โยน newillegalstateException ("ค่าแล้ว [" + oldValue + "] สำหรับคีย์ [" + realkey + "] bound to Thread [" + thread.currentthread (). getName () + "]"); } if (logger.istraceEnabled ()) {logger.trace ("boundValue [" + value + "] สำหรับคีย์ [" + realekey + "] ไปยังเธรด [" + thread.currentthread (). getName () + "]"); -ตัวแปรทรัพยากรคือตัวแปร ThreadLocal ที่กล่าวถึงข้างต้นเพื่อให้ JDBCTEMPLATE ที่ตามมาสามารถใช้ DataSource เป็นคีย์เพื่อค้นหาการเชื่อมต่อฐานข้อมูล
3. ดำเนินการ SQL
jdbctemplate
Public ObjectExecute (PreparedStatementCreator PSC, PrepactStatementCallback การกระทำ) throwsDataAccessException {assert.notnull (PSC, "PreparedStatementCreator ต้องไม่เป็นโมฆะ"); assert.notnull (การกระทำ "วัตถุเรียกกลับจะต้องไม่เป็นโมฆะ"); if (logger.isdebugenabled ()) {string sql = getSql (psc); logger.debug ("ExecutingPrepared คำสั่ง SQL" + (sql! = null? "[" + sql + "]": ""); } Connection con = dataSourceutils.getConnection (getDataSource ()); PreparedStatement PS = NULL; ลอง {Connection Contouse = con; if (this.nativejdbcextractor! = null && this.nativejdbcextractor.isnativeConnectionNectionAryFornativePrepreperedStatements ()) {contouse = this.nativejdbcextractor.getNativeConnection (con); } ps = psc.createPreparedStatement (Contouse); ApplyStatementSettings (PS); PreparedStatementsPSTOUSE = PS; if (this.nativejdbcextractor! = null) {pstouse = this.nativejdbcextractor.getNativePreparedStatement (PS); } object result = action.doinpreparedStatement (pstouse); Handlewarnings (PS); ผลการกลับมา; } catch (sqlexception ex) {// releaseconnection ก่อนเพื่อหลีกเลี่ยงการเชื่อมต่อที่มีศักยภาพพูล deadlock // ในกรณีที่นักแปลข้อยกเว้นยังไม่ได้เริ่มต้น if (PSC InstanceOfParameterDisposer) {((parameterDisposer) PSC) .cleanupparameters (); } สตริง sql = getSql (psc); PSC = null; jdbcutils.closestatement (PS); ps = null; DataSourceutils.releaseconnection (con, getDataSource ()); con = null; ThrowGetExceptionTranslator (). แปล ("PreparedStatementCallback", SQL, EX); } ในที่สุด {if (PSC InstanceOfParameterDisposer) {((ParameterDisposer) PSC) .cleanupparameters (); } jdbcutils.closestatement (PS); DataSourceutils.releaseconnection (con, getDataSource ()); -
4. รับการเชื่อมต่อ
DataSourceUtils
การเชื่อมต่อแบบคงที่สาธารณะ DogetConnection (DataSourceDataTaSource) พ่น Sqlexception {assert.notNull (DataSource, "ไม่ระบุแหล่งข้อมูล"); Connectionholder conholder = (Connectionholder) TransactionSynchronizationManager.getResource (DataSource); if (conholder! = null && (conholder.hasconnection () || conholder.issynchronizedWithTransaction ())) {conholder.requested (); if (! conholder.hasconnection ()) {logger.debug ("FetchingResumed การเชื่อมต่อ JDBC จาก DataSource"); conholder.SetConnection (dataSource.getConnection ()); } returnConholder.getConnection (); } // อย่างอื่นเราอาจได้รับ noholder หรือผู้ถือมัดเธรดที่ว่างเปล่าที่นี่ logger.debug ("การเชื่อมต่อ FetchingJDBC จาก DataSource"); การเชื่อมต่อ con = dataSource.getConnection (); if (transactionSynchronizationManager.issynchronizationactive ()) {logger.debug ("การซิงโครไนซ์ registeringTransaction สำหรับการเชื่อมต่อ JDBC"); // ใช้ sameconnection สำหรับการดำเนินการ JDBC เพิ่มเติมภายในธุรกรรม // Thread-BoundObject จะถูกลบออกโดยการซิงโครไนซ์เมื่อเสร็จสิ้นการทำธุรกรรม ConnectionholderHolderTouse = ผู้ถือครอง; if (holderTouse == null) {holderTouse = new ConnectionHolder (con); } else {HolderTouse.SetConnection (con); } HolderTouse.Requested (); TransactionSynchronizationManager.registersynchronization (newConnectionSynchronization (HolderTouse, DataSource)); HolderTouse.SetSynchronizedWithTransaction (จริง); if (HolderTouse! = conholder) {transactionSynchronizationManager.bindResource (DataSource, HolderTouse); }} return con; - จะเห็นได้ว่า DataSourceUtils ยังได้รับการเชื่อมต่อผ่าน TransactionSynchronizationManager ดังนั้นตราบใดที่ JDBCTEMPLATE และ DataSourceTransactionManager มีแหล่งข้อมูลเดียวกันคุณจะได้รับการเชื่อมต่อฐานข้อมูลเดียวกันอย่างแน่นอนและตามธรรมชาติคุณสามารถส่งและย้อนกลับธุรกรรมได้อย่างถูกต้อง
ลองใช้ไฮเบอร์เนตเป็นตัวอย่างเพื่อแสดงให้เห็นถึงปัญหาที่กล่าวถึงในตอนต้นและดูว่าทำไมผู้จัดการธุรกรรมของกรอบ ORM ไม่สามารถจัดการ JDBCTEMPLATE ได้
5 ORM Transaction Manager
Hibernatetransactionmanager
if (txobject.isNewSessionHolder ()) {transactionSynchronizationManager.bindresource (getSessionFactory (), txobject.getSessionHolder ()); -เนื่องจาก ORM Framework ไม่ได้ฉีดแหล่งข้อมูลโดยตรงลงใน TransactionManager เพื่อใช้งาน แต่ใช้ SessionFactory และวัตถุอื่น ๆ ของตัวเองเพื่อใช้งานแหล่งข้อมูลเช่นเดียวกับตัวจัดการธุรกรรมไฮเบอร์เนตด้านบน ดังนั้นถึงแม้ว่าแหล่งข้อมูลพื้นฐานของ SessionFactory และ JDBCTEMPLATE อาจเหมือนกันเนื่องจากมีการใช้คีย์ที่แตกต่างกันเมื่อมีการเชื่อมโยงใน TransactionSynchronizationManager (หนึ่งคือชื่อ SessionFactory และอีกชื่อหนึ่งคือชื่อแหล่งข้อมูล) JDBCTEMPLATE ไม่สามารถรับการเชื่อมต่อฐานข้อมูล
ความแตกต่างระหว่างถั่ว
ไฟล์การกำหนดค่าฤดูใบไม้ผลิในโครงการสาธารณะอาจถูกอ้างอิงโดยหลายโครงการ เนื่องจากแต่ละโครงการอาจต้องใช้เพียงส่วนหนึ่งของถั่วในโครงการสาธารณะเมื่อคอนเทนเนอร์ฤดูใบไม้ผลิของโครงการเหล่านี้เริ่มต้นขึ้นจึงจำเป็นต้องแยกแยะว่าจะสร้างถั่วชนิดใด
1. ตัวอย่างแอปพลิเคชัน
การกำหนดค่าใน JetSpeed ซึ่งเป็นเฟรมเวิร์กโอเพ่นซอร์สของ Apache เป็นตัวอย่าง: page-manager.xml
<bean name = "xmlpageManager" class = "org.apache.jetspeed.page.psml.castorxmlpagemanager" init-method = "init" destroy-method = "ทำลาย"> <meta key = "j2: cat" value = "xmlpagemanager orpageserializer </constructor-arg> <constructor-arg index = "1"> <refbean = "xmldocumenthandlerfactory"/> </constructor-arg> … </epean> <bean id = "dbpagemanager" class = "org.apache.jetspeed.peed.page. key = "j2: cat" value = "dbpageManager orpageserializer"/> <!-ojb การกำหนดค่าไฟล์ resourcepath-> <constructor-Arg index = "0"> <value> jetspeed-inf/ojb/page-manager-repository.xml < bean = "idgenerator"/> </constructor-Arg> … </ebean>
2. ตัวกรองเบียน
เมื่อ jetspeedbeandefinitionfilter วิเคราะห์คำจำกัดความของถั่วในคอนเทนเนอร์ฤดูใบไม้ผลิมันจะนำค่าที่สอดคล้องกับ J2: CAT ในการกำหนดค่าถั่วข้างต้นเช่น DBPageManageror Pageserializer ส่วนนี้จะถูกจับคู่เป็นนิพจน์ทั่วไปไปยังคีย์ปัจจุบัน (อ่านจากไฟล์กำหนดค่า) เฉพาะถั่วในการจับคู่จะถูกสร้างขึ้นโดยคอนเทนเนอร์ฤดูใบไม้ผลิ
jetspeedbeandefinitionfilter
การจับคู่บูลีนสาธารณะ (BeanDefinition BD) {String BeanCategoriesExpression = (String) Bd.GetAttribute (category_meta_key); บูลีนจับคู่ = true; if (BeanCategoriesExpression! = null) {matched = ((matcher! = null) && matcher.match (BeanCategoriesExpression)); } Return Matched;} โมฆะสาธารณะ registerDynamicalias (BeanDefinitionRegistry Registry, String BeanName, BeanDefinition BD) {String aliases = (String) Bd.GetAttribute (alias_meta_key); ถ้า (นามแฝง! = null) {StringTokenizer st = newStringTokenizer (นามแฝง, ","); ในขณะที่ (st.hasmoretokens ()) {string alias = st.nexttoken (); if (! Alias.equals (Beanname)) {registry.registeralias (Beanname, นามแฝง); - ค่าของ category_meta_key ในวิธีการจับคู่ () คือ J2: cat คีย์ปัจจุบันจะถูกบันทึกไว้ในคลาส Matcher และรับผิดชอบในการจับคู่คีย์ปัจจุบันกับนิพจน์ปกติของถั่วแต่ละตัว
บทบาทของ registerDynamicalias คือ: หลังจากการจับคู่ถั่วสำเร็จคอนเทนเนอร์สปริงที่กำหนดเองจะเรียกวิธีนี้เพื่อลงทะเบียนนามแฝงสำหรับถั่ว สำหรับรายละเอียดดูซอร์สโค้ดใน 1.3 ด้านล่าง
3. ปรับแต่งคอนเทนเนอร์สปริง
ปรับแต่งคอนเทนเนอร์สปริงแทนที่เมธอด registerBeandefinition () และสกัดกั้นเมื่อฤดูใบไม้ผลิลงทะเบียนถั่ว
คลาสสาธารณะ FilteringXMLWEBAPPLICAINCONTENTENTS XMLWEBAPPLICANCONTEXTEXT {ส่วนตัว JETSPEEDBEANDEFINITIONFILTERFILTER; publicfilteringxmlwebapplicationContext (ตัวกรอง jetspeedbeanDefinitionFilter, สตริง [] การกำหนดค่า, คุณสมบัติ initProperties, servletContext servletContext) {สิ่งนี้ (ตัวกรอง, configlocations, initProperties, servletContext, null); } publicfilteringxmlwebapplicationContext (ตัวกรอง jetspeedbeanDefinitionFilter, สตริง [] การกำหนดค่า, คุณสมบัติ initProperties, servletContext servletContext, ApplicationContext parent) {super (); if (parent! = null) {this.setparent (parent); } if (initProperties! = null) {PropertyPlaceHolderConFigurer PPC = ใหม่ PropertyPlaceHolderConFigurer (); PPC.ESTIGNOREUNRESOLVABLEPLACEHOLDERS (จริง); PPC.SetSystempropertiesMode (PropertyPlaceHolderConfigurer.system_properties_mode_fallback); PPC.SetProperties (InitProperties); AddBeanFactoryPostProcessor (PPC); } setConfigLocations (การกำหนดค่า); setServletContext (servletContext); this.filter = ตัวกรอง; } ป้องกัน defaultListableBeanFactoryCreateBeanFactory () {ส่งคืน FilteringListableBeanFactory ใหม่ (ตัวกรอง, getInternalParentBeanFactory ()); }} public classfilteringListableBeanFactory ขยาย defaultListableBeanFactory {ส่วนตัว JetSpeedBeandEfinitionFilterFilter; Public FilteringListableBeanFactory (JetSpeedBeanDefinitionFilterFilter, BeanFactory ParentBeanFactory) {Super (ParentBeanFactory); this.filter = ตัวกรอง; if (this.filter == null) {this.filter = newJetSpeedBeandEfinitionFilter (); } this.filter.init (); } / ** * แทนที่ของ registerBeanDefinitionMethod เพื่อเลือกตัวเลือก beandefinition และ * หากขอให้ลงทะเบียนนามแฝง anbean แบบไดนามิก * / โมฆะสาธารณะ registerBeandefinition (StringBeanname, beandefinition BD) super.registerbeandefinition (Beanname, BD); if (filter! = null) {filter.registerdynamicalias (นี่, Beanname, bd); - 4. นามแฝงถั่ว
ใช้ BeanreferenceFactoryBean Factory Bean เพื่อห่อถั่วสองตัวที่กำหนดค่าไว้ด้านบน (XMLPAGENAGER และ DBPAGEMANAGER) คีย์จะถูกจับคู่เป็นของตัวเองและการใช้งานคือการสลับระหว่างการใช้งานทั้งสองโดยการกำหนดค่าคีย์ปัจจุบัน นามแฝงทั้งหมดถูกจับคู่เป็นหนึ่งเดียวเพื่อให้ถั่วที่อ้างถึงถั่วของพวกเขาเป็นเพียงการอ้างถึงนามแฝงโดยตรง ตัวอย่างเช่น pagelayoutComponent ด้านล่าง
page-manager.xml
<bean> <meta key = "j2: cat" value = "xmlpageManager" /> <meta key = "j2: alias" value = "org.apache.jetspeed.page.pagemanager" /> <poreportname = "targetBeanName" value = "xmlpagemanager" /> key = "j2: cat" value = "dbpageManager" /> <meta key = "j2: alias" value = "org.apache.jetspeed.page.pageManager" /> <poreportname = "targetBeanName" value = "dbpageManager" /> <meta key = "j2: cat" value = "default"/> <constructor-arg index = "0"> <refbean = "org.apache.jetspeed.page.pagemanager"/> </constructor-arg> <constructor-Arg index = "1">