2010-10-20 4 views
2

J'utilise une application Spring MVC, soutenue par une base de données MySQL à laquelle j'accède en utilisant JDBC. J'utilise le même code depuis un moment, et je n'ai jamais vraiment cherché à savoir si je l'utilisais correctement ou non (en utilisant les pools de connexion correctement, etc).Utilisation incorrecte du pool de connexions JDBC

Je suis conscient qu'il ya le JdbcTemplate là-bas, et je l'ai envisagé d'utiliser, mais si le seul avantage est que j'aurais juste de ne pas écrire le code passe-partout pour, alors je ne suis pas tout à fait convaincu que je devrait l'utiliser. En fait, je préfère la lisibilité de mon code sur le code JDBCTemplate. Ci-dessous le code dans mon DAO, pour une raison quelconque, j'ai l'impression de ne pas utiliser ConnectionPooling correctement.

public Agent get(Integer id){ 
    ConnectionPool pool = new ConnectionPool(); 
    Connection connection = pool.getConnection(); 
    PreparedStatement ps = null; 
    try{ 
     String query = "SELECT * FROM agent where id= ?"; 
     ps = connection.prepareStatement(query); 
     ps.setInt(1,id); 
     ResultSet rs = ps.executeQuery(); 

     Agent agent = null; 
     if(rs.next()){ 
      agent = new Agent(); 
      agent.setFirstName(rs.getString(1)); 
      agent.setLastName(rs.getString(2)); 
      agent.setStreet(rs.getString(3)); 
      agent.setCity(rs.getString(4)); 
      agent.setZip(rs.getString(5)); 
      agent.setState(rs.getString(6)); 
      agent.setUsername(rs.getString(7)); 
      agent.setPassword(rs.getString(8)); 
      agent.setId(rs.getInt(9)); 
      agent.setEmail(rs.getString(10)); 
     } 
     return agent; 
    } 
    catch(SQLException e) 
    { 
     e.printStackTrace(); 
     return null; 
    } 
    finally{ 
     ConnectionUtility utility = new ConnectionUtility(); 
     utility.closePreparedStatement(ps); 
     pool.freeConnection(connection); 
    } 
} 

Le code ci-dessus est ce que je suis inquiet pour le plus d'être incorrect, mais j'ai quelques classes utilitaires qui peuvent également contribuer à une mauvaise pratique/code incorrect. Ci-dessous est ConnectionUtility classe.

public class ConnectionUtility{ 

public static void closeStatement(Statement s){ 
    try{ 
     if(s != null){ 
      s.close(); 
     } 
    } 
    catch(SQLException e){ 
     e.printStackTrace(); 
    } 
} 
public void closePreparedStatement(Statement ps){ 
    try{ 
     if(ps != null){ 
      ps.close(); 
     } 
    } 
    catch(SQLException e){ 
     e.printStackTrace(); 
    } 
} 
public static void closeResultSet(ResultSet rs){ 
    try{ 
     if(rs != null){ 
      rs.close(); 
     } 
    } 
    catch(SQLException e){ 

    } 
} 

} 

Voici mon ConnectionPool classe,

public class ConnectionPool { 
private static ConnectionPool pool = null; 

public ConnectionPool(){ 
} 
public static ConnectionPool getInstance(){ 
    if(pool == null){ 
     pool = new ConnectionPool(); 
    } 
    return pool; 
} 

@SuppressWarnings("static-access") 
public Connection getConnection(){ 
    try{ 

     return ConnectionFactory.getInstance().getConnection(); 
    } 
    catch(SQLException e){ 
     e.printStackTrace(); 
     return null; 
    } 

} 
public void freeConnection(Connection c){ 
    try{ 
     c.close(); 
    } 
    catch(SQLException e){ 
     e.printStackTrace(); 
    } 
} 
} 

Encore une fois, je me sens comme si je me sers vraiment toutes ces classes mal, même si tout fonctionne bien, mais rien n'a été mis à le test en production. Je préfère rester avec JDBC, donc s'il vous plaît pas de conseils sur le passage à l'autre.

+1

Jeez, installez-vous. La question était sur la façon d'améliorer son code. Votre commentaire n'est pas utile. –

+0

J'améliore son code. Utiliser Spring sera une énorme amélioration par rapport à cela. C'est plus utile que vous le savez. – duffymo

+3

Suggérer le printemps est utile. Lui dire que son code est horrible, vous ne travaillerez jamais avec lui, et qu'il n'aurait pas dû recevoir son diplôme n'est pas. –

Répondre

6

L'un des noyau principes du ressort est dependency injection. Spring est basé sur la croyance que l'injection des composants qu'une classe utilise dans cette classe conduit à un code plus facile à lire, plus facile à tester et plus facile à gérer que les classes/codes (comme la méthode get()) responsable de trouver leurs propres dépendances.

Comme exemple de ce que cela signifie concrètement: votre méthode get() dépend d'au moins deux autres classes: 1) le "pool de connexion" et 2) la connexion elle-même. La méthode get() a une connaissance intime de comment il doit obtenir ces instances.

Comme alternative à votre style de codage ici, avec l'approche DI la classe qui est propriétaire de votre méthode get() aurait Connection (ou Datasource) injecté dans (via une injection de setter ou constructeur).

Maintenant, pourquoi ce simple changement rend-il le code plus facile et meilleur? Parce que la méthode get() ne doit plus se préoccuper des détails qui ne relèvent pas de sa responsabilité principale. La principale responsabilité de la méthode get() est de savoir comment obtenir un Agent étant donné un Integer id. Pourquoi cette méthode doit-elle également savoir 1) où se connecter et 2) si vous voulez mettre en commun les connexions? Que se passe-t-il lorsque vous voulez changer cette logique de connexion? Vous devez appuyer sur le code de chaque et sur chaque méthode d'accès aux données dans votre application. Ce serait un changement beaucoup plus difficile qu'il ne devrait l'être. C'est la puissance de l'injection de dépendance: elle vous permet de changer les détails (comme d'où provient une connexion JDBC) sans avoir à changer le code qui utilise ces informations.

En ce qui concerne votre code de pool de connexion réelle, il semble que vous comprenez mal deux concepts:

1) Votre ConnectionPool prétend vouloir être un Singleton mais vous exposer un constructeur public, ce qui permet des collaborateurs aux réglages par défaut complètement le but d'avoir une seule instance de ConnectionPool.

2) Votre pool de connexions n'est pas réellement un pool de connexions! L'idée d'un pool de connexions est d'ouvrir N connexions à une base de données, puis de distribuer chacune de ces N connexions au code qui a besoin d'une connexion à la demande. L'idée de base ici est que vous pouvez recycler les connexions et éviter les coûts élevés d'ouverture d'une nouvelle connexion pour chaque demande. Dans un pool de connexions, lorsque le code utilisant une connexion est fait avec sa connexion, la connexion physique n'est pas réellement terminée - à la place, le handle de connexion est simplement renvoyé au pool pour être réutilisé par une autre demande/thread/méthode.

Plus important encore, dans les applications qui utilisent un pool de connexion le code responsable de l'accès aux données généralement ne connaît même pas ses connexions sont regroupées - au contraire, l'OFA a simplement une référence à une interface DataSource et le DAO a aucune idée de ce qui se passe réellement quand il demande le DataSource pour une connexion ou ce qui se passe lorsque la connexion est libérée. Ainsi, vous pouvez faire abstraction des détails de "comment me connecter" du code responsable de la logique d'ordre supérieur tel que "comment puis-je obtenir un agent à partir de cet entier?". Cette abstraction est ce qui vous permet de changer une couche de votre application sans réécrire toutes les autres couches - vous avez découplé les couches et chacune n'est concernée que par ce dont elle est réellement responsable.

Je vous recommande vivement de faire plus de lecture non seulement sur l'idée d'un pool de connexion, mais aussi sur Dependency Injection. Pourquoi dans le monde utiliseriez-vous Spring sans les composants DI?En ce qui concerne les pools de connexion, pourquoi passer du temps à réinventer la roue en écrivant la vôtre au lieu d'utiliser un certain nombre de bibliothèques déjà existantes et populaires telles que commons-dbcp ou c3p0? Au lieu de réinventer la roue, utilisez une bibliothèque existante (qui est moins susceptible d'avoir des bugs que votre solution maison) et concentrez-vous sur la construction de votre application actuelle.

3

Il y a deux problèmes avec votre code:

1) Vous créez un nouvel objet ConnectionPool pour chaque appel à get(). Votre classe ConnectionPool est conçue pour être un singleton, donc renforcez-la en rendant le constructeur privé et changez votre code client en ConnectionPool pool = ConnectionPool.getInstance().

2) Fondamentalement, le même problème avec ConnectionUtility. Vous avez des méthodes statiques, mais vous les utilisez de manière non statique. Remplacer

ConnectionUtility utility = new ConnectionUtility(); 
utility.closePreparedStatement(ps); 

avec

ConnectionUtility.closePreparedStatement(ps); 

et rendre privées et la finale de la classe du constructeur ConnectionUtility.

Les singletons et les classes utilitaires peuvent être difficiles à résoudre, aussi je vous recommande d'utiliser des outils d'analyse statiques tels que Findbugs pour détecter ce genre de problèmes. Il vous avertira lorsque vous avez un cours qui semble être un singleton ou une classe d'utilité mais qui n'est pas utilisé de cette façon.

Questions connexes