J'ai le code suivant:
@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");
}
Je m'attends à ce que les deux lignes execute () réutilisent la même connexion. Cependant, la sortie du journal indique:
2011-02-10 12:24:17 DriverManagerDataSource [INFO] Pilote JDBC chargé: org.h2.Driver 2011-02-10 12:24:17 JdbcTemplate [DEBUG] Exécution d'une instruction SQL [SELECT 1] 2011-02-10 12:24:17 DataSourceUtils [DEBUG] Récupération de la connexion JDBC à partir de DataSource 2011-02-10 12:24:17 DriverManagerDataSource [DEBUG] Création d'une nouvelle connexion JDBC DriverManager vers [jdbc: h2: /data/h2/testa] 2011-02-10 12:24:17 DataSourceUtils [DEBUG] Retour de la connexion JDBC à DataSource 2011-02-10 12:24:17 JdbcTemplate [DEBUG] Exécution d'une instruction SQL [ SELECT 1] 2011-02-10 12:24:17 DataSourceUtils [DEBUG] Récupération de la connexion JDBC à partir de DataSource 2011-02-10 12:24:17 DriverManagerDataSource [DEBUG] Création d'une nouvelle connexion JDBC DriverManager avec [jdbc : h2:/data/h2/testa] 2011-02-10 12:24:17 DataSourceUtils [DEBUG] Retour de la connexion JDBC à DataSource
L'exemple ci-dessus est assez rapide, mais j'ai un morceau de code plus gros qui fait la même chose et reste bloqué longtemps sur Creating new JDBC DriverManager Connection
. Je n'ai jamais d'erreur, mais le code est exécuté très lentement. Puis-je en quelque sorte refactoriser le code ci-dessus pour utiliser simplement la même connexion?
Merci
Voici un exemple utilisant 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 sortie log4j est: -
[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
Spring fournit un DataSource spécial qui vous permet de le faire: SingleConnectionDataSource
Changer votre code pour ceci devrait faire l'affaire:
SingleConnectionDataSource dataSource = new SingleConnectionDataSource();
....
// The rest stays as is
Pour une utilisation dans des applications multithreads, vous pouvez rendre le code réentrant en empruntant une nouvelle connexion du pool et en l'enveloppant dans la section de code gourmande en base de données:
// ... 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
}
En regardant le code du printemps c'est ma compréhension à un niveau élevé.
Vous créez un DriverManagerDataSource . Ceci utilise en interne DataSourceUtils pour obtenir une connexion. Et il ne réutilise la connexion que si une transaction active est en cours. Donc, si vous exécutez les deux exécutions dans une transaction unique, il utilisera la même connexion. Vous pouvez également utiliser le regroupement avec 1 connexion de sorte qu'une seule connexion soit créée et réutilisée.
Vous avez besoin que les appels soient emballés dans une transaction unique. En règle générale, vous le feriez avec l'annotation AOP + @Transactional
de Spring dans une application. Vous pouvez également le faire par programme avec une PlatformTranactionManager
, une TransactionTemplate
et encapsuler le code à exécuter dans une TransactionCallback
. Voir la documentation de la transaction .
Dans un mot, Spring JDBCTemplate DriverManagerDataSource
ne prend pas en charge le pool de connexions. Si vous souhaitez utiliser le pool de connexions, DBCP
et C3P0
sont des choix judicieux.
Passons en revue le code source JDBCTemplate pour voir pourquoi ...
Peu importe l'appel update
, queryForObject
et d'autres méthodes, ils vont finalement appeler la méthode 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());
}
}
Il appelle la méthode DataSourceUtils.getConnection
pour obtenir la connexion et DataSourceUtils.releaseConnection
pour libérer la connexion.
Dans DataSourceUtils code source, nous voyons Connection con = dataSource.getConnection();
et con.close();
.
Ce qui signifie que l'opération de connexion est définie par l'implémentation de l'interface DataSource et que l'opération de connexion fermée est définie par l'implémentation de l'interface Connection . Cela permet à d'autres implémentations DataSource
Connection
d'injecter facilement dans Spring JDBCTemplate.
L'implémentation DataSource
dans Spring JDBCTemplate est _/DriverManagerDataSource . De:
protected Connection getConnectionFromDriverManager(String url, Properties props) throws SQLException {
return DriverManager.getConnection(url, props);
}
Et
public static void doCloseConnection(Connection con, DataSource dataSource) throws SQLException {
if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {
con.close();
}
}
On voit à chaque fois qu'il retourne une nouvelle connexion, et ferme la connexion en cours. C'est pourquoi il ne prend pas en charge le pool de connexion.
Alors que dans DBCP
, l'implémentation DataSource
est PoolingDataSource , nous voyons que getConnection()
provient d'un pool de connexions; l'implémentation Connection
est PoolableConnection , nous voyons que la méthode close()
ne permet pas de fermer la connexion, mais renvoie la connexion au pool de connexions.
C'est la magie!
Je sais que cela est lié à la situation (en fonction du jeu de fonctionnalités que vous souhaitez utiliser), mais vous pouvez simplement utiliser les méthodes JdbcTemplate.batchUpdate
.