001: /*
002: * Created on 03/01/2004
003: *
004: * Swing Components - visit http://sf.net/projects/gfd
005: *
006: * Copyright (C) 2004 Igor Regis da Silva Simões
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or (at your option) any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: *
022: */
023:
024: package br.com.gfpshare.db;
025:
026: import java.io.File;
027: import java.io.FileInputStream;
028: import java.io.FileOutputStream;
029: import java.io.IOException;
030: import java.sql.Connection;
031: import java.sql.PreparedStatement;
032: import java.sql.ResultSet;
033: import java.sql.SQLException;
034: import java.sql.Statement;
035: import java.sql.Types;
036: import java.util.ArrayList;
037: import java.util.Calendar;
038: import java.util.Date;
039: import java.util.EventListener;
040: import java.util.Properties;
041: import java.util.WeakHashMap;
042:
043: import javax.swing.event.EventListenerList;
044:
045: /**
046: * Classe responssável por gerenciar as transações com o Banco de Dados.
047: *
048: * @author Igor Regis da Silva Simões
049: */
050: public class DataBaseManager {
051:
052: /**
053: * Constante que representa o nome do banco de dados.
054: */
055: public static final String NOME_BANCO_DE_DADOS = "nome.banco.de.dados";
056:
057: /**
058: * Constante que representa a propriedade que indica
059: * o driver a ser usado para se conectar ao banco de dados.
060: */
061: public static final String DRIVER = "driver";
062:
063: /**
064: * Constante que representa a propriedade que indica
065: * o numero inicial de conexões que podem ser abertas com o Banco de dados.
066: */
067: public static final String CONEXOES_INICIAIS = "conexoes.iniciais";
068:
069: /**
070: * Constante que representa a propriedade que indica
071: * o numero máximo de conexões que podem ser abertas com o Banco de dados.
072: */
073: public static final String CONEXOES_MAXIMAS = "conexoes.maximas";
074:
075: /**
076: * Constante que representa a propriedade que indica
077: * se o pool de conexões deve aguardar a libração de uma conexão ou lançar
078: * uma exceção quando mão houver conexões disponíveis.
079: */
080: public static final String AGUARDAR_SE_OCUPADO = "aguardar.se.ocupado";
081:
082: /**
083: * Constante que representa a propriedade que indica
084: * se as conexões com o banco de dados serão do tipo auto commit.
085: *
086: * @see java.sql.Connection
087: */
088: public static final String AUTO_COMMIT = "auto.commit";
089:
090: /**
091: * Constante que representa a propriedade que indica
092: * a versão do layout do banco de dados em uso
093: *
094: * @see java.sql.Connection
095: */
096: public static final String VERSAO_DB = "versao.db";
097:
098: /**
099: * Pool de conexões com o banco de dados.
100: */
101: private ConnectionPool pool = null;
102:
103: /**
104: * Armazena as propiedades contidas no arquivo de propriedades de banco de dados.
105: */
106: private final Properties propriedades = new Properties();
107:
108: /**
109: * Endereço do arquivo de propriedades de banco de dados.
110: */
111: private static File arquivoPropriedades = null;
112:
113: /**
114: * Statment responsável por manter aberto os recursos com o BD.
115: */
116: private Statement statement = null;
117:
118: /**
119: * Quantidades de ResultSets abertos
120: */
121: private int resultSetCount = 0;
122:
123: /**
124: * Instancia única do DataBaseManager.
125: */
126: private static DataBaseManager dbManager;
127:
128: /**
129: * Lista de listeners de eventos do controller
130: */
131: private final EventListenerList listeners = new EventListenerList();
132:
133: /**
134: * Cache de prepared statements usados para acessar os DB
135: */
136: private WeakHashMap<String, PreparedStatement> statementCache = new WeakHashMap<String, PreparedStatement>();
137:
138: /**
139: * A conexão usada para manter todos os prepared statements
140: */
141: private Connection preapredStatementsConnection = null;
142:
143: /**
144: * Cria uma nova instância de DataBaseManager
145: * @param arquivoDeConfiguracao caminho para o arquivo de configuração de banco de dados
146: * @throws IOException exceção que pode ser lançada ao ler o arquivo de configuração
147: * @throws SQLException exceção que pode ser lançada durante a tentativa de se conectar ao banco de dados
148: */
149: private DataBaseManager(String arquivoDeConfiguracao, String dbDir)
150: throws IOException, SQLException {
151: arquivoPropriedades = new File(arquivoDeConfiguracao);
152: if (!arquivoPropriedades.exists())
153: criarArquivoPropriedades();
154:
155: //Carregamos as configurações de banco de dados a partir do arquivo de configurações
156: FileInputStream fis = new FileInputStream(arquivoPropriedades);
157: propriedades.load(fis);
158: fis.close();
159: pool = new ConnectionPool(propriedades.getProperty(DRIVER),
160: "jdbc:hsqldb:" + dbDir + File.separator + "db", "sa",
161: "", Integer.parseInt(propriedades
162: .getProperty(CONEXOES_INICIAIS)), Integer
163: .parseInt(propriedades
164: .getProperty(CONEXOES_MAXIMAS)),
165: propriedades.getProperty(AGUARDAR_SE_OCUPADO)
166: .equalsIgnoreCase("sim") ? true : false,
167: propriedades.getProperty(AUTO_COMMIT).equalsIgnoreCase(
168: "sim") ? true : false);
169: }
170:
171: /**
172: * Cria um novo arquivo de propriedades de banco de dados com valores default.
173: * @throws IOException em caso de falha na leitura do arquivo de configuração
174: */
175: private void criarArquivoPropriedades() throws IOException {
176: try {
177: arquivoPropriedades.createNewFile();
178: } catch (IOException ioe) {
179: //TODO Tratamento de exceção
180: }
181:
182: propriedades.put(NOME_BANCO_DE_DADOS, "HSQLDB");
183: propriedades.put(DRIVER, "org.hsqldb.jdbcDriver");
184: propriedades.put(CONEXOES_INICIAIS, "1");
185: propriedades.put(CONEXOES_MAXIMAS, "10");
186: propriedades.put(AGUARDAR_SE_OCUPADO, "não");
187: propriedades.put(AUTO_COMMIT, "sim");
188: propriedades.put(VERSAO_DB, "14");
189:
190: FileOutputStream fos = new FileOutputStream(arquivoPropriedades);
191: propriedades.store(fos, "Configuração do Banco do Dados");
192: fos.flush();
193: fos.close();
194: }
195:
196: /**
197: * Cria uma nova instância de DataBaseManager
198: * @param arquivoDeConfiguracao caminho para o arquivo de configuração de banco de dados
199: * @return Nova instância de DataBaseManager
200: * @throws IOException exceção que pode ser lançada ao ler o arquivo de configuração
201: * @throws SQLException exceção que pode ser lançada durante a tentativa de se conectar ao banco de dados
202: */
203: public synchronized static DataBaseManager getDataBaseManager(
204: String arquivoDeConfiguracao, String dbDir)
205: throws IOException, SQLException {
206: if (dbManager == null)
207: dbManager = new DataBaseManager(arquivoDeConfiguracao,
208: dbDir);
209: return dbManager;
210: }
211:
212: /**
213: * Cria uma nova instância de DataBaseManager
214: * @return Nova instância de DataBaseManager
215: */
216: public synchronized static DataBaseManager getDataBaseManager() {
217: if (dbManager == null)
218: throw new RuntimeException(
219: "Chame o método DataBaseManager(String arquivoDeConfiguracao), para poder carregar o arquivo de configuração");
220: return dbManager;
221: }
222:
223: /**
224: * Executa uma consulta ao banco de dados
225: * @return O resultado da consulta
226: * @param sql o query SQL a ser executada como consulta
227: * @throws SQLException Lança uma exceção caso ocorra erro durante a consulta
228: */
229: public synchronized ResultSet executarConsulta(String sql,
230: ArrayList<Object> params) throws SQLException {
231: PreparedStatement preparedStatement = null;
232: if ((preparedStatement = statementCache.get(sql)) == null) {
233: try {
234: if (preapredStatementsConnection == null)
235: preapredStatementsConnection = pool.getConnection();
236: preparedStatement = preapredStatementsConnection
237: .prepareStatement(sql,
238: ResultSet.TYPE_SCROLL_SENSITIVE,
239: ResultSet.CONCUR_UPDATABLE);
240: statementCache.put(sql, preparedStatement);
241: } catch (SQLException sqle) {
242: try {
243: if (statement != null) {
244: pool.free(statement.getConnection());
245: statement.close();
246: }
247: } catch (SQLException e) {
248: }
249: throw sqle;
250: }
251: }
252: preparedStatement.clearParameters();
253: try {
254: loadValues(preparedStatement, params);
255: } catch (SQLException ee) {
256: System.out.println("********* ERRO " + sql);
257: System.out.println(ee.getMessage());
258: for (Object par : params)
259: System.out.println(par);
260: throw ee;
261: }
262: fireExecutandoConsulta(sql);
263: ResultSet resultado = preparedStatement.executeQuery();
264: resultSetCount++;
265: return resultado;
266: }
267:
268: private void loadValues(PreparedStatement preparedStatement,
269: ArrayList<Object> params) throws SQLException {
270: int i = 1;
271: for (Object param : params) {
272: if (param instanceof Float)
273: preparedStatement.setFloat(i++, (Float) param);
274: else if (param instanceof Double)
275: preparedStatement.setDouble(i++, (Double) param);
276: else if (param instanceof Boolean)
277: preparedStatement.setBoolean(i++, (Boolean) param);
278: else if (param instanceof Byte)
279: preparedStatement.setByte(i++, (Byte) param);
280: else if (param instanceof Date) {
281: Date dia = (Date) param;
282: Calendar dadoCalendar = Calendar.getInstance();
283: dadoCalendar.setTime(dia);
284: //Ano 1000 será considerado para setar valor igual a NULL
285: if (dadoCalendar.get(Calendar.YEAR) == 1000)
286: preparedStatement.setNull(i++, Types.NULL);
287: else
288: preparedStatement.setDate(i++, new java.sql.Date(
289: ((Date) param).getTime()));
290: } else if (param instanceof Integer)
291: preparedStatement.setInt(i++, (Integer) param);
292: else if (param instanceof Long)
293: preparedStatement.setLong(i++, (Long) param);
294: else if (param instanceof String) {
295: if (param.equals("**NULL**"))
296: preparedStatement.setNull(i++, Types.NULL);
297: else
298: preparedStatement.setString(i++, param.toString());
299: } else if (param instanceof Short)
300: preparedStatement.setShort(i++, (Short) param);
301: else
302: preparedStatement.setObject(i++, param);
303: }
304: }
305:
306: /**
307: * Executa um comando de update no banco de dados.
308: * @return o mesmo retorno do método <code>executeUpdate</code> da classe Statement
309: * @see java.sql.Statement
310: * @param sql O SQL a ser executado
311: * @throws SQLException Lança uma exceção caso ocorra um erro durante a execução do SQl
312: */
313: public synchronized int executarAtualizacao(String sql,
314: ArrayList<Object> params) throws SQLException {
315: if (params == null)
316: return executeStatement(sql);
317: PreparedStatement preparedStatement = null;
318: if ((preparedStatement = statementCache.get(sql)) == null) {
319: try {
320: if (preapredStatementsConnection == null)
321: preapredStatementsConnection = pool.getConnection();
322: preparedStatement = preapredStatementsConnection
323: .prepareStatement(sql,
324: ResultSet.TYPE_SCROLL_SENSITIVE,
325: ResultSet.CONCUR_UPDATABLE);
326: statementCache.put(sql, preparedStatement);
327: } catch (SQLException sqle) {
328: if (statement != null) {
329: try {
330: pool.free(statement.getConnection());
331: statement.close();
332: } catch (SQLException e) {
333: sqle.setNextException(e);
334: }
335: }
336: throw sqle;
337: }
338: }
339: preparedStatement.clearParameters();
340: loadValues(preparedStatement, params);
341: int i = preparedStatement.executeUpdate();
342: fireExecutadoAtualizacao(sql);
343: return i;
344: }
345:
346: /**
347: * Executa um statement sem ser prepared
348: * @param sql
349: * @return
350: * @throws SQLException
351: */
352: private int executeStatement(String sql) throws SQLException {
353: if (statement == null) {
354: try {
355: statement = pool.getConnection().createStatement(
356: ResultSet.TYPE_SCROLL_SENSITIVE,
357: ResultSet.CONCUR_UPDATABLE);
358: } catch (SQLException sqle) {
359: if (statement != null) {
360: try {
361: pool.free(statement.getConnection());
362: statement.close();
363: } catch (SQLException e) {
364: sqle.setNextException(e);
365: }
366: }
367: throw sqle;
368: }
369: }
370: int i = 0;
371: try {
372: i = statement.executeUpdate(sql);
373: } catch (SQLException e) {
374: if (e.getMessage().equals("Statement is closed")) {
375: statement = null;
376: executeStatement(sql);
377: } else
378: throw e;
379: }
380: fireExecutadoAtualizacao(sql);
381: return i;
382: }
383:
384: /** Fecha o Statement e libera a conexão para reuso.
385: * @param lista ResultSet asossiado a uma conexão com um banco de dados
386: * @throws SQLException Lança uma exceção caso ocorra um erro durante o fechamento da execução do comando
387: */
388: public synchronized void fecharConexao(ResultSet lista)
389: throws SQLException {
390: if (lista != null) {
391: if (--resultSetCount >= 0) {
392: return;
393: }
394: if (statement != null) {
395: pool.free(statement.getConnection());
396: statement.close();
397: statement = null;
398: }
399: }
400: }
401:
402: /**
403: * Retorna uma propiedade das configurações do DataBaseManager.
404: * @param propriedade Nome da propriedade a ser lida
405: * @return valor da propriedade.
406: */
407: public synchronized String getPropriedade(String propriedade) {
408: return propriedades.getProperty(propriedade);
409: }
410:
411: /**
412: * Seta a nova versão do banco de dados
413: * @param versao A versão nova do banco de dados
414: */
415: public synchronized void setVersaoDB(int versao) {
416: propriedades.setProperty(VERSAO_DB, "" + versao);
417: FileOutputStream fos = null;
418: try {
419: fos = new FileOutputStream(arquivoPropriedades);
420: propriedades.store(fos, null);
421: } catch (IOException e) {
422: //Não há o que se fazer
423: } finally {
424: try {
425: if (fos != null) {
426: fos.flush();
427: fos.close();
428: }
429: } catch (IOException e1) {
430: //Não há o que se fazer
431: }
432: }
433: }
434:
435: /**
436: * Fecha o banco de dados ativo
437: * @throws SQLException
438: */
439: public void fecharBancoDeDados() throws SQLException {
440: fireFechandoDB(null);
441: pool.closeAllConnections();
442: dbManager = null;
443: }
444:
445: /**
446: * Retorna uma conexão livre do pool de conexões
447: * @return Connection
448: * @throws SQLException
449: */
450: public Connection getFreeConnection() throws SQLException {
451: return pool.getConnection();
452: }
453:
454: /**
455: * Libera uma conexão para o pool
456: * @param connection Conexao a ser liberada
457: */
458: public void liberaConexao(Connection connection) {
459: pool.free(connection);
460: }
461:
462: /**
463: * Adiciona um listener que será avisado dos eventos do DataBaseManager
464: * @param listener ControllerListener
465: */
466: public void addDataBaseListener(DataBaseListener listener) {
467: listeners.add(DataBaseListener.class, listener);
468: }
469:
470: /**
471: * Remove um listener que seria avisado dos eventos do DataBaseManager
472: * @param listener ControllerListener
473: */
474: public void removeDataBaseListener(DataBaseListener listener) {
475: listeners.remove(DataBaseListener.class, listener);
476: }
477:
478: private void fireFechandoDB(String sql) {
479: EventListener[] listenerList = listeners
480: .getListeners(DataBaseListener.class);
481:
482: DataBaseEvent e = new DataBaseEvent(sql);
483:
484: for (int i = 0; i < listenerList.length; i++) {
485: ((DataBaseListener) listenerList[i]).fechandoDB(e);
486: }
487: }
488:
489: private void fireExecutandoConsulta(String sql) {
490: EventListener[] listenerList = listeners
491: .getListeners(DataBaseListener.class);
492:
493: DataBaseEvent e = new DataBaseEvent(sql);
494:
495: for (int i = 0; i < listenerList.length; i++) {
496: ((DataBaseListener) listenerList[i]).executandoConsulta(e);
497: }
498: }
499:
500: private void fireExecutadoAtualizacao(String sql) {
501: EventListener[] listenerList = listeners
502: .getListeners(DataBaseListener.class);
503:
504: DataBaseEvent e = new DataBaseEvent(sql);
505:
506: for (int i = 0; i < listenerList.length; i++) {
507: ((DataBaseListener) listenerList[i])
508: .executadoAtualizacao(e);
509: }
510: }
511: }
|