Transaksi campuran
Dalam transaksi Manajer Transaksi Kerangka ORM, menggunakan JDBCtemplate untuk menjalankan SQL tidak akan dimasukkan dalam manajemen transaksi.
Berikut ini adalah analisis kode sumber untuk melihat mengapa JDBCtemplate harus digunakan dalam transaksi DataSourCetRansactionManager.
1. Mulai transaksi
DataSourCetransactionManager
void dobegin yang dilindungi (transaksi objek, definisi definisi transaksi) {DataSourCetRansactionObjectTXObject = (DataSourCetransactionObject) Transaksi; Koneksi con = null; coba {if (txObject.getConnectionHolder () == null || txObject.getConnectionHolder (). ISSYNCHRONIZEDWithTransAction ()) {connectionNewCon = this.datasource.getConnection (); if (logger.isdebugeNabled ()) {logger.debug ("AcquiredConnection [" + newCon + "] untuk transaksi JDBC"); } txObject.setConnectionHolder (newConnectionHolder (newCon), true); } txObject.getConnectionHolder (). SetSynchronizedWithTransaction (true); con = txObject.getConnectionHolder (). getConnection (); IntegerPreviousisolationLevel = DataSourceutils.PrepareconnectionFortransaction (CON, Definition); txObject.setPreviousisolationLevel (PriSterisolationLevel); // Beralih ke manual yang berkomitmen jika perlu. Ini sangat mahal di beberapa driver JDBC, // jadi kami tidak ingin melakukannya secara tidak perlu (misalnya jika kami telah secara eksplisit // mengonfigurasi kumpulan koneksi untuk mengaturnya). if (con.getautocommit ()) {txObject.setMustrestoreAutocommit (true); if (logger.isdebugeNabled ()) {logger.debug ("switchingjdbc koneksi [" + con + "] ke manual komit"); } con.setAutocommit (false); } txObject.getConnectionHolder (). setTransactionactive (true); int timeout = detectioneTimeout (definisi); if (timeout! = transactionDefinition.timeout_default) {txObject.getConnectionHolder (). setTimeOutInconds (timeout); } // Bind the Sessionholder ke utas. if (txObject.isNewConnectionHolder ()) {TransactionsynchronizationManager.BindResource (getDataSource (), txObject.getConnectionHolder ()); }} catch (Exception ex) {DataSourceutils.Releaseconnection (con, this.datasource); Throw NewcannotCreateTransactionException ("tidak dapat membuka Fortransaction Connection JDBC", EX); }}Metode Dobegin () akan menggunakan kunci nama sumber data dan pemegang koneksi sebagai nilai, dan mengikat koneksi database yang telah dibuka ke variabel threadlocal.
2. Bind koneksi
public static void bindResource (ObjectKey, Nilai Objek) melempar ilegalstateException {objek aktual = transactionsynchronicationutils.unwrapresourceifnecary (key); Assert.notnull (nilai, "nilai tidak boleh nol"); Peta <object, object> peta = resource.get (); // atur ifnone peta threadlocal ditemukan if (peta == null) {map = newhashmap <objek, objek> (); sumber daya.set (peta); } Objek oldValue = Map.put (aktual, nilai); // Menekan aresourceHolder yang secara transparan ditandai sebagai batal ... if (oldValue instance ofResourceHolder && (((sumber daya) oldValue) .isvoid ()) {oldValue = null; } if (oldvalue! = null) {throw newillegalstateException ("sudah nilai [" + oldvalue + "] untuk kunci [" + aktual + "] terikat untuk utas [" + thread.currentThread (). getName () + "]"); } if (logger.istraceEnabled ()) {logger.trace ("boundValue [" + value + "] untuk kunci [" + aktual + "] untuk thread [" + thread.currentThread (). getName () + "]"); }}Variabel sumber daya adalah variabel threadlocal yang disebutkan di atas, sehingga jdbctemplate berikutnya dapat menggunakan sumber data sebagai kunci untuk menemukan koneksi database.
3. Jalankan SQL
Jdbctemplate
Public ObjectExecute (PrepiedStateMentCreator PSC, Persiapan Tindakan Callback) ThrowsDataAccessException {Assert.notnull (PSC, "PrepiedStatementCreator tidak boleh nol"); Assert.notnull (tindakan, "objek callback tidak boleh nol"); if (logger.isdebugeNabled ()) {string sql = getSql (psc); Logger.debug ("Executing Preprepared SQL Pernyataan" + (SQL! = NULL? "[" + SQL + "]": "")); } Connection con = DataSourceutils.getConnection (getDataSource ()); Disiapkan ps = null; coba {koneksi contuse = con; if (this.nativeJdbcextractor! = null && this.nativeJdbcextractor.isnativeConnectionNecessaryFornativeParedStatements ()) {contouse = this.nativeJdbcextractor.getnativeConnection (con); } ps = psc.createPreparedStatement (contouse); ApplyStatementsettings (PS); PreparedStatementspStouse = ps; if (this.nativeJdbcextractor! = null) {pstouse = this.nativeJdbcextractor.getnativePreparedStatement (ps); } Hasil objek = Action.doinPreparedStatement (Pstouse); HandleWarnings (PS); hasil pengembalian; } catch (sqlexception ex) {// Releaseconnection lebih awal, untuk menghindari potensi koneksi unggul kumpulan // dalam kasus ketika penerjemah pengecualian belum diinisialisasi. if (psc instanceofparameterDisposer) {((parameterDisposer) psc) .cleanupparameters (); } String sql = getSql (psc); psc = null; Jdbcutils.closestatement (PS); ps = null; DataSourceutils.Releaseconnection (Con, getDataSource ()); con = null; ThrowGetExceptionTranslator (). Translate ("PrepiedStateMementCallback", SQL, EX); } akhirnya {if (psc instanceofparameterDisposer) {((parameterDisposer) psc) .cleanupparameters (); } Jdbcutils.closestatement (PS); DataSourceutils.Releaseconnection (Con, getDataSource ()); }}
4. Dapatkan koneksi
DataSourceutils
Koneksi Statis Publik DoGetConnection (DataSourCedataSource) melempar SQLException {Assert.notnull (DataSource, "No DataSource ditentukan"); ConnectionHolder conHolder = (connectionHolder) TransactionsynchronizationManager.getResource (DataSource); if (conHolder! = null && (conHolder.hasconnection () || conholder.issynchronizedWithTransaction ())) {conholder.requested (); if (! conholder.hasconnection ()) {logger.debug ("fetchingResumed koneksi JDBC dari DataSource"); conHolder.setConnection (DataSource.getConnection ()); } returnConHolder.getConnection (); } // Kalau tidak, kami mendapat no golder atau pemegang utas yang kosong di sini. Logger.debug ("FetchingJDBC Koneksi dari DataSource"); Koneksi con = DataSource.getConnection (); if (transactionsynchronizationManager.issynchronizationActive ()) {Logger.Debug ("Sinkronisasi RegisteringTransaction untuk koneksi JDBC"); // Gunakan sameconnection untuk tindakan JDBC lebih lanjut dalam transaksi. // Thread-BoundObject akan dihapus dengan sinkronisasi pada penyelesaian transaksi. ConnectionHolderHolderTouse = conholder; 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; } Dapat dilihat bahwa DataSourceutils juga memperoleh koneksi melalui TransactionSynchronizationManager. Oleh karena itu, selama JDBCtemplate dan DataSourCetransactionManager memiliki sumber data yang sama, Anda pasti akan mendapatkan koneksi basis data yang sama dan secara alami Anda dapat mengirimkan dan mengembalikan transaksi dengan benar.
Mari kita ambil hibernate sebagai contoh untuk menggambarkan masalah yang disebutkan di awal, dan lihat mengapa manajer transaksi kerangka kerja ORM tidak dapat mengelola jdbctemplate.
5 ORM Transaction Manager
HibernatetransactionManager
if (txObject.isNewSessionHolder ()) {TransactionsynchronizationManager.BindResource (getSessionFactory (), txObject.getSessionHolder ()); }Karena kerangka kerja ORM tidak secara langsung menyuntikkan sumber data ke TransactionManager untuk digunakan, tetapi menggunakan sessionFactory dan objek lain untuk mengoperasikan sumber data, seperti halnya Hibernate Transaction Manager di atas. Jadi, meskipun sumber data yang mendasari sessionfactory dan jdbctemplate mungkin sama, karena kunci yang berbeda digunakan ketika mengikat dalam transaksi SynchronicationManager (satu adalah nama sesiFactory dan yang lainnya adalah nama data data), jdbctemplate tidak dapat mendapatkan koneksi database yang transaksi transaksi ORM memulai transaksi.
Perbedaan antara kacang
File konfigurasi pegas dalam proyek publik dapat dirujuk oleh banyak proyek. Karena setiap proyek mungkin hanya memerlukan sebagian dari kacang dalam proyek publik, ketika wadah musim semi dari proyek -proyek ini dimulai, perlu untuk membedakan kacang mana yang akan dibuat.
1. Contoh aplikasi
Mengambil konfigurasi di Jetspeed, kerangka kerja open source Apache, sebagai contoh: halaman-manager.xml
<name bean = "xmlpageManager" class = "org.apache.jetspeed.page.psml.castxmlpagemanager" init-method = "init" dash-method = "hancurkan"> <meta key = "j2: cat" value = "xmlpageManager orpageserializer" /J2: Cat "value =" xmlpageManager orpageserializer " </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="destroy"> <meta key = "j2: cat" value = "dbpageManager orpageserializer"/> <!-ojb file konfigurasi resourcePath-> <constructor-arg index = "0"> <value> Jetspeed-inf/ojb/page-manager-repository.xml </value> </constructor-Arg/page-repository.xml </value> </constructor-Arg/page-repository.xml </value> </constructor-Arg/page-repository.xml </value> </constructor-Arg/page-repository.xml </value> </constructor-Arg/page-repository.xml </value> </constructor-Arg/lagr fragor = "" "" "" " bean = "idgenerator"/> </constructor-Arg> ... </tac>
2. Filter BEAN
Ketika jetspeedbeandefinitionfilter mem -parsing setiap definisi kacang dalam wadah musim semi, itu akan mengambil nilai yang sesuai dengan j2: kucing dalam konfigurasi kacang di atas, seperti dbpagemanageror pageserializer. Bagian ini kemudian dicocokkan sebagai ekspresi reguler dengan kunci saat ini (dibaca dari file konfigurasi). Hanya kacang pada pencocokan yang akan dibuat oleh wadah musim semi.
Jetspeedbeandefinitionfilter
Public Boolean Match (Beandefinition bd) {String beancategoriesExpression = (string) bd.getAttribute (kategori_meta_key); Boolean cocok = true; if (beAncategoriesExpression! = null) {dicocokkan = ((pencocokan! = null) && matcher.match (beancategoriesExpression)); } return yang dicocokkan;} public void registerdynamicalias (beandefinitionregistry registry, string beanname, beandefinition bd) {string aliass = (string) bd.getAttribute (alias_meta_key); if (aliass! = null) {stringTokenizer st = newstringTokenizer (alias, ","); while (st.hasmoretokens ()) {string alias = st.nextToken (); if (! alias.equals (beanname)) {registry.registeralias (beanname, alias); }}}} Nilai kategori_meta_key dalam metode match () adalah J2: Cat. Kunci saat ini disimpan di kelas pencocokan, dan bertanggung jawab untuk mencocokkan kunci saat ini dengan ekspresi reguler dari masing -masing kacang.
Peran registerdynamicalia adalah: setelah pertandingan Bean berhasil, Container Spring Container yang disesuaikan akan memanggil metode ini untuk mendaftarkan alias untuk kacang. Untuk detailnya, lihat kode sumber di 1.3 di bawah ini.
3. Kustomisasi Wadah Musim Semi
Kustomisasi wadah musim semi, angkani metode registerBeandefinition (), dan mencegatnya saat musim semi mendaftarkan kacang.
Public Class FilteringXMLWebApplicationContextends XMLWebApplicationContext {private JetSpeedBeandFinitionFilterFilter; PublicFilteringXMLWebApplicationContext (JetspeedBeandefinitionFilter Filter, String [] ConfigLocations, Properties InitProperties, ServletContext ServletContext) {this (filter, configlocations, initproperties, servletContext, null); } publicFilteringXMLWebApplicationContext (JetspeedBeandefinitionFilter Filter, String [] ConfigLocations, Properties InitProperties, ServletContext ServletContext, ApplicationContext Parent) {super (); if (Parent! = null) {this.setParent (Parent); } if (initproperties! = null) {propertiplaceHolderConfigurer ppc = properti baru propertyploveDerConfigurer (); ppc.setignoreUnresolvable placeHolders (true); ppc.setsystempropertiesMode (propertiplaceHolderConfigurer.system_properties_mode_fallback); ppc.setProperties (initproperties); AddBeanFactoryPostProcessor (PPC); } setConfigLocations (configLocations); setSerVletContext (servletContext); this.filter = filter; } Dilindungi DefaultListableBeanFactoryCreateBeanFactory () {return baru filteringListableBeanFactory (filter, getInternalparentBeanFactory ()); }} public classFilteringListableBeanFactory meluas defaultListableBeanFactory {private jetspeedbeandefinitionfilterfilter; public fifteringListableBeanFactory (jetspeedbeandefinitionfilterfilter, beanfactory parentBeanFactory) {super (parentbeanfactory); this.filter = filter; if (this.filter == null) {this.filter = newjetspeedbeandefinitionFilter (); } this.filter.init (); } / ** * Override dari registerbeandefinitionMethod untuk secara opsional menyaring beandefinition dan * jika diminta secara dinamis mendaftar anbean alias * / public void registerbeandefinition (stringBeanName, beandefinition bd) (bd) (bd) (filter.match (snilfinition) (filter.match (filter. super.registerbeandefinition (beanname, bd); if (filter! = null) {filter.registerdynamicalias (this, beanname, bd); }}}} 4. Alias the Bean
Gunakan Beanreferencybean Factory Bean untuk membungkus dua kacang yang dikonfigurasi di atas (XMLPAGEMANGER dan DBPAGEMANGER). Kunci dicocokkan dengan milik mereka sendiri, dan implementasinya adalah beralih di antara dua implementasi dengan mengonfigurasi kunci saat ini. Semua alias dicocokkan menjadi satu, sehingga kacang yang mengacu pada kacang mereka hanya mengutip alias secara langsung. Misalnya, pagelayoutComponent di bawah ini.
halaman-manager.xml
<Bean> <meta key = "j2: cat" value = "xmlpagemanager" /> <meta key = "j2: alias" value = "org.apache.jetspeed.page.pageManager" /<PropertiyName = "TargetBeanName" value = "XMLPAGEMANGER" /< / / /< /bean> < /bean> <meta = "xmlpageManager" /< /bean> < /bean> < /bean> < /bean> <meta = "xmlpageMerager" /< /bean> < /bean> < /bean> < /bean> <meta> <mlpageMager " /< / / /bean> < /bean> < /bean> < /bean> < /bean> <tap key = "j2: cat" value = "dbpageManager" /> <meta key = "j2: alias" value = "org.apache.jetspeed.page.pageManager" /> <propuryname = "targetBeanName" value = "dbpageManager" /< /bean> <bean id = "org.apce. <meta key = "j2: cat" value = "default"/> <constructor-arg index = "0"> <refbean = "org.apache.jetspeed.page.pageManager"/> </konstruktor-arg> <konstruktor- arg = "1"> <value> Jetspeed-layouts :: velocityOnec = velocity-neEnec = velocity-neeNCOn>