namedparameterjdbctemplate mvc journaldev example java spring jdbc

java - mvc - ¿Cómo reutilizar la misma conexión con Spring''s JdbcTemplate?



spring jdbctemplate configuration (6)

Tengo el siguiente código:

@Test public void springTest() throws SQLException{ //Connect to the DB. DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("org.h2.Driver"); dataSource.setUrl("jdbc:h2:/data/h2/testa"); dataSource.setUsername(""); dataSource.setPassword(""); JdbcTemplate jt=new JdbcTemplate(dataSource); jt.execute("SELECT 1"); jt.execute("SELECT 1"); }

Espero que las dos líneas de ejecución () reutilicen la misma conexión. Sin embargo, la salida de registro dice:

2011-02-10 12:24:17 DriverManagerDataSource [INFO] Loaded JDBC driver: org.h2.Driver 2011-02-10 12:24:17 JdbcTemplate [DEBUG] Executing SQL statement [SELECT 1] 2011-02-10 12:24:17 DataSourceUtils [DEBUG] Fetching JDBC Connection from DataSource 2011-02-10 12:24:17 DriverManagerDataSource [DEBUG] Creating new JDBC DriverManager Connection to [jdbc:h2:/data/h2/testa] 2011-02-10 12:24:17 DataSourceUtils [DEBUG] Returning JDBC Connection to DataSource 2011-02-10 12:24:17 JdbcTemplate [DEBUG] Executing SQL statement [SELECT 1] 2011-02-10 12:24:17 DataSourceUtils [DEBUG] Fetching JDBC Connection from DataSource 2011-02-10 12:24:17 DriverManagerDataSource [DEBUG] Creating new JDBC DriverManager Connection to [jdbc:h2:/data/h2/testa] 2011-02-10 12:24:17 DataSourceUtils [DEBUG] Returning JDBC Connection to DataSource

El ejemplo anterior funciona bastante rápido, pero tengo un fragmento de código más grande que hace básicamente lo mismo y se cuelga durante mucho tiempo al Creating new JDBC DriverManager Connection . Nunca recibo un error, pero hace que el código se ejecute muy lentamente. ¿De alguna manera puedo refactorizar el código anterior para simplemente usar la misma conexión?

Gracias


Aquí hay un ejemplo usando Apache DBCP: -

BasicDataSource dbcp = new BasicDataSource(); dbcp.setDriverClassName("com.mysql.jdbc.Driver"); dbcp.setUrl("jdbc:mysql://localhost/test"); dbcp.setUsername(""); dbcp.setPassword(""); JdbcTemplate jt = new JdbcTemplate(dbcp); jt.execute("SELECT 1"); jt.execute("SELECT 1");

La salida de log4j es: -

[DEBUG] [JdbcTemplate] [execute:416] - Executing SQL statement [SELECT 1] [DEBUG] [DataSourceUtils] [doGetConnection:110] - Fetching JDBC Connection from DataSource [DEBUG] [DataSourceUtils] [doReleaseConnection:332] - Returning JDBC Connection to DataSource [DEBUG] [JdbcTemplate] [execute:416] - Executing SQL statement [SELECT 1] [DEBUG] [DataSourceUtils] [doGetConnection:110] - Fetching JDBC Connection from DataSource [DEBUG] [DataSourceUtils] [doReleaseConnection:332] - Returning JDBC Connection to DataSource


En una palabra, Spring JDBCTemplate DriverManagerDataSource no admite el grupo de conexiones. Si desea utilizar el grupo de conexiones, DBCP y C3P0 son buenas opciones.

Repasemos el código fuente de JDBCTemplate para ver por qué ...

No importa la update llamada, queryForObject y otros métodos, finalmente llamarán al método de execute :

@Override public <T> T execute(ConnectionCallback<T> action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); Connection con = DataSourceUtils.getConnection(getDataSource()); try { Connection conToUse = con; if (this.nativeJdbcExtractor != null) { // Extract native JDBC Connection, castable to OracleConnection or the like. conToUse = this.nativeJdbcExtractor.getNativeConnection(con); } else { // Create close-suppressing Connection proxy, also preparing returned Statements. conToUse = createConnectionProxy(con); } return action.doInConnection(conToUse); } catch (SQLException ex) { // Release Connection early, to avoid potential connection pool deadlock // in the case when the exception translator hasn''t been initialized yet. DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex); } finally { DataSourceUtils.releaseConnection(con, getDataSource()); } }

Llama al método DataSourceUtils.getConnection para obtener conexión y DataSourceUtils.releaseConnection para liberar la conexión.

Del código fuente de DataSourceUtils , vemos Connection con = dataSource.getConnection(); y con.close(); .

Lo que significa que la operación de conexión get se define mediante la implementación de la interfaz DataSource , y la operación de conexión cercana se define mediante la implementación de la interfaz de Connection . Esto permite que otras implementaciones de DataSource / Connection puedan inyectar fácilmente en Spring JDBCTemplate.

La implementación de DataSource en Spring JDBCTemplate es DriverManagerDataSource . De:

protected Connection getConnectionFromDriverManager(String url, Properties props) throws SQLException { return DriverManager.getConnection(url, props); }

Y

public static void doCloseConnection(Connection con, DataSource dataSource) throws SQLException { if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) { con.close(); } }

Vemos cada vez que devuelve una nueva conexión y cerramos la conexión actual. Es por eso que no es compatible con el grupo de conexiones.

Mientras que en DBCP , la implementación de DataSource es PoolingDataSource , vemos que getConnection() proviene de un grupo de conexiones; la implementación de Connection es PoolableConnection , vemos que el método close() no es para cerrar la conexión, sino que devuelve la conexión al grupo de conexiones.

Esa es la magia!


Necesita que las llamadas se envuelvan en una sola transacción. Normalmente harías esto con la anotación AOP + @Transactional en una aplicación. También puede hacerlo mediante programación con PlatformTranactionManager , una TransactionTemplate y envolviendo el código para ejecutar en una TransactionCallback . Ver la documentación de la transacción .


Sé que esto es situacional (dependiendo del conjunto de características que quiera usar), pero podría simplemente usar los métodos JdbcTemplate.batchUpdate .


Spring proporciona un DataSource especial que le permite hacer esto: SingleConnectionDataSource

Cambiar tu código a esto debería hacer el truco:

SingleConnectionDataSource dataSource = new SingleConnectionDataSource(); .... // The rest stays as is

Para usar en aplicaciones de subprocesos múltiples, puede hacer que el código vuelva a entrar tomando prestada una nueva conexión del grupo y envolviéndola en la sección de código de la base de datos intensiva:

// ... this code may be invoked in multiple threads simultaneously ... try(Connection conn = dao.getDataSource().getConnection()) { JdbcTemplate db = new JdbcTemplate(new SingleConnectionDataSource(conn, true)); // ... database-intensive code goes here ... // ... this code also is safe to run simultaneously in multiple threads ... // ... provided you are not creating new threads inside here }


Viendo el código de Spring, este es mi entendimiento a un alto nivel.

Está creando un DriverManagerDataSource . Esto internamente usa DataSourceUtils para obtener una conexión. Y solo reutiliza la conexión si hay una transacción activa en progreso. Entonces, si ejecuta los dos ejecutables en una sola transacción, usará la misma conexión. O también puede usar la agrupación con 1 conexión para que se cree y reutilice una sola conexión.