La bonne manière de gérer les connexions de base de données dans un support de taille de l'application web
Je suis en train de maintien de petites et moyennes applications web java (à l'aide de simples pages Jsp/Servlets) que stagiaire faite pour l'usage interne d'une entreprise et je vais avoir quelques problèmes avec les connexions.
Parfois juste sorti de nulle part, nous obtenez des erreurs telles que "l'Instruction est fermée" ou "la Connexion est fermée", puis l'ensemble de l'application serait juste arrêter de travailler et le serveur doit être redémarré.
Je n'ai pas beaucoup d'expérience et je n'ai personne pour les encadrer ou de m'enseigner ce qui concerne les meilleures pratiques, des modèles de conception, etc. mais je suis sûr que ce n'est pas la bonne façon de le faire. J'ai lu sur des trucs comme DALs, DAOs, et de l'Otd. Notre application a aucun de ceux-ci.
L'ensemble de l'application web (ie. les servlets) sont essentiellement remplis avec des appels semblable à la suivante:
Database db = Database.getInstance();
db.execute("INSERT INTO SomeTable VALUES (a, b, c)");
db.execute("UPDATE SomeTable SET Col = Val");
Sélectionne sont fait comme suit:
ArrayList<Model> results = Model.fetch("SELECT * FROM SomeTable");
Où le Modèle est une classe qui étend la classe HashMap et représente une seule Ligne dans une Table.
C'est le code pour Database.java et je me demandais si quelqu'un peut les choses évidentes qui sont mauvais (je suis assez sûr il y en a beaucoup), toute solution rapide qui peut être fait et certaines ressources sur les bonnes pratiques en ce qui concerne les connexions de base de données /gestion des connexions.
package classes;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
public final class Database {
public static Database getInstance() {
if (Database.instance == null) {
Database.instance = new Database();
}
return Database.instance;
}
//Returns the results for an SQL SELECT query.
public ArrayList<HashMap<String, Object>> fetch(String sql) {
ArrayList<HashMap<String, Object>> results = new ArrayList<HashMap<String, Object>>();
try {
PreparedStatement stmt = this.connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
ResultSet rs = stmt.executeQuery();
this.doFetch(rs, results);
stmt.close();
} catch (SQLException e) {
this.handleException(e, sql);
}
return results;
}
public ArrayList<HashMap<String, Object>> fetch(String sql, ArrayList<Object> parameters) {
ArrayList<HashMap<String, Object>> results = new ArrayList<HashMap<String, Object>>();
try {
//Bind parameters to statement.
PreparedStatement pstmt = this.connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
for (int i=0; i<parameters.size(); i++) {
pstmt.setObject(i+1, parameters.get(i));
}
ResultSet rs = pstmt.executeQuery();
this.doFetch(rs, results);
pstmt.close();
} catch (SQLException e) {
this.handleException(e, sql, parameters);
}
return results;
}
public int execute(String sql) {
int result = 0;
try {
Statement stmt = this.connection.createStatement();
result = stmt.executeUpdate(sql);
stmt.close();
} catch (SQLException e) {
this.handleException(e, sql);
}
return result;
}
public int execute(String sql, ArrayList<Object> parameters) {
int result = 0;
try {
PreparedStatement pstmt = this.connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
for (int i=0; i<parameters.size(); i++) {
if (parameters.get(i) == null) {
pstmt.setNull(i+1, java.sql.Types.INTEGER);
} else {
pstmt.setObject(i+1, parameters.get(i));
}
}
result = pstmt.executeUpdate();
pstmt.close();
} catch (SQLException e) {
this.handleException(e, sql, parameters);
}
return result;
}
public void commit() {
try {
this.connection.commit();
} catch (SQLException e) {
System.out.println("Failed to commit transaction.");
}
}
public Connection getConnection() {
return this.connection;
}
private static Database instance;
private static DataSource dataSource = null;
private Connection connection;
private Database() {
this.connect();
this.execute("SET SCHEMA " + Constant.DBSCHEMA);
}
private void connect() {
Connection connection = null;
if (dataSource == null) {
try {
InitialContext initialContext = new InitialContext();
dataSource = (DataSource)initialContext.lookup(
Constant.DEPLOYED ? Constant.PROD_JNDINAME : Constant.TEST_JNDINAME);
} catch (NamingException e) {
e.printStackTrace();
}
}
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
this.connection = connection;
}
//Fetches the results from the ResultSet into the given ArrayList.
private void doFetch(ResultSet rs, ArrayList<HashMap<String, Object>> results) throws SQLException {
ResultSetMetaData rsmd = rs.getMetaData();
ArrayList<String> cols = new ArrayList<String>();
int numCols = rsmd.getColumnCount();
for (int i=1; i<=numCols; i++) {
cols.add(rsmd.getColumnName(i));
}
while (rs.next()) {
HashMap<String, Object> result = new HashMap<String, Object>();
for (int i=1; i<=numCols; i++) {
result.put(cols.get(i-1), rs.getObject(i));
}
results.add(result);
}
rs.close();
}
private void handleException(SQLException e, String sql) {
System.out.println("SQLException " + e.getErrorCode() + ": " + e.getMessage());
System.out.println("Statement: " + sql);
ExceptionAdapter ea = new ExceptionAdapter(e);
ea.setSQLInfo(e, sql);
throw ea;
}
private void handleException(SQLException e, String sql, ArrayList<Object> parameters) {
if (parameters.size() < 100) {
System.out.println("SQLException " + e.getErrorCode() + ": " + e.getMessage());
System.out.println("PreparedStatement: " + sql.replace("?", "[?]"));
System.out.println("Parameters: " + parameters.toString());
}
ExceptionAdapter ea = new ExceptionAdapter(e);
ea.setSQLInfo(e, sql, parameters);
throw ea;
}
}
Merci!
OriginalL'auteur Connection | 2011-07-08
Vous devez vous connecter pour publier un commentaire.
La classe n'a jamais, jamais fermer une connexion:
this.connection.close()
. CommeDatabase
est un Singleton l'application n'utilise pas la connexion à la piscine (la source de données). Une seule connexion est utilisé pour toutes les demandes entrantes.Règle de base: obtenir une connexion par méthode (peut-être par instruction SQL).
dataSource.getConnection()
n'est pas cher.C'est de savoir comment je refactoriser la classe:
getConnection
méthode, si elle est utilisée à l'extérieur de laDatabase
classe vous avez vraiment un problème de conceptioncommit
méthode. Je suppose qu'il n'a pas de sens en tant queconnection.setAutoCommit(false)
n'est jamais appelé, et je ne vois pasrollback
méthodeconnection
, au lieu d'obtenir une connexion par appelfinally
bloc de chaque appelAvertissement: Aucune idée de la façon dont votre transaction, la manipulation fonctionne pour le moment, donc j'ai peut-être tort avec #2.
Exemple de code pour une méthode pour obtenir une connexion:
Intéressant de voir comment cette application peut fonctionner à tous 🙂
OriginalL'auteur home
Il n'y a rien de mal avec cette façon de faire pour de simples applications par dire. Mais si votre application est même modérément complexe, vous voudrez peut-être chercher dans un cadre simple comme iBatis.
Un couple de choses que j'ai serait certainement faire bien. Pour l'un, l'application pourrait être une fuite de connexions lorsqu'une exception est levée le faire à la manière dont les états sont en cours de fermeture. Tous les proches des états doivent être déplacés à l'intérieur enfin blocs.
Ainsi, au lieu de:
Faire ceci à la place:
L'autre chose est, je voudrais assurez-vous que vous utilisez une connexion de base de données de la piscine pour votre source de données. Si vous exécutez ce dans Tomcat, heureusement qu'il y a un pool de connexions définies dans l'installation de tomcat, et que votre demande est en les utilisant.
EDIT: Après avoir regardé de nouveau le code, je suis aussi de ne pas voir où la connexion de base de données est fermée. C'est probablement la raison pour laquelle vous exécutez hors de connexions. Vous avez besoin d'ajouter une méthode de fermeture de la classe de Base de données, que les appels de connexion.close(). Et assurez-vous que vous appelez lorsque vous avez terminé avec vos requêtes. Encore une fois, dans un try/finally bloc.
C'est une application web. Cela signifie qu'à tout moment il peut y avoir plusieurs requêtes en cours de traitement. Il n'y a pas de synchronisation et de connexion est une variable membre de la classe de Base de données qui peut être utilisé à partir de plusieurs threads.
OriginalL'auteur Nobody
Vous utilisez la connexion JDBC dans un très dangereux. Il peut être consulté à partir de plusieurs threads, et il n'est pas thread-safe. C'est une application web et plusieurs demandes peuvent provenir de différents utilisateurs en même temps. C'est un petit miracle de votre demande est de ne pas tomber de plus en plus fréquemment. Vous pouvez utiliser plusieurs stratégies pour résoudre ce problème. Vous pouvez stocker des connexions dans un ThreadLocal ou sur la pile. Si vous allez garder des connexions sur la pile, vous devrez les ouvrir et les fermer à l'intérieur de chaque appel de méthode. Pour cela à un impact sur les performances, vous aurez à utiliser le pool de connexions. Le pool de connexion ne ferait pas de mal en tout cas.
OriginalL'auteur Olaf
Pour répondre à votre question au sujet des principes de conception, cet objet est essentiellement un objet DAO, il n'utilisez pas la convention de nommage, également une grande application qui aurait plusieurs de ces objets pour les différents types de données (peut-être l'aide d'un DAO de base de l'objet qu'ils héritent tous de).
L'idée générale est qu'UN DAO est un lieu central où les connexions de base de données sont traitées de sorte que vous n'avez pas ce code dans un objet Contrôleur.
Outre les lacunes déjà souligné par d'autres, c'est un code écrit par quelqu'un qui comprend la programmation orientée objet, très bien. Ma recommandation est de changer l'objet d'un singleton, et de gérer la connexion de base de données avec un pool de connexion(comme d'autres l'ont déjà mentionné).
Cela semble être une très abstraite de l'objet qui renvoie une liste de tableaux, de cartes(paires clé-valeur), qui peut être utilisé pour différents types de données qui est ensuite utilisé dans le Modèle d'objets ou de types de données pour construire des objets java avec les informations renvoyées.
OriginalL'auteur Jamy Spencer