This is the work of a real problem encountered in the processing process, if the analysis process is not interested, you can skip to the final view of the final plan.
We are not using any ORM framework (either Hibernate or MyBatis, or dbutils) in the persistence layer, but instead use a simple wrapper on the jdbctemplate basis, We also decided to set autocommit to true to commit immediately after the SQL statement execution is complete. This is better and more convenient than @transactional annotations or using AOP to control transactional performance.
After evaluating the pros and cons, we begin to use this approach, and we also have a real problem with it. Here we have all the information about the company is hidden, the simplified requirement is: a business to execute two consecutive table insert operation, must be guaranteed to take effect or failure. Of course, the use of compensation can also achieve the effect, but considering that our user volume is not very large, and in the future for a period of time the user will not increase, the use of compensation a little outweigh the benefits, so decided to use the manual control of the transaction under certain circumstances, the rest of the default autocommit is true. After setting this goal, we begin to study if it is done on a jdbctemplate basis.
The first thing to try is to get connection, and set Autocommit to False, the code is as follows:
DataSource DataSource = ((JdbcTemplate) namedparameterjdbctemplate.getjdbcoperations ()). Getdatasource (); Connection Connection = datasourceutils.getconnection (DataSource); Connection.setautocommit (false);
After Setup, the test discovery does not take effect, and it is already known that this is useless. Next we further analyze how to get the database connection in JdbcTemplate, can be viewed by breaking point, each connection object hashcode inconsistent.
We know that namedparameterjdbctemplate This class holds an example of JdbcTemplate , and we're from Namedparameterjdbctemplate 's The update method follows the layers and finds that the following method of the JdbcTemplate class is finally called:
protected int Update (final PreparedStatementCreator psc, Final PreparedStatementSetter pss) throws Dataaccessexceptio N
This method also calls the following method:
Public <T> T Execute (PreparedStatementCreator psc, preparedstatementcallback<t> action) throws DataAccess Exception
In this execute method we find the way JdbcTemplate obtains a database connection, namely:
Connection con = datasourceutils.getconnection (Getdatasource ());
Continue to trace in, found that the final call is datasourceutils the following method:
Public static connection dogetconnection (Datasource datasource) throws Sqlexception { assert.notnull (datasource, "no datasource specified"); ConnectionHolder conHolder = (Connectionholder) Transactionsynchronizationmanager.getresource (DataSource); if (conholder != null && (conholder.hasconnection () | | conholder.issynchronizedwithtransaction ())) { Conholder.requested (); if (!conholder.hasconnection ()) { logger.debug ("Fetching resumed jdbc connection from datasource "); conholder.setconnection ( Datasource.getconnection ()); } return conholder.gEtconnection (); } // else we either got no Holder or an empty thread-bound holder here. logger.debug (" Fetching jdbc connection from datasource "); connection con = datasource.getconnection (); if ( Transactionsynchronizationmanager.issynchronizationactive ()) { Logger.debug ("Registering transaction synchronization for jdbc connection"); // Use same Connection for further JDBC actions within the transaction. // thread-bound Object will get removed by synchronization at transaction completion. connectionholder holdertouse = conholder; if (holdertouse == null) { holdertouse = new connectionholder (Con); } else { holdertouse.setconnection (Con); } Holdertouse.requested (); Transactionsynchronizationmanager.registersynchronization ( new connectionsynchronization (Holdertouse, datasource)); holdertouse.setsynchronizedwithtransaction (true); if (Holdertouse != conholder) { Transactionsynchronizationmanager.bindresource (datasource, holdertouse); &nbsP; } } return con;}
Explain the above code: each time you get a database connection, it is first to determine whether Transactionsynchronizationmanager contains connectionholder, If it contains a straight return, if not included, first obtain a connection from DataSource and then process it in two cases:
When transactionsynchronizationmanager.issynchronizationactive () is true, the Connectionholder is initialized, and call Transactionsynchronizationmanager.bindresource (DataSource, Holdertouse) and complete the binding. As for the scope of the binding, we can see the definition of the variable in the Transacionsynchronizationmanager code.
private static final threadlocal<map<object, object>> resources = new NamedThreadLocal<Map<Object, Object>> (" Transactional resources ");p rivate static final threadlocal<set< transactionsynchronization>> synchronizations = new Namedthreadlocal<set<transactionsynchronization>> ("Transaction synchronizations");p rivate static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal<String> ("Current transaction name");p rivate static final threadlocal<boolean> currenttransactionreadonly = new NamedThreadLocal<Boolean> ("current transaction read-only Status ");p Rivate static final threadlocal<integer> currenttransactionisolationlevel = new Namedthreadlocal<integer> ("Current transaction isolation level");p rivate static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<Boolean> ("actual transaction active");
Are threadlocal, that is, the effective range is within the current thread.
Two do not deal with Connectionholder, return directly to connection.
See here, we must know what to do, and then give us the final modification code (omit all the catch):
transactionsynchronizationmanager.initsynchronization ();D atasource datasource = ((JdbcTemplate) jdbctemplate.getjdbcoperations ()). Getdatasource (); Connection connection = datasourceutils.getconnection (DataSource);try { connection.setautocommit (false); //needs to manipulate two inserts of the database, or provide callbacks to business developers connection.commit ();} catch (sqlexception e) {} finally { try { transactionsynchronizationmanager.clearsynchronization (); } catch (illegalstateexception e) { } try { connection.setautocommit (True); } catch (Sqlexception e) { }}
In conclusion , Spring's JdbcTemplate provides a wealth of operations that are not normally noticed. In the face of problems must not panic, careful analysis of logic, read the source code, I believe the problem will be resolved.
This article is from the "Valley Wind" blog, please be sure to keep this source http://1202955.blog.51cto.com/1192955/1926768
JdbcTemplate manual control of transactions when Autocommit is true