/*
* File : ConnectionBean.java
* Created : 21-oct-2003 18:11
* By : fbusquets
*
* JClic - Authoring and playing system for educational activities
*
* Copyright (C) 2000 - 2018 Francesc Busquets & Departament
* d'Educacio de la Generalitat de Catalunya
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details (see the LICENSE file).
*/
package edu.xtec.util.db;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* This class stores an SQL {@link java.sql.Connection} and provides methods to
* obtain {@link java.sql.PreparedStatement} objects from it.
* ConnectionBean
can also store a collection containing all the
* PreparedStatement
objects, allowing client applications to reuse them.
* @author Francesc Busquets (fbusquets@xtec.cat)
* @version 13.09.16
*/
public class ConnectionBean {
private Connection con;
private Map statements;
private Date born;
private Date lastUsed;
private String lastStatement;
private Date closed;
/**
* This field is used by {@link edu.xtec.util.db.ConnectionBeanProvider} to count
* the number of times a ConectionBean has been used.
*/
public int usageCount;
/**
* Creates a new instance of ConnectionBean
* @param con {@link java.sql.Connection} object in wich this ConnectionBean will be based.
* @param mapStatements When set to true, PreparedStatement objects will be stored and reused.
*/
public ConnectionBean(Connection con, boolean mapStatements) {
this.con=con;
if(mapStatements)
statements=new HashMap();
born=new Date();
}
/**
* Provides information about the current state of the ConnectionBean.
* @return Information string, formatted in HTML.
*/
public String getInfo(){
StringBuilder sb=new StringBuilder();
sb.append("ConnectionBean ").append(hashCode());
if(statements!=null){
sb.append(" (").append(statements.size()).append(" statements)");
}
sb.append("
\n");
sb.append("Created: ").append(born).append("
\n");
sb.append("Usage count: ").append(usageCount).append("
\n");
if(lastUsed==null)
sb.append("Not yet used!
\n");
else{
sb.append("Last used: ").append(lastUsed).append("
\n");
sb.append("Last statement: ").append(lastStatement).append("
\n");
}
if(closed!=null){
sb.append("Closed: ").append(closed).append("
\n");
}
return sb.toString();
}
/**
* Provides direct access to the {@link java.sql.Connection} object.
* @return The {@link java.sql.Connection} object used in this ConnectionBean.
*/
public Connection getConnection(){
lastUsed=new Date();
return con;
}
/**
* Creates a PreparedStatement based on the current connection and the provided SQL expression.
* Important: Never call the close() method on Prepared statements obtained
* from ConnectionBean
objects created with the mapStatements
option.
* @param sql The SQL statement.
* @throws SQLException If something goes wrong
* @return The {@link java.sql.PreparedStatement} object.
*/
public PreparedStatement getPreparedStatement(String sql) throws SQLException{
if(closed!=null)
throw new SQLException("Connection closed!");
lastUsed=new Date();
lastStatement=sql;
PreparedStatement stmt=(statements==null) ? null : (PreparedStatement)statements.get(sql);
if(stmt==null){
stmt=con.prepareStatement(sql);
if(statements!=null)
statements.put(sql, stmt);
}
else{
try{
// try - catch because
// odbc.jdbc driver throws NullPoiterException on
// PreparedStatement.clearParameters
//
stmt.clearParameters();
} catch(Exception ex){
// eat exception
}
}
return stmt;
}
/**
* Creates a PreparedStatement based on the current connection and the provided SQL expression.
* Important: Never call the close() method on Prepared statements obtained
* from ConnectionBean
objects created with the mapStatements
option.
* @return The {@link java.sql.PreparedStatement} object.
* @param resultSetType A resultSet type. See {@link java.sql.ResultSet}.
* @param resultSetConcurrency A concurrency type. See {@link java.sql.ResultSet}.
* @param sql The SQL sentence of the PreparedStatemnt.
* @throws SQLException If something goes wrong
*/
public PreparedStatement getPreparedStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException{
if(closed!=null)
throw new SQLException("Connection closed!");
lastUsed=new Date();
lastStatement=sql;
PreparedStatement stmt=(statements==null) ? null : (PreparedStatement)statements.get(sql);
if(stmt==null){
stmt=con.prepareStatement(sql, resultSetType, resultSetConcurrency);
if(statements!=null)
statements.put(sql, stmt);
}
else{
try{
// try - catch because
// odbc.jdbc driver throws NullPoiterException on PreparedStatement.clearParameters
//
stmt.clearParameters();
} catch(Exception ex){
// eat exception
}
}
return stmt;
}
/**
* Closes a {@link java.sql.PreparedStatement} object supplied by a previous call
* to getPreparedStatement()
. Applications should always call this method
* when the PreparedStatement
is no longer needed, and never directly
* call close()
in the PreparedStatement
.
* @param stmt The PreparedStatement
to be closed.
*/
public void closeStatement(PreparedStatement stmt){
closeStatement(stmt, false);
}
/**
* ConnectionBeans
created with the mapStatements
tag set
* to true can reuse {@link java.sql.PreparedStatement} objects between calls.
* This method is functionally identical to closeStatement(PreparedStatement)
, but
* has a param that allows to specify if the statement must be really closed.
* @param stmt The PreparedStatement
to be closed.
* @param forceClose When true, the statement will be effectively closed.
*/
public void closeStatement(PreparedStatement stmt, boolean forceClose){
if(stmt!=null){
if(forceClose || statements==null){
try{
stmt.close();
} catch(Exception ex){
System.err.println("Error closing statement: "+stmt);
}
}
if(statements!=null && forceClose){
for(String s : statements.keySet()){
PreparedStatement ps = statements.get(s);
if(ps!=null && ps==stmt){
statements.remove(s);
break;
}
}
}
}
}
/**
* Clears all the stored {@link java.sql.PreparedStatement} objects (if any) and closes
* the {@link java.sql.Connection}.
* @throws SQLException If something goes wrong.
*/
public void closeConnection() throws SQLException{
if(closed==null){
closed=new Date();
if(statements!=null){
for(String s : statements.keySet()){
PreparedStatement stmt=statements.get(s);
if (stmt != null) {
try {
stmt.close();
} catch (Exception ex) {
// Eat Exception
}
}
}
statements.clear();
}
con.close();
}
}
/**
* Getter for property lastStatement.
* @return Value of property lastStatement.
*/
public java.lang.String getLastStatement() {
return lastStatement;
}
/**
* Returns the number of {@link java.sql.PreparedStatement} objects stored by this
* ConnectionBean
. Used only in connection beans created with the mapStatements
* param set to true
.
* @return The number of stored statements.
*/
public int getNumStatements(){
return statements==null ? 0 : statements.size();
}
}