المعاملات المختلطة
في معاملات مدير المعاملات في ORM Framework ، لن يتم تضمين jdbctemplate لتنفيذ SQL في إدارة المعاملات.
فيما يلي تحليل رمز المصدر لمعرفة سبب استخدام JDBCtemplate في معاملة DataSourCetransActionManager.
1. ابدأ المعاملات
DatasourCetransactionManager
dobegin void المحمي (معاملة الكائن ، تعريف المعاملة) {datasourCetRansActionObjectTxObject = (datasourCetransActionObject) المعاملة ؛ الاتصال con = null ؛ حاول {if (txObject.getConnectionHolder () == null || txObject.getConnectionHolder (). issynchronizedWithTransaction ()) {connectionNewCon = this.datasource.getConnection () ؛ if (logger.isdebugenabled ()) {logger.debug ("quessiredConnection [" + newCon + "] لمعاملة JDBC") ؛ } txobject.setConnectionHolder (NewConnectionHolder (NewCon) ، true) ؛ } txobject.getConnectionHolder (). con = txobject.getConnectionHolder (). getConnection () ؛ integerpreviousIsOlationLevel = dataSourceUtils.prepareconnectionfortransaction (con ، definition) ؛ txObject.setPreviousIsoLationLevel (depireiSoLationLevel) ؛ // التبديل إلى ManualCommit إذا لزم الأمر. هذا مكلف للغاية في بعض برامج تشغيل JDBC ، // لذلك نحن لا نريد القيام بذلك دون داع (على سبيل المثال إذا قمنا بتكوين تجمع Theconnection بشكل صريح لتعيينه بالفعل). if (con.getautocommit ()) {txObject.setMustrestoreaUtocommit (true) ؛ if (logger.isdebugenabled ()) {logger.debug ("switchingjdbc connection [" + con + "] إلى الالتزام اليدوي") ؛ } consetautocommit (false) ؛ } txobject.getConnectionHolder (). setTransActionActive (true) ؛ int timeout = denterItimeOut (تعريف) ؛ if (timeout! = TransactionDefinition.timeout_default) {txObject.getConnectionHolder (). } // ربط حامل الجلسة بالخيط. if (txobject.isnewConnectionHolder ()) {TransactionSynchronizationManager.BindResource (getDataSource () ، txObject.getConnectionHolder ()) ؛ }} catch (استثناء ex) {dataSourceUtils.ReLeasEconnection (con ، this.datasource) ؛ رمي newcannotcreateTransactionException ("لا يمكن فتح اتصال JDBC fortransaction" ، ex) ؛ }}ستستخدم طريقة dobegin () مفتاح اسم مصدر البيانات و ConnectionHolder كقيمة ، وربط اتصال قاعدة البيانات الذي تم فتحه بمتغير مؤشر ترابط.
2. ربط الاتصال
الفراغ الثابت العام bindResource (ObjectKey ، قيمة الكائن) يلقي aluctionStateException {Object vensykey = TransactionSynchronizationUtils.unwrapResourceIfnecessary (Key) ؛ Assert.notnull (القيمة ، "يجب ألا تكون القيمة لاغية") ؛ الخريطة <object ، object> map = resources.get () ؛ // set threadlocal map ifnone وجدت إذا (map == null) {map = newhashMap <object ، object> () ؛ الموارد. } Object OldValue = map.put (actualKey ، value) ؛ // قمع معامل Aresourceholder بشفافية تم تمييزه على أنه باطل ... إذا (مثيل OldValue OfresourceHolder && ((ResourceHolder) Oldvalue) .isvoid ()) {Oldvalue = null ؛ } if (oldvalue! = null) {throw newileGalStateException ("value بالفعل [" + oldvalue + "] للمفتاح [" + stuterkey + "] ملزمة إلى موضوع [ } if (logger.istraceEnabled ()) {logger.trace ("BoundValue [" + value + "] للمفتاح [" + stuterkey + "] إلى الموضوع [ + thread.currentThRead (). getName () +"] ") ؛ }}متغير الموارد هو متغير ThreadLocal المذكور أعلاه ، بحيث يمكن لـ JDBCTEMPLATE اللاحق استخدام مصدر البيانات كمفتاح للعثور على اتصال قاعدة البيانات.
3. تنفيذ SQL
jdbctemplate
Public ObjectExecute (إعداد psc psc ، reparedStatementCallback) TrowsDataAccessException {Assert.notnull (psc ، "يجب ألا يكون reparedStateMentCreator فارغًا") ؛ Assert.notnull (الإجراء ، "كائن رد الاتصال يجب ألا يكون فارغًا") ؛ if (logger.isdebugenabled ()) {string sql = getSql (psc) ؛ logger.debug ("eventingpreped sql state" + (sql! = null؟ "[" + sql + "]": "")) ؛ } connect con = dataSourceUtils.getConnection (getDataSource ()) ؛ أعدت PS = NULL ؛ حاول {connection contouse = con ؛ if (this.nativejdbcextractor! = null && this.nativejdbcextractor.isnativeConnectionNecessaryFornativePrepedStatements ()) {contouse = this.nativejdbcextractor.getnativeConnection (con) ؛ } ps = psc.createpreparedStatement (contouse) ؛ ApplicStatementSettings (PS) ؛ ReparedStatementspStouse = PS ؛ if (this.nativejdbcextractor! = null) {pStouse = this.nativejdbcextractor.getNativePrepedStatement (ps) ؛ } نتيجة الكائن = Action.doinPreparedStatement (pStouse) ؛ تعاني من الحواجز (PS) ؛ نتيجة العودة } catch (sqlexception ex) {// RELEASECONNECTION EARRY ، لتجنب DEADLOCK BOOL المحتملة // في الحالة عندما لم يتم تهيئة مترجم الاستثناء بعد. if (psc almateOfParameterDisposer) {((parameterDisposer) psc) .clenupparameters () ؛ } string sql = getSQL (psc) ؛ PSC = NULL ؛ jdbcutils.closestatement (ps) ؛ PS = NULL ؛ dataSourceUtils.ReleAseconnection (con ، getDataSource ()) ؛ يخدع = فارغ ؛ ThereGetExceptionTranslator (). ترجمة ("ReparedStatementCallback" ، SQL ، EX) ؛ } أخيرًا {if (psc almateOfParameterDisposer) {((parameterDisposer) psc) .clenupparameters () ؛ } jdbcutils.closestatement (ps) ؛ dataSourceUtils.ReleAseconnection (con ، getDataSource ()) ؛ }}
4. احصل على اتصال
ثبات البيانات
الاتصال الثابت العام doggeConnection (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 ("connect jdbc jdbc من dataSource") ؛ conholder.setConnection (datasource.getConnection ()) ؛ } returnConholder.getConnection () ؛ } // آخر ، إما حصلنا على NOHOLDER أو حامل فارغ مرتبط هنا. logger.debug ("connectjdbc connection from dataSource") ؛ Connection con = datasource.getConnection () ؛ if (TransactionSynChronizationManager.issynchronization ()) {logger.debug ("registeringTransaction Synchronization for JDBC Connection") ؛ // استخدم sameconnection لمزيد من إجراءات JDBC داخل المعاملة. // سيتم إزالة CHENTER-BOUNDOBJECT عن طريق التزامن عند الانتهاء من المعاملة. ConnectionHolderderTouse = Conholder ؛ if (HolderTouse == null) {HolderTouse = new ConnectionHolder (con) ؛ } آخر {holdertouse.setConnection (con) ؛ } holdertouse.requested () ؛ TransactionSynchronizationManager.regtersynchronization (NewConnectionSynchronization (HolderTouse ، DataSource)) ؛ HolderTouse.SetSynchronizedWithTransaction (True) ؛ if (holdertouse! = conholder) {TransactionSynchronizationManager.BindResource (DataSource ، HolderTouse) ؛ }} return con ؛ } يمكن ملاحظة أن dataSourceUtils تحصل أيضًا على الاتصال من خلال المعاملات ynchronizationManager. لذلك ، طالما أن JDBCtemplate و DataSourCetransActionManager لديهما نفس مصدر البيانات ، ستحصل بالتأكيد على نفس اتصال قاعدة البيانات وبشكل طبيعي يمكنك إرسال المعاملات وتراجعها بشكل صحيح.
دعنا نأخذ السبات كمثال لتوضيح المشكلة المذكورة في البداية ، ونرى لماذا لا يستطيع مدير المعاملات في إطار ORM إدارة JDBCtemplate.
5 مدير معاملات ORM
HibernatetransactionManager
if (txobject.isnewsessionholder ()) {TransactionSynchronizationManager.BindResource (getSessionFactory () ، txObject.getSessionHolder ()) ؛ }نظرًا لأن ORM Framework لا يضخ مباشرة بيانات البيانات في المعاملات للاستخدام ، ولكنه يستخدم SessionFactory وغيرها من الكائنات لتشغيل مصدر البيانات ، تمامًا مثل مدير معاملات Hibernate أعلاه. لذلك ، على الرغم من أن مصدر البيانات الأساسي لـ SessionFactory و JDBCtemplate قد يكون هو نفسه ، لأن المفاتيح المختلفة تستخدم عند الربط في المعاملات synchronizationManager (واحد هو اسم الجلسة والآخر هو اسم دواء البيانات) ، JDBCtemplate لا يمكنه الحصول على اتصال Database الذي يبدأ مدير معاملات ORM عند التنفيذ.
التمييز بين الفاصوليا
يمكن الرجوع إلى ملف تكوين الربيع في المشروع العام بواسطة مشاريع متعددة. نظرًا لأن كل مشروع قد يتطلب فقط جزءًا من الفاصوليا في المشروع العام ، عند بدء تشغيل حاوية الربيع لهذه المشاريع ، من الضروري التمييز بين الفول الذي سيتم إنشاؤه.
1. أمثلة التطبيق
أخذ تكوين في JetSpeed ، إطار مفتوح المصدر من Apache ، كمثال: Page-Manager.xml
<bean name = "xmlpagemanager" class = "org.apache.jetspeed.page.psml.castorxmlpageManager" init-method = "init" DRIDED-Method = "DestRutor"> <meta key = "j2: cat" value = " <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" destroy-method = "dester"> <meta key = "j2: cat" value = "dbpagemanager orpageserializer" />! <Value> jetspeed-inf/ojb/page-manager-repository.xml </value> </constructor-arg> <!-مولد معرف الشظية-> <constructor-arg index = "1"> <ref bean = "idgenerator"/> </constructor-arg> ... </bean>
2. مرشح البيان
عندما تقوم JetSpeedBeanDefinitionFilter بتوزيع كل تعريف بين الحاوية في حاوية الزنبرك ، فإنه سيأخذ القيمة المقابلة لـ J2: CAT في تكوين الفاصوليا أعلاه ، مثل DBPagemanagerororializer. ثم تتم مطابقة هذا الجزء كتعبير منتظم للمفتاح الحالي (اقرأ من ملف التكوين). سيتم إنشاء الفاصوليا على المطابقة فقط بواسطة حاوية الربيع.
jetspeedbeandefinitionfilter
مطابقة منطقية عامة (BeanDefinition BD) {String BeanCategoriesExpression = (String) bd.getAttribute (category_meta_key) ؛ مطابقة منطقية = صواب ؛ if (beancategoriesexpression! = null) {matchiced = ((matcher! = null) && matcher.match (beanCategoriesExpression)) ؛ } إرجاع مطابقة ؛} public void registerDynamicalias (سجل beandefinitionregistry ، سلسلة beanname ، beandefinition bd) {string alases = (string) bd.getAttribute (alias_meta_key) ؛ if (alases! = null) {StringTokenizer st = newstringTokenizer (alases ، "،") ؛ بينما (st.hasmoretokens ()) {string alias = st.nextToken () ؛ if (! alias.equals (beanname)) {registry.registeralias (beanname ، alias) ؛ }}}} قيمة الفئة _meta_key في طريقة match () هي j2: cat. يتم حفظ المفتاح الحالي في فئة Matcher ، وهو مسؤول عن مطابقة المفتاح الحالي مع التعبير العادي عن كل حبة.
دور registerDynamicalias هو: بعد تطابق الفول بنجاح ، ستدعو حاوية الربيع المخصصة هذه الطريقة لتسجيل الاسم المستعار للفول. للحصول على التفاصيل ، راجع الكود المصدر في 1.3 أدناه.
3. تخصيص حاوية الربيع
تخصيص حاوية زنبركية ، وتجاوز طريقة registerBeanDefinition () ، واعتراضها عندما يسجل الربيع حبة.
الفئة العامة filteringxmlwebapplicationContextends publicfilteringxmlWebapplicationContext (JetSpeedBeanDefinitionFilter Filter ، string [] configlocations ، stitproperties ، servletcontext servletcont) } publicfilteringxmlwebapplicationContext (JetSpeedBeanDefinitionFilter Filter ، string [] configlocations ، stitproperties ، servletcontext servletcontext ، applicationContext parent) {super () ؛ if (parent! = null) {this.setParent (parent) ؛ } if (InitProperties! = null) {propertyHolderConfigurer ppc = new PropertyPlaceholderConfigurer () ؛ ppc.setignoreUnresolvablebolors (True) ؛ ppc.SetSystemPropertiesMode (propertyholderConfigurer.system_properties_mode_fallback) ؛ ppc.setProperties (InitProperties) ؛ AddBeanfactoryPostProcessor (PPC) ؛ } setConfigLocations (configlocations) ؛ setServletContext (servletContext) ؛ this.filter = filter ؛ } DefaultListableBeanFactoryCreateBeanFactory () {return new FilteringListableBeanFactory (filter ، getInternalParentBeanfactory ()) ؛ }} public classFilteringListableBeanfactory يمتد DefaultListableBeanFactory {private jetspeedbeandefinitionfilterfilter ؛ publiceringListableBeanFactory (JetSpeedBeanDefinitionFilterFilter ، Beanfactory ParentBeanFactory) {Super (ParentBeanFactory) ؛ this.filter = filter ؛ if (this.filter == null) {this.filter = newjetspeedbeandefinitionfilter () ؛ } this.filter.init () ؛ } / ** * تجاوز registerBeanDefinitionMethod لتصفية اختياريًا من الفاصولياء و * إذا تم طلبها بشكل ديناميكي ، فإن anbean alias * / public void registerBeanDefinition (stringBeanName ، beandefinition bd) يلقي beandefinitionstoreException {if (filter.match (bd)) if (filter! = null) {filter.registerDynamicalias (this ، beanname ، bd) ؛ }}}} 4. الاسم المستعار الفول
استخدم BeanReferenceFactorybean Factory Bean لالتفاف الفاصلين المكونة أعلاه (XMLPagemanager و DBPagemanager). تتم مطابقة المفاتيح معها ، والتنفيذ هو التبديل بين التطبيقين من خلال تكوين المفتاح الحالي. تتم مطابقة جميع الاسم المستعار مع واحد ، بحيث تقتبس الفول الذي يشير إلى الفاصوليا فقط الاسم المستعار. على سبيل المثال ، pagelayoutComponent أدناه.
الصفحة-ماناجر. xml
<Bean> <meta key = "j2: cat" value = "xmlpagemanager" /> <meta key = "j2: alias" value = "org.apache.jetspeed.page.pagemanager key = "j2: cat" value = "dbpageManager" /> <meta key = "j2: alias" value = "org.apache.jetspeed.page.pagemanager" /> <propertyName = "targetBeanname" value = "dbpagemanager" /> < /bean id = org.apate.layOt.layOt.layOt.layOt.layOt.paginOt.paginOt.paginOt.paginOt.paginOt.PageAt.paginOt.pageAt.pageAt.paginOt.pageAt.pageAt.pageOt.paginOt.pageAt.pageOt.paginOat. <meta key = "j2: cat" value = "default"/> <constructor-arg index = "0"> <refbean = "org.apache.jetspeed.pageManager"/> </constructor-arg> <constructor-arg index = "1"