私は現在、社内での使用のために作られたインターンが、中小規模のJava Webアプリケーション(普通のJSP /サーブレットのみを使用しています)を維持しています。接続に問題があります。中規模のWebアプリケーションでデータベース接続を適切に処理する方法
「文が閉じている」、「接続が閉じている」などのエラーが表示され、アプリケーション全体が機能しなくなり、サーバーを再起動する必要があります。
私は多くの経験がなく、ベストプラクティスやデザインパターンなどについて教えたり教えたりする人がいませんが、これは正しい方法ではないと確信しています。私はDAL、DAO、DTOのようなものについて読んだことがあります。私たちのアプリはそれらのどれも持っていません。
全体のWebアプリケーション(。つまり、サーブレット)は、基本的に次のような呼び出しで満たされています。
Database db = Database.getInstance();
db.execute("INSERT INTO SomeTable VALUES (a, b, c)");
db.execute("UPDATE SomeTable SET Col = Val");
のSELECTは、そのように行われています。
モデルは、そのクラスでArrayList<Model> results = Model.fetch("SELECT * FROM SomeTable");
をHashMapを拡張し、テーブル内の単一の行を表します。
これはDatabase.javaのコードであり、間違っていることを誰かが指摘できるかどうか疑問に思っていました(私はかなりたくさんあると確信しています)、ベストプラクティスデータベース接続/接続処理に関して。
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;
}
}
ありがとう!
私はあなたのためのApacheコモンズのDbUtils.closeQuietly()を使用することを示唆していますfinallyブロック内のステートメント、結果セット、および接続オブジェクトを閉じます。これにより、close()を呼び出すときにNULLをチェックしSQLExceptionsをトラップしないようにします。 – svachon
これはWebアプリケーションです。これは、任意の所与の時点において、処理される要求がいくつか存在する可能性があることを意味する。同期はありません。また、接続は、複数のスレッドから使用できるDatabaseクラスのメンバー変数です。 – Olaf