From: Marco Zanon Date: Sat, 2 Mar 2024 17:30:38 +0000 (+0000) Subject: Renamed 'database' package to 'databases'. X-Git-Tag: SVN-to-Git~7 X-Git-Url: https://gitweb.marcozanon.com/?a=commitdiff_plain;h=d0858dada9c5da1ff7737ce14c9be5d17459ef74;p=Macaco Renamed 'database' package to 'databases'. --- diff --git a/10.x/CHANGELOG b/10.x/CHANGELOG index 1169b68..afc8059 100644 --- a/10.x/CHANGELOG +++ b/10.x/CHANGELOG @@ -5,6 +5,7 @@ See LICENSE for details. =================== 10.0.0 (2024-03-02) =================== +* Renamed 'database' package to 'databases'. * Renamed 'conversion' package to 'conversions'. * Renamed some methods and variables. diff --git a/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseConnection.java b/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseConnection.java deleted file mode 100644 index 8e8dd54..0000000 --- a/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseConnection.java +++ /dev/null @@ -1,346 +0,0 @@ -/** - * Macaco - * Copyright (c) 2009-2024 Marco Zanon . - * See LICENSE for details. - */ - -package com.marcozanon.macaco.database; - -import com.marcozanon.macaco.MObject; -import com.marcozanon.macaco.logging.MLogListener; -import com.marcozanon.macaco.text.MText; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.LinkedHashMap; -import java.util.LinkedList; - -public class MDatabaseConnection extends MObject { - - public static enum TransactionStatus { - CLOSED, - SUCCESSFUL, - FAILED - }; - - protected String driver = null; - protected String url = null; - protected String username = null; - protected String password = null; - protected boolean localTypesMode = false; - protected MLogListener logListener = null; - - protected Connection connection = null; - - protected MDatabaseConnection.TransactionStatus transactionStatus = MDatabaseConnection.TransactionStatus.CLOSED; - - /* */ - - public MDatabaseConnection(String driver, String url, String username, String password, boolean localTypesMode, MLogListener logListener) throws MDatabaseConnectionFailureException { - super(); - // - if (MText.isBlank(driver)) { - throw new IllegalArgumentException("Invalid 'driver': null or empty."); - } - if (MText.isBlank(url)) { - throw new IllegalArgumentException("Invalid 'url': null or empty."); - } - if (null == username) { - throw new IllegalArgumentException("Invalid 'username': null."); - } - if (null == password) { - throw new IllegalArgumentException("Invalid 'password': null."); - } - // - this.driver = driver; - this.url = url; - this.username = username; - this.password = password; - this.localTypesMode = localTypesMode; - this.setLogListener(logListener); - // Load driver. - try { - Class.forName(this.getDriver()).newInstance(); - } - catch (ClassNotFoundException exception) { - throw new MDatabaseConnectionFailureException("Could not load driver.", exception); - } - catch (IllegalAccessException exception) { - throw new MDatabaseConnectionFailureException("Could not load driver.", exception); - } - catch (InstantiationException exception) { - throw new MDatabaseConnectionFailureException("Could not load driver.", exception); - } - // Initialize the connection. - this.initialize(); - } - - public void initialize() throws MDatabaseConnectionFailureException { - // Establish a connection. - try { - this.connection = DriverManager.getConnection(this.getUrl(), this.getUsername(), this.getPassword()); - } - catch (SQLException exception) { - throw new MDatabaseConnectionFailureException("Could not get database connection.", exception); - } - // Set SQL mode to standard ANSI and TRADITIONAL. - try { - this.getConnection().createStatement().executeUpdate("SET SQL_MODE = 'ANSI,TRADITIONAL'"); -/* Disabled to prevent an infinite loop. - this.logStatement("### SET SQL_MODE = 'ANSI,TRADITIONAL' ###"); -*/ - } - catch (SQLException exception) { - throw new MDatabaseConnectionFailureException("Could not set SQL mode to ANSI and TRADITIONAL.", exception); - } - } - - protected void check() throws MDatabaseConnectionFailureException { - try { - this.getConnection().createStatement().executeQuery("/* ping */ SELECT 1"); - } - catch (SQLException exception) { - this.initialize(); - } - } - - public void close() throws MDatabaseConnectionFailureException { - try { - this.getConnection().close(); - } - catch (SQLException exception) { - throw new MDatabaseConnectionFailureException("Could not close database connection.", exception); - } - } - - public boolean isClosed() throws MDatabaseConnectionFailureException { - try { - return this.getConnection().isClosed(); - } - catch (SQLException exception) { - throw new MDatabaseConnectionFailureException("Could not determine the state.", exception); - } - } - - @Override - protected void finalize() { - try { - this.close(); - } - catch (Exception exception) { - } - } - - /* Driver. */ - - public String getDriver() { - return this.driver; - } - - /* Url. */ - - public String getUrl() { - return this.url; - } - - /* Username. */ - - public String getUsername() { - return this.username; - } - - /* Password. */ - - public String getPassword() { - return this.password; - } - - /* Local types mode. */ - - public boolean getLocalTypesMode() { - return this.localTypesMode; - } - - /* Connection. */ - - protected Connection getConnection() { - return this.connection; - } - - /* Transactions. */ - - protected void setTransactionStatus(MDatabaseConnection.TransactionStatus transactionStatus) { - if (null == transactionStatus) { - throw new IllegalArgumentException("Invalid 'transactionStatus': null."); - } - // - this.transactionStatus = transactionStatus; - } - - public MDatabaseConnection.TransactionStatus getTransactionStatus() { - return this.transactionStatus; - } - - public void startTransaction() throws MSqlTransactionException { - if (MDatabaseConnection.TransactionStatus.CLOSED != this.getTransactionStatus()) { - throw new MSqlTransactionException("Nested transactions not allowed."); - } - // - try { - // Check the connection. - this.check(); - // Start the transaction. - this.getConnection().setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); - this.getConnection().setAutoCommit(false); - this.setTransactionStatus(MDatabaseConnection.TransactionStatus.SUCCESSFUL); - this.logStatement("### BEGIN TRANSACTION ###"); - } - catch (MDatabaseConnectionFailureException exception) { - throw new MSqlTransactionException("Could not start transaction.", exception); - } - catch (SQLException exception) { - throw new MSqlTransactionException("Could not start transaction.", exception); - } - } - - public void rollBackTransaction() throws MSqlTransactionException { - if (MDatabaseConnection.TransactionStatus.CLOSED == this.getTransactionStatus()) { - throw new MSqlTransactionException("Transaction not started."); - } - // - try { -/* - // Check the connection. - this.check(); -*/ - // Cancel the transaction. - this.getConnection().rollback(); - this.getConnection().setAutoCommit(true); - this.setTransactionStatus(MDatabaseConnection.TransactionStatus.CLOSED); - this.logStatement("### ROLLBACK ###"); - } - catch (SQLException exception) { - this.setTransactionStatus(MDatabaseConnection.TransactionStatus.FAILED); - throw new MSqlTransactionException("Could not roll back transaction.", exception); - } - } - - public MDatabaseConnection.TransactionStatus commitTransaction() throws MSqlTransactionException { - switch (this.getTransactionStatus()) { - case SUCCESSFUL: - try { -/* - // Check the connection. - this.check(); -*/ - // Commit the transaction. - this.getConnection().commit(); - this.getConnection().setAutoCommit(true); - this.setTransactionStatus(MDatabaseConnection.TransactionStatus.CLOSED); - this.logStatement("### COMMIT ###"); - // - return MDatabaseConnection.TransactionStatus.SUCCESSFUL; - } - catch (SQLException exception) { - this.rollBackTransaction(); - // - return MDatabaseConnection.TransactionStatus.FAILED; - } - case FAILED: - this.rollBackTransaction(); - // - return MDatabaseConnection.TransactionStatus.FAILED; - default: // instead of "case CLOSED:" (necessary to avoid Java compilation errors) - throw new MSqlTransactionException("Transaction not started."); - } - } - - /* Statements. */ - - public MSqlStatementResults executePreparedStatement(String statement) throws MDatabaseConnectionFailureException, MSqlStatementException { - return this.executePreparedStatement(statement, new LinkedList()); - } - - public MSqlStatementResults executePreparedStatement(String statement, LinkedList parameters) throws MDatabaseConnectionFailureException, MSqlStatementException { - return this.executePreparedStatement(statement, parameters, this.getLocalTypesMode()); - } - - public MSqlStatementResults executePreparedStatement(String statement, LinkedList parameters, boolean localTypesMode) throws MDatabaseConnectionFailureException, MSqlStatementException { - return this.executePreparedStatement(statement, parameters, this.getLocalTypesMode(), true); - }; - - public MSqlStatementResults executePreparedStatement(String statement, LinkedList parameters, boolean localTypesMode, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException { - if (MText.isBlank(statement)) { - throw new IllegalArgumentException("Invalid 'statement': null or empty."); - } - if (null == parameters) { - throw new IllegalArgumentException("Invalid 'parameters': null."); - } - // - MSqlStatementResults results = null; - PreparedStatement preparedStatement = null; - try { - // Check the connection. - this.check(); - // Prepare the statement. - preparedStatement = this.getConnection().prepareStatement(statement, PreparedStatement.RETURN_GENERATED_KEYS); - for (int p = 0; parameters.size() > p; p++) { - preparedStatement.setObject(p + 1, parameters.get(p)); - } - // Execute the statement and parse the results. - preparedStatement.execute(); - results = new MSqlStatementResults(preparedStatement, localTypesMode); - if (loggableStatement) { - this.logStatement(preparedStatement.toString()); - } - } - catch (MDatabaseConnectionFailureException exception) { - if (MDatabaseConnection.TransactionStatus.SUCCESSFUL == this.getTransactionStatus()) { - this.setTransactionStatus(MDatabaseConnection.TransactionStatus.FAILED); - } - throw new MSqlStatementException(String.format("Could not execute prepared statement: %s.", preparedStatement), exception); - } - catch (SQLException exception) { - if (MDatabaseConnection.TransactionStatus.SUCCESSFUL == this.getTransactionStatus()) { - this.setTransactionStatus(MDatabaseConnection.TransactionStatus.FAILED); - } - throw new MSqlStatementException(String.format("Could not execute prepared statement: %s.", preparedStatement), exception); - } - // - return results; - } - - /* Tables. */ - - public MSqlTable getTable(String table, String primaryKey) { - return new MSqlTable(this, table, primaryKey); - } - - /* Logging. */ - - public void setLogListener(MLogListener logListener) { - this.logListener = logListener; - } - - protected MLogListener getLogListener() { - return this.logListener; - } - - protected void logStatement(String statement) { - MLogListener logListener = this.getLogListener(); - if (null != logListener) { - logListener.onMessageLogging(statement); - } - } - - /* Engine version. */ - - public String getEngineVersion() throws MDatabaseConnectionFailureException, MSqlStatementException { - MSqlStatementResults results = this.executePreparedStatement("SELECT VERSION()"); - LinkedList> resultList = results.getRecords(); - // - return (String)resultList.get(0).get("VERSION()"); - } - -} diff --git a/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseConnectionFailureException.java b/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseConnectionFailureException.java deleted file mode 100644 index c8fdda3..0000000 --- a/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseConnectionFailureException.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Macaco - * Copyright (c) 2009-2024 Marco Zanon . - * See LICENSE for details. - */ - -package com.marcozanon.macaco.database; - -@SuppressWarnings("serial") -public class MDatabaseConnectionFailureException extends MDatabaseException { - - /* */ - - public MDatabaseConnectionFailureException() { - super(); - } - - public MDatabaseConnectionFailureException(String message) { - super(message); - } - - public MDatabaseConnectionFailureException(Throwable error) { - super(error); - } - - public MDatabaseConnectionFailureException(String message, Throwable error) { - super(message, error); - } - -} diff --git a/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseConnectionGenerator.java b/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseConnectionGenerator.java deleted file mode 100644 index 7ea9348..0000000 --- a/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseConnectionGenerator.java +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Macaco - * Copyright (c) 2009-2024 Marco Zanon . - * See LICENSE for details. - */ - -package com.marcozanon.macaco.database; - -import com.marcozanon.macaco.MObject; -import com.marcozanon.macaco.logging.MLogListener; -import com.marcozanon.macaco.text.MText; - -public class MDatabaseConnectionGenerator extends MObject { - - protected String driver = null; - protected String url = null; - protected String username = null; - protected String password = null; - protected boolean localTypesMode = false; - protected MLogListener logListener = null; - - /* */ - - public MDatabaseConnectionGenerator(String driver, String url, String username, String password, boolean localTypesMode, MLogListener logListener) { - super(); - // - if (MText.isBlank(driver)) { - throw new IllegalArgumentException("Invalid 'driver': null or empty."); - } - if (MText.isBlank(url)) { - throw new IllegalArgumentException("Invalid 'url': null or empty."); - } - if (null == username) { - throw new IllegalArgumentException("Invalid 'username': null."); - } - if (null == password) { - throw new IllegalArgumentException("Invalid 'password': null."); - } - // - this.driver = driver; - this.url = url; - this.username = username; - this.password = password; - this.localTypesMode = localTypesMode; - this.setLogListener(logListener); - } - - /* Driver. */ - - public String getDriver() { - return this.driver; - } - - /* Url. */ - - public String getUrl() { - return this.url; - } - - /* Username. */ - - public String getUsername() { - return this.username; - } - - /* Password. */ - - public String getPassword() { - return this.password; - } - - /* Local types mode. */ - - public boolean getLocalTypesMode() { - return this.localTypesMode; - } - - /* Log listener. */ - - public MLogListener getLogListener() { - return this.logListener; - } - - public void setLogListener(MLogListener logListener) { - this.logListener = logListener; - } - - /* Generator. */ - - public MDatabaseConnection getNewDatabaseConnection() throws MDatabaseConnectionFailureException { - return new MDatabaseConnection(this.getDriver(), this.getUrl(), this.getUsername(), this.getPassword(), this.getLocalTypesMode(), this.getLogListener()); - } - - public boolean isGeneratorFor(MDatabaseConnection databaseConnection) { - if (null == databaseConnection) { - throw new IllegalArgumentException("Invalid 'databaseConnection': null."); - } - // - if (!databaseConnection.getDriver().equals(this.getDriver())) { - return false; - } - if (!databaseConnection.getUrl().equals(this.getUrl())) { - return false; - } - if (!databaseConnection.getUsername().equals(this.getUsername())) { - return false; - } - if (!databaseConnection.getPassword().equals(this.getPassword())) { - return false; - } - if (databaseConnection.getLocalTypesMode() != this.getLocalTypesMode()) { - return false; - } - if (databaseConnection.getLogListener() != this.getLogListener()) { - return false; - } - // - return true; - } - -} diff --git a/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseConnectionPool.java b/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseConnectionPool.java deleted file mode 100644 index b40e934..0000000 --- a/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseConnectionPool.java +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Macaco - * Copyright (c) 2009-2024 Marco Zanon . - * See LICENSE for details. - */ - -package com.marcozanon.macaco.database; - -import com.marcozanon.macaco.MObject; -import com.marcozanon.macaco.logging.MLogListener; -import java.util.LinkedList; - -public class MDatabaseConnectionPool extends MObject { - - protected MDatabaseConnectionGenerator databaseConnectionGenerator = null; - - protected LinkedList databaseConnections = new LinkedList(); - protected int minimumSize = 0; - protected int maximumSize = 0; - - /* */ - - public MDatabaseConnectionPool(String driver, String url, String username, String password, boolean localTypesMode, int minimumSize, int maximumSize, MLogListener logListener) throws MDatabaseConnectionFailureException { - super(); - // - if (0 > minimumSize) { - throw new IllegalArgumentException(String.format("Invalid 'minimumSize': %s.", minimumSize)); - } - if (1 > maximumSize) { - throw new IllegalArgumentException(String.format("Invalid 'maximumSize': %s.", maximumSize)); - } - else if (minimumSize > maximumSize) { - throw new IllegalArgumentException(String.format("Invalid 'maximumSize': %s < %s ('minimumSize').", maximumSize, minimumSize)); - } - // - this.databaseConnectionGenerator = new MDatabaseConnectionGenerator(driver, url, username, password, localTypesMode, logListener); - this.minimumSize = minimumSize; - this.maximumSize = maximumSize; - // - this.initialize(); - } - - protected void initialize() throws MDatabaseConnectionFailureException { - for (int c = 0; c < this.getMinimumSize(); c++) { - MDatabaseConnection databaseConnection = this.getDatabaseConnectionGenerator().getNewDatabaseConnection(); - this.pushDatabaseConnection(databaseConnection); - } - } - - protected void closeConnections() { - LinkedList databaseConnections = this.getDatabaseConnections(); - while (0 < databaseConnections.size()) { - MDatabaseConnection databaseConnection = databaseConnections.removeLast(); - // - try { - databaseConnection.close(); - } - catch (MDatabaseConnectionFailureException exception) { - } - } - } - - @Override - protected void finalize() { - this.closeConnections(); - } - - /* Database connection generator. */ - - protected MDatabaseConnectionGenerator getDatabaseConnectionGenerator() { - return this.databaseConnectionGenerator; - } - - /* Database connections. */ - - protected LinkedList getDatabaseConnections() { - return this.databaseConnections; - } - - protected int getMinimumSize() { - return this.minimumSize; - } - - protected int getMaximumSize() { - return this.maximumSize; - } - - public synchronized MDatabaseConnection popDatabaseConnection() throws MDatabaseConnectionFailureException { - LinkedList databaseConnections = this.getDatabaseConnections(); - // - MDatabaseConnection databaseConnection = null; - if (0 == databaseConnections.size()) { - databaseConnection = this.getDatabaseConnectionGenerator().getNewDatabaseConnection(); - } - else { - databaseConnection = databaseConnections.removeLast(); - } - // - if (this.getMinimumSize() > databaseConnections.size()) { - databaseConnections.add(this.getDatabaseConnectionGenerator().getNewDatabaseConnection()); - } - // - return databaseConnection; - } - - public synchronized void pushDatabaseConnection(MDatabaseConnection databaseConnection) throws MDatabaseConnectionFailureException { - if (null == databaseConnection) { - throw new IllegalArgumentException("Invalid 'databaseConnection': null."); - } - else if (databaseConnection.isClosed()) { - throw new IllegalArgumentException("Invalid 'databaseConnection': closed."); - } - // - if (!this.getDatabaseConnectionGenerator().isGeneratorFor(databaseConnection)) { - databaseConnection.close(); - } - else { - LinkedList databaseConnections = this.getDatabaseConnections(); - // - if (this.getMaximumSize() >= databaseConnections.size()) { - databaseConnections.add(databaseConnection); - } - } - } - - /* Logging. */ - - public synchronized void setLogListener(MLogListener logListener) { - this.getDatabaseConnectionGenerator().setLogListener(logListener); - // - for (MDatabaseConnection databaseConnection: this.getDatabaseConnections()) { - databaseConnection.setLogListener(logListener); - } - } - -} diff --git a/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseException.java b/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseException.java deleted file mode 100644 index 498a752..0000000 --- a/10.x/src/main/java/com/marcozanon/macaco/database/MDatabaseException.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Macaco - * Copyright (c) 2009-2024 Marco Zanon . - * See LICENSE for details. - */ - -package com.marcozanon.macaco.database; - -import com.marcozanon.macaco.MException; - -public abstract class MDatabaseException extends MException { - - /* */ - - public MDatabaseException() { - super(); - } - - public MDatabaseException(String message) { - super(message); - } - - public MDatabaseException(Throwable error) { - super(error); - } - - public MDatabaseException(String message, Throwable error) { - super(message, error); - } - -} diff --git a/10.x/src/main/java/com/marcozanon/macaco/database/MSqlStatementException.java b/10.x/src/main/java/com/marcozanon/macaco/database/MSqlStatementException.java deleted file mode 100644 index cd67820..0000000 --- a/10.x/src/main/java/com/marcozanon/macaco/database/MSqlStatementException.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Macaco - * Copyright (c) 2009-2024 Marco Zanon . - * See LICENSE for details. - */ - -package com.marcozanon.macaco.database; - -@SuppressWarnings("serial") -public class MSqlStatementException extends MDatabaseException { - - /* */ - - public MSqlStatementException() { - super(); - } - - public MSqlStatementException(String message) { - super(message); - } - - public MSqlStatementException(Throwable error) { - super(error); - } - - public MSqlStatementException(String message, Throwable error) { - super(message, error); - } - -} diff --git a/10.x/src/main/java/com/marcozanon/macaco/database/MSqlStatementResults.java b/10.x/src/main/java/com/marcozanon/macaco/database/MSqlStatementResults.java deleted file mode 100644 index d4c9632..0000000 --- a/10.x/src/main/java/com/marcozanon/macaco/database/MSqlStatementResults.java +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Macaco - * Copyright (c) 2009-2024 Marco Zanon . - * See LICENSE for details. - */ - -package com.marcozanon.macaco.database; - -import com.marcozanon.macaco.MObject; -import com.marcozanon.macaco.text.MText; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; - -public class MSqlStatementResults extends MObject { - - protected PreparedStatement preparedStatement = null; - protected boolean localTypesMode = false; - - protected ResultSet resultSet = null; - - protected LinkedList> records = new LinkedList>(); - - protected LinkedHashSet generatedKeys = new LinkedHashSet(); - - /* */ - - public MSqlStatementResults(PreparedStatement preparedStatement, boolean localTypesMode) throws MSqlStatementException { - super(); - // - if (null == preparedStatement) { - throw new IllegalArgumentException("Invalid 'preparedStatement': null."); - } - // - this.preparedStatement = preparedStatement; - this.localTypesMode = localTypesMode; - // - try { - this.resultSet = this.getPreparedStatement().getResultSet(); - if (null != this.getResultSet()) { - while (this.getResultSet().next()) { - ResultSetMetaData resultSetMetaData = this.getResultSet().getMetaData(); - LinkedHashMap record = new LinkedHashMap(); - // - for (int f = 0; resultSetMetaData.getColumnCount() > f; f++) { - String field = resultSetMetaData.getColumnLabel(f + 1); - Object value = this.getResultSet().getObject(field); - // - if ((value instanceof java.sql.Date) && (this.getLocalTypesMode())) { - value = ((java.sql.Date)value).toLocalDate(); - } - else if ((value instanceof java.sql.Time) && (this.getLocalTypesMode())) { - value = ((java.sql.Time)value).toLocalTime(); - } - else if ((value instanceof java.sql.Timestamp) && (this.getLocalTypesMode())) { - value = ((java.sql.Timestamp)value).toLocalDateTime(); - } - // - record.put(field, value); - } - // - this.getRecords().add(record); - } - } - } - catch (SQLException exception) { - throw new MSqlStatementException("Could not retrieve statement records.", exception); - } - // - try { - ResultSet generatedKeysResultSet = this.getPreparedStatement().getGeneratedKeys(); - while (generatedKeysResultSet.next()) { - this.getGeneratedKeys().add(generatedKeysResultSet.getObject(1)); - } - generatedKeysResultSet.close(); - } - catch (SQLException exception) { - throw new MSqlStatementException("Could not retrieve generated keys.", exception); - } - } - - public void close() throws MDatabaseConnectionFailureException { - try { - if (null != this.getResultSet()) { - this.getResultSet().close(); - } - this.getPreparedStatement().close(); - } - catch (SQLException exception) { - throw new MDatabaseConnectionFailureException("Could not close statement and/or result set references.", exception); - } - } - - @Override - protected void finalize() { - try { - this.close(); - } - catch (Exception exception) { - } - } - - /* References. */ - - protected PreparedStatement getPreparedStatement() { - return this.preparedStatement; - } - - public ResultSet getResultSet() { - return this.resultSet; - } - - public boolean getLocalTypesMode() { - return this.localTypesMode; - } - - /* Records. */ - - public LinkedList> getRecords() { - return this.records; - } - - public LinkedList getRecordsByField(String field) { - if (MText.isBlank(field)) { - throw new IllegalArgumentException("Invalid 'field': null or empty."); - } - // - LinkedList recordsByField = new LinkedList(); - for (LinkedHashMap record: this.getRecords()) { - Object r = record.get(field); - if (null == r) { - throw new IllegalArgumentException(String.format("Invalid 'field': %s.", field)); - } - recordsByField.add(r); - } - // - return recordsByField; - } - - /* Generated keys. */ - - public LinkedHashSet getGeneratedKeys() { - return this.generatedKeys; - } - -} diff --git a/10.x/src/main/java/com/marcozanon/macaco/database/MSqlTable.java b/10.x/src/main/java/com/marcozanon/macaco/database/MSqlTable.java deleted file mode 100644 index d58d8e4..0000000 --- a/10.x/src/main/java/com/marcozanon/macaco/database/MSqlTable.java +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Macaco - * Copyright (c) 2009-2024 Marco Zanon . - * See LICENSE for details. - */ - -package com.marcozanon.macaco.database; - -import com.marcozanon.macaco.MObject; -import com.marcozanon.macaco.text.MText; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.StringJoiner; - -public class MSqlTable extends MObject { - - protected MDatabaseConnection databaseConnection = null; - protected String table = null; - protected String primaryKey = null; - - /* */ - - public MSqlTable(MDatabaseConnection databaseConnection, String table, String primaryKey) { - super(); - // - if (null == databaseConnection) { - throw new IllegalArgumentException("Invalid 'databaseConnection': null."); - } - if (MText.isBlank(table)) { - throw new IllegalArgumentException("Invalid 'table': null or empty."); - } - if (MText.isBlank(primaryKey)) { - throw new IllegalArgumentException("Invalid 'primaryKey': null or empty."); - } - // - this.databaseConnection = databaseConnection; - this.table = table; - this.primaryKey = primaryKey; - } - - /* Database connection. */ - - protected MDatabaseConnection getDatabaseConnection() { - return this.databaseConnection; - } - - /* Table. */ - - public String getTable() { - return this.table; - } - - /* Primary key. */ - - public String getPrimaryKey() { - return this.primaryKey; - } - - /* Statements. */ - - protected MSqlStatementResults insertRecord(LinkedHashMap map, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException { - if (null == map) { - throw new IllegalArgumentException("Invalid 'map': null."); - } - // - LinkedList p = new LinkedList(); - StringBuilder fields = new StringBuilder(""); - StringBuilder s = new StringBuilder(""); - // - for (String field: map.keySet()) { - fields.append("\"" + this.getTable() + "\".\"" + field + "\", "); - s.append("?, "); - p.add(map.get(field)); - } - fields.delete(fields.length() - 2, fields.length()); - s.delete(s.length() - 2, s.length()); - // - return this.getDatabaseConnection().executePreparedStatement(" INSERT INTO \"" + this.getTable() + "\"" - + " (" + fields + ")" - + " VALUES (" + s + ")", - p, true, loggableStatement); - } - - protected MSqlStatementResults updateRecord(LinkedHashMap map, Object id, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException { - if (null == map) { - throw new IllegalArgumentException("Invalid 'map': null."); - } - if (null == id) { - throw new IllegalArgumentException("Invalid 'id': null."); - } - // - LinkedList p = new LinkedList(); - StringBuilder s = new StringBuilder(""); - // - for (String field: map.keySet()) { - s.append("\"" + this.getTable() + "\".\"" + field + "\" = ?, "); - p.add(map.get(field)); - } - s.delete(s.length() - 2, s.length()); - p.add(id); - // - return this.getDatabaseConnection().executePreparedStatement(" UPDATE \"" + this.getTable() + "\"" - + " SET " + s - + " WHERE (\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" = ?)", - p, true, loggableStatement); - } - - public MSqlStatementResults setRecord(LinkedHashMap map, Object id) throws MDatabaseConnectionFailureException, MSqlStatementException { - return this.setRecord(map, id, true); - } - - public MSqlStatementResults setRecord(LinkedHashMap map, Object id, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException { - if (null == id) { - return this.insertRecord(map, loggableStatement); - } - else { - return this.updateRecord(map, id, loggableStatement); - } - } - - public MSqlStatementResults getRecord(Object id) throws MDatabaseConnectionFailureException, MSqlStatementException { - if (null == id) { - throw new IllegalArgumentException("Invalid 'id': null."); - } - // - LinkedList p = new LinkedList(); - p.add(id); - // - return this.getDatabaseConnection().executePreparedStatement(" SELECT *" - + " FROM \"" + this.getTable() + "\"" - + " WHERE (\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" = ?)", - p); - } - - public MSqlStatementResults deleteRecord(Object id) throws MDatabaseConnectionFailureException, MSqlStatementException { - if (null == id) { - throw new IllegalArgumentException("Invalid 'id': null."); - } - // - LinkedList p = new LinkedList(); - p.add(id); - // - return this.getDatabaseConnection().executePreparedStatement(" DELETE FROM \"" + this.getTable() + "\"" - + " WHERE (\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" = ?)", - p); - } - - public MSqlStatementResults deleteRecords(LinkedHashSet idSet) throws MDatabaseConnectionFailureException, MSqlStatementException { - if (null == idSet) { - throw new IllegalArgumentException("Invalid 'idSet': null."); - } - // - String whereClause = null; - LinkedList p = null; - // - if (0 == idSet.size()) { - whereClause = "0"; - } - else { - StringJoiner s = new StringJoiner(", "); - p = new LinkedList(); - // - for (Object id: idSet) { - s.add("?"); - p.add(id); - } - // - whereClause = "\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" IN (" + s.toString() + ")"; - } - // - return this.getDatabaseConnection().executePreparedStatement(" DELETE FROM \"" + this.getTable() + "\"" - + " WHERE (" + whereClause + ")", - p); - } - -} diff --git a/10.x/src/main/java/com/marcozanon/macaco/database/MSqlTransactionException.java b/10.x/src/main/java/com/marcozanon/macaco/database/MSqlTransactionException.java deleted file mode 100644 index d63591b..0000000 --- a/10.x/src/main/java/com/marcozanon/macaco/database/MSqlTransactionException.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Macaco - * Copyright (c) 2009-2024 Marco Zanon . - * See LICENSE for details. - */ - -package com.marcozanon.macaco.database; - -@SuppressWarnings("serial") -public class MSqlTransactionException extends MDatabaseException { - - /* */ - - public MSqlTransactionException() { - super(); - } - - public MSqlTransactionException(String message) { - super(message); - } - - public MSqlTransactionException(Throwable error) { - super(error); - } - - public MSqlTransactionException(String message, Throwable error) { - super(message, error); - } - -} diff --git a/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseConnection.java b/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseConnection.java new file mode 100644 index 0000000..a3e8f41 --- /dev/null +++ b/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseConnection.java @@ -0,0 +1,346 @@ +/** + * Macaco + * Copyright (c) 2009-2024 Marco Zanon . + * See LICENSE for details. + */ + +package com.marcozanon.macaco.databases; + +import com.marcozanon.macaco.MObject; +import com.marcozanon.macaco.logging.MLogListener; +import com.marcozanon.macaco.text.MText; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.LinkedList; + +public class MDatabaseConnection extends MObject { + + public static enum TransactionStatus { + CLOSED, + SUCCESSFUL, + FAILED + }; + + protected String driver = null; + protected String url = null; + protected String username = null; + protected String password = null; + protected boolean localTypesMode = false; + protected MLogListener logListener = null; + + protected Connection connection = null; + + protected MDatabaseConnection.TransactionStatus transactionStatus = MDatabaseConnection.TransactionStatus.CLOSED; + + /* */ + + public MDatabaseConnection(String driver, String url, String username, String password, boolean localTypesMode, MLogListener logListener) throws MDatabaseConnectionFailureException { + super(); + // + if (MText.isBlank(driver)) { + throw new IllegalArgumentException("Invalid 'driver': null or empty."); + } + if (MText.isBlank(url)) { + throw new IllegalArgumentException("Invalid 'url': null or empty."); + } + if (null == username) { + throw new IllegalArgumentException("Invalid 'username': null."); + } + if (null == password) { + throw new IllegalArgumentException("Invalid 'password': null."); + } + // + this.driver = driver; + this.url = url; + this.username = username; + this.password = password; + this.localTypesMode = localTypesMode; + this.setLogListener(logListener); + // Load driver. + try { + Class.forName(this.getDriver()).newInstance(); + } + catch (ClassNotFoundException exception) { + throw new MDatabaseConnectionFailureException("Could not load driver.", exception); + } + catch (IllegalAccessException exception) { + throw new MDatabaseConnectionFailureException("Could not load driver.", exception); + } + catch (InstantiationException exception) { + throw new MDatabaseConnectionFailureException("Could not load driver.", exception); + } + // Initialize the connection. + this.initialize(); + } + + public void initialize() throws MDatabaseConnectionFailureException { + // Establish a connection. + try { + this.connection = DriverManager.getConnection(this.getUrl(), this.getUsername(), this.getPassword()); + } + catch (SQLException exception) { + throw new MDatabaseConnectionFailureException("Could not get database connection.", exception); + } + // Set SQL mode to standard ANSI and TRADITIONAL. + try { + this.getConnection().createStatement().executeUpdate("SET SQL_MODE = 'ANSI,TRADITIONAL'"); +/* Disabled to prevent an infinite loop. + this.logStatement("### SET SQL_MODE = 'ANSI,TRADITIONAL' ###"); +*/ + } + catch (SQLException exception) { + throw new MDatabaseConnectionFailureException("Could not set SQL mode to ANSI and TRADITIONAL.", exception); + } + } + + protected void check() throws MDatabaseConnectionFailureException { + try { + this.getConnection().createStatement().executeQuery("/* ping */ SELECT 1"); + } + catch (SQLException exception) { + this.initialize(); + } + } + + public void close() throws MDatabaseConnectionFailureException { + try { + this.getConnection().close(); + } + catch (SQLException exception) { + throw new MDatabaseConnectionFailureException("Could not close database connection.", exception); + } + } + + public boolean isClosed() throws MDatabaseConnectionFailureException { + try { + return this.getConnection().isClosed(); + } + catch (SQLException exception) { + throw new MDatabaseConnectionFailureException("Could not determine the state.", exception); + } + } + + @Override + protected void finalize() { + try { + this.close(); + } + catch (Exception exception) { + } + } + + /* Driver. */ + + public String getDriver() { + return this.driver; + } + + /* Url. */ + + public String getUrl() { + return this.url; + } + + /* Username. */ + + public String getUsername() { + return this.username; + } + + /* Password. */ + + public String getPassword() { + return this.password; + } + + /* Local types mode. */ + + public boolean getLocalTypesMode() { + return this.localTypesMode; + } + + /* Connection. */ + + protected Connection getConnection() { + return this.connection; + } + + /* Transactions. */ + + protected void setTransactionStatus(MDatabaseConnection.TransactionStatus transactionStatus) { + if (null == transactionStatus) { + throw new IllegalArgumentException("Invalid 'transactionStatus': null."); + } + // + this.transactionStatus = transactionStatus; + } + + public MDatabaseConnection.TransactionStatus getTransactionStatus() { + return this.transactionStatus; + } + + public void startTransaction() throws MSqlTransactionException { + if (MDatabaseConnection.TransactionStatus.CLOSED != this.getTransactionStatus()) { + throw new MSqlTransactionException("Nested transactions not allowed."); + } + // + try { + // Check the connection. + this.check(); + // Start the transaction. + this.getConnection().setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); + this.getConnection().setAutoCommit(false); + this.setTransactionStatus(MDatabaseConnection.TransactionStatus.SUCCESSFUL); + this.logStatement("### BEGIN TRANSACTION ###"); + } + catch (MDatabaseConnectionFailureException exception) { + throw new MSqlTransactionException("Could not start transaction.", exception); + } + catch (SQLException exception) { + throw new MSqlTransactionException("Could not start transaction.", exception); + } + } + + public void rollBackTransaction() throws MSqlTransactionException { + if (MDatabaseConnection.TransactionStatus.CLOSED == this.getTransactionStatus()) { + throw new MSqlTransactionException("Transaction not started."); + } + // + try { +/* + // Check the connection. + this.check(); +*/ + // Cancel the transaction. + this.getConnection().rollback(); + this.getConnection().setAutoCommit(true); + this.setTransactionStatus(MDatabaseConnection.TransactionStatus.CLOSED); + this.logStatement("### ROLLBACK ###"); + } + catch (SQLException exception) { + this.setTransactionStatus(MDatabaseConnection.TransactionStatus.FAILED); + throw new MSqlTransactionException("Could not roll back transaction.", exception); + } + } + + public MDatabaseConnection.TransactionStatus commitTransaction() throws MSqlTransactionException { + switch (this.getTransactionStatus()) { + case SUCCESSFUL: + try { +/* + // Check the connection. + this.check(); +*/ + // Commit the transaction. + this.getConnection().commit(); + this.getConnection().setAutoCommit(true); + this.setTransactionStatus(MDatabaseConnection.TransactionStatus.CLOSED); + this.logStatement("### COMMIT ###"); + // + return MDatabaseConnection.TransactionStatus.SUCCESSFUL; + } + catch (SQLException exception) { + this.rollBackTransaction(); + // + return MDatabaseConnection.TransactionStatus.FAILED; + } + case FAILED: + this.rollBackTransaction(); + // + return MDatabaseConnection.TransactionStatus.FAILED; + default: // instead of "case CLOSED:" (necessary to avoid Java compilation errors) + throw new MSqlTransactionException("Transaction not started."); + } + } + + /* Statements. */ + + public MSqlStatementResults executePreparedStatement(String statement) throws MDatabaseConnectionFailureException, MSqlStatementException { + return this.executePreparedStatement(statement, new LinkedList()); + } + + public MSqlStatementResults executePreparedStatement(String statement, LinkedList parameters) throws MDatabaseConnectionFailureException, MSqlStatementException { + return this.executePreparedStatement(statement, parameters, this.getLocalTypesMode()); + } + + public MSqlStatementResults executePreparedStatement(String statement, LinkedList parameters, boolean localTypesMode) throws MDatabaseConnectionFailureException, MSqlStatementException { + return this.executePreparedStatement(statement, parameters, this.getLocalTypesMode(), true); + }; + + public MSqlStatementResults executePreparedStatement(String statement, LinkedList parameters, boolean localTypesMode, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException { + if (MText.isBlank(statement)) { + throw new IllegalArgumentException("Invalid 'statement': null or empty."); + } + if (null == parameters) { + throw new IllegalArgumentException("Invalid 'parameters': null."); + } + // + MSqlStatementResults results = null; + PreparedStatement preparedStatement = null; + try { + // Check the connection. + this.check(); + // Prepare the statement. + preparedStatement = this.getConnection().prepareStatement(statement, PreparedStatement.RETURN_GENERATED_KEYS); + for (int p = 0; parameters.size() > p; p++) { + preparedStatement.setObject(p + 1, parameters.get(p)); + } + // Execute the statement and parse the results. + preparedStatement.execute(); + results = new MSqlStatementResults(preparedStatement, localTypesMode); + if (loggableStatement) { + this.logStatement(preparedStatement.toString()); + } + } + catch (MDatabaseConnectionFailureException exception) { + if (MDatabaseConnection.TransactionStatus.SUCCESSFUL == this.getTransactionStatus()) { + this.setTransactionStatus(MDatabaseConnection.TransactionStatus.FAILED); + } + throw new MSqlStatementException(String.format("Could not execute prepared statement: %s.", preparedStatement), exception); + } + catch (SQLException exception) { + if (MDatabaseConnection.TransactionStatus.SUCCESSFUL == this.getTransactionStatus()) { + this.setTransactionStatus(MDatabaseConnection.TransactionStatus.FAILED); + } + throw new MSqlStatementException(String.format("Could not execute prepared statement: %s.", preparedStatement), exception); + } + // + return results; + } + + /* Tables. */ + + public MSqlTable getTable(String table, String primaryKey) { + return new MSqlTable(this, table, primaryKey); + } + + /* Logging. */ + + public void setLogListener(MLogListener logListener) { + this.logListener = logListener; + } + + protected MLogListener getLogListener() { + return this.logListener; + } + + protected void logStatement(String statement) { + MLogListener logListener = this.getLogListener(); + if (null != logListener) { + logListener.onMessageLogging(statement); + } + } + + /* Engine version. */ + + public String getEngineVersion() throws MDatabaseConnectionFailureException, MSqlStatementException { + MSqlStatementResults results = this.executePreparedStatement("SELECT VERSION()"); + LinkedList> resultList = results.getRecords(); + // + return (String)resultList.get(0).get("VERSION()"); + } + +} diff --git a/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseConnectionFailureException.java b/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseConnectionFailureException.java new file mode 100644 index 0000000..a15b042 --- /dev/null +++ b/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseConnectionFailureException.java @@ -0,0 +1,30 @@ +/** + * Macaco + * Copyright (c) 2009-2024 Marco Zanon . + * See LICENSE for details. + */ + +package com.marcozanon.macaco.databases; + +@SuppressWarnings("serial") +public class MDatabaseConnectionFailureException extends MDatabaseException { + + /* */ + + public MDatabaseConnectionFailureException() { + super(); + } + + public MDatabaseConnectionFailureException(String message) { + super(message); + } + + public MDatabaseConnectionFailureException(Throwable error) { + super(error); + } + + public MDatabaseConnectionFailureException(String message, Throwable error) { + super(message, error); + } + +} diff --git a/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseConnectionGenerator.java b/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseConnectionGenerator.java new file mode 100644 index 0000000..91e8aeb --- /dev/null +++ b/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseConnectionGenerator.java @@ -0,0 +1,121 @@ +/** + * Macaco + * Copyright (c) 2009-2024 Marco Zanon . + * See LICENSE for details. + */ + +package com.marcozanon.macaco.databases; + +import com.marcozanon.macaco.MObject; +import com.marcozanon.macaco.logging.MLogListener; +import com.marcozanon.macaco.text.MText; + +public class MDatabaseConnectionGenerator extends MObject { + + protected String driver = null; + protected String url = null; + protected String username = null; + protected String password = null; + protected boolean localTypesMode = false; + protected MLogListener logListener = null; + + /* */ + + public MDatabaseConnectionGenerator(String driver, String url, String username, String password, boolean localTypesMode, MLogListener logListener) { + super(); + // + if (MText.isBlank(driver)) { + throw new IllegalArgumentException("Invalid 'driver': null or empty."); + } + if (MText.isBlank(url)) { + throw new IllegalArgumentException("Invalid 'url': null or empty."); + } + if (null == username) { + throw new IllegalArgumentException("Invalid 'username': null."); + } + if (null == password) { + throw new IllegalArgumentException("Invalid 'password': null."); + } + // + this.driver = driver; + this.url = url; + this.username = username; + this.password = password; + this.localTypesMode = localTypesMode; + this.setLogListener(logListener); + } + + /* Driver. */ + + public String getDriver() { + return this.driver; + } + + /* Url. */ + + public String getUrl() { + return this.url; + } + + /* Username. */ + + public String getUsername() { + return this.username; + } + + /* Password. */ + + public String getPassword() { + return this.password; + } + + /* Local types mode. */ + + public boolean getLocalTypesMode() { + return this.localTypesMode; + } + + /* Log listener. */ + + public MLogListener getLogListener() { + return this.logListener; + } + + public void setLogListener(MLogListener logListener) { + this.logListener = logListener; + } + + /* Generator. */ + + public MDatabaseConnection getNewDatabaseConnection() throws MDatabaseConnectionFailureException { + return new MDatabaseConnection(this.getDriver(), this.getUrl(), this.getUsername(), this.getPassword(), this.getLocalTypesMode(), this.getLogListener()); + } + + public boolean isGeneratorFor(MDatabaseConnection databaseConnection) { + if (null == databaseConnection) { + throw new IllegalArgumentException("Invalid 'databaseConnection': null."); + } + // + if (!databaseConnection.getDriver().equals(this.getDriver())) { + return false; + } + if (!databaseConnection.getUrl().equals(this.getUrl())) { + return false; + } + if (!databaseConnection.getUsername().equals(this.getUsername())) { + return false; + } + if (!databaseConnection.getPassword().equals(this.getPassword())) { + return false; + } + if (databaseConnection.getLocalTypesMode() != this.getLocalTypesMode()) { + return false; + } + if (databaseConnection.getLogListener() != this.getLogListener()) { + return false; + } + // + return true; + } + +} diff --git a/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseConnectionPool.java b/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseConnectionPool.java new file mode 100644 index 0000000..33d839d --- /dev/null +++ b/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseConnectionPool.java @@ -0,0 +1,136 @@ +/** + * Macaco + * Copyright (c) 2009-2024 Marco Zanon . + * See LICENSE for details. + */ + +package com.marcozanon.macaco.databases; + +import com.marcozanon.macaco.MObject; +import com.marcozanon.macaco.logging.MLogListener; +import java.util.LinkedList; + +public class MDatabaseConnectionPool extends MObject { + + protected MDatabaseConnectionGenerator databaseConnectionGenerator = null; + + protected LinkedList databaseConnections = new LinkedList(); + protected int minimumSize = 0; + protected int maximumSize = 0; + + /* */ + + public MDatabaseConnectionPool(String driver, String url, String username, String password, boolean localTypesMode, int minimumSize, int maximumSize, MLogListener logListener) throws MDatabaseConnectionFailureException { + super(); + // + if (0 > minimumSize) { + throw new IllegalArgumentException(String.format("Invalid 'minimumSize': %s.", minimumSize)); + } + if (1 > maximumSize) { + throw new IllegalArgumentException(String.format("Invalid 'maximumSize': %s.", maximumSize)); + } + else if (minimumSize > maximumSize) { + throw new IllegalArgumentException(String.format("Invalid 'maximumSize': %s < %s ('minimumSize').", maximumSize, minimumSize)); + } + // + this.databaseConnectionGenerator = new MDatabaseConnectionGenerator(driver, url, username, password, localTypesMode, logListener); + this.minimumSize = minimumSize; + this.maximumSize = maximumSize; + // + this.initialize(); + } + + protected void initialize() throws MDatabaseConnectionFailureException { + for (int c = 0; c < this.getMinimumSize(); c++) { + MDatabaseConnection databaseConnection = this.getDatabaseConnectionGenerator().getNewDatabaseConnection(); + this.pushDatabaseConnection(databaseConnection); + } + } + + protected void closeConnections() { + LinkedList databaseConnections = this.getDatabaseConnections(); + while (0 < databaseConnections.size()) { + MDatabaseConnection databaseConnection = databaseConnections.removeLast(); + // + try { + databaseConnection.close(); + } + catch (MDatabaseConnectionFailureException exception) { + } + } + } + + @Override + protected void finalize() { + this.closeConnections(); + } + + /* Database connection generator. */ + + protected MDatabaseConnectionGenerator getDatabaseConnectionGenerator() { + return this.databaseConnectionGenerator; + } + + /* Database connections. */ + + protected LinkedList getDatabaseConnections() { + return this.databaseConnections; + } + + protected int getMinimumSize() { + return this.minimumSize; + } + + protected int getMaximumSize() { + return this.maximumSize; + } + + public synchronized MDatabaseConnection popDatabaseConnection() throws MDatabaseConnectionFailureException { + LinkedList databaseConnections = this.getDatabaseConnections(); + // + MDatabaseConnection databaseConnection = null; + if (0 == databaseConnections.size()) { + databaseConnection = this.getDatabaseConnectionGenerator().getNewDatabaseConnection(); + } + else { + databaseConnection = databaseConnections.removeLast(); + } + // + if (this.getMinimumSize() > databaseConnections.size()) { + databaseConnections.add(this.getDatabaseConnectionGenerator().getNewDatabaseConnection()); + } + // + return databaseConnection; + } + + public synchronized void pushDatabaseConnection(MDatabaseConnection databaseConnection) throws MDatabaseConnectionFailureException { + if (null == databaseConnection) { + throw new IllegalArgumentException("Invalid 'databaseConnection': null."); + } + else if (databaseConnection.isClosed()) { + throw new IllegalArgumentException("Invalid 'databaseConnection': closed."); + } + // + if (!this.getDatabaseConnectionGenerator().isGeneratorFor(databaseConnection)) { + databaseConnection.close(); + } + else { + LinkedList databaseConnections = this.getDatabaseConnections(); + // + if (this.getMaximumSize() >= databaseConnections.size()) { + databaseConnections.add(databaseConnection); + } + } + } + + /* Logging. */ + + public synchronized void setLogListener(MLogListener logListener) { + this.getDatabaseConnectionGenerator().setLogListener(logListener); + // + for (MDatabaseConnection databaseConnection: this.getDatabaseConnections()) { + databaseConnection.setLogListener(logListener); + } + } + +} diff --git a/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseException.java b/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseException.java new file mode 100644 index 0000000..40b3107 --- /dev/null +++ b/10.x/src/main/java/com/marcozanon/macaco/databases/MDatabaseException.java @@ -0,0 +1,31 @@ +/** + * Macaco + * Copyright (c) 2009-2024 Marco Zanon . + * See LICENSE for details. + */ + +package com.marcozanon.macaco.databases; + +import com.marcozanon.macaco.MException; + +public abstract class MDatabaseException extends MException { + + /* */ + + public MDatabaseException() { + super(); + } + + public MDatabaseException(String message) { + super(message); + } + + public MDatabaseException(Throwable error) { + super(error); + } + + public MDatabaseException(String message, Throwable error) { + super(message, error); + } + +} diff --git a/10.x/src/main/java/com/marcozanon/macaco/databases/MSqlStatementException.java b/10.x/src/main/java/com/marcozanon/macaco/databases/MSqlStatementException.java new file mode 100644 index 0000000..a20ec13 --- /dev/null +++ b/10.x/src/main/java/com/marcozanon/macaco/databases/MSqlStatementException.java @@ -0,0 +1,30 @@ +/** + * Macaco + * Copyright (c) 2009-2024 Marco Zanon . + * See LICENSE for details. + */ + +package com.marcozanon.macaco.databases; + +@SuppressWarnings("serial") +public class MSqlStatementException extends MDatabaseException { + + /* */ + + public MSqlStatementException() { + super(); + } + + public MSqlStatementException(String message) { + super(message); + } + + public MSqlStatementException(Throwable error) { + super(error); + } + + public MSqlStatementException(String message, Throwable error) { + super(message, error); + } + +} diff --git a/10.x/src/main/java/com/marcozanon/macaco/databases/MSqlStatementResults.java b/10.x/src/main/java/com/marcozanon/macaco/databases/MSqlStatementResults.java new file mode 100644 index 0000000..1ad6ba3 --- /dev/null +++ b/10.x/src/main/java/com/marcozanon/macaco/databases/MSqlStatementResults.java @@ -0,0 +1,150 @@ +/** + * Macaco + * Copyright (c) 2009-2024 Marco Zanon . + * See LICENSE for details. + */ + +package com.marcozanon.macaco.databases; + +import com.marcozanon.macaco.MObject; +import com.marcozanon.macaco.text.MText; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; + +public class MSqlStatementResults extends MObject { + + protected PreparedStatement preparedStatement = null; + protected boolean localTypesMode = false; + + protected ResultSet resultSet = null; + + protected LinkedList> records = new LinkedList>(); + + protected LinkedHashSet generatedKeys = new LinkedHashSet(); + + /* */ + + public MSqlStatementResults(PreparedStatement preparedStatement, boolean localTypesMode) throws MSqlStatementException { + super(); + // + if (null == preparedStatement) { + throw new IllegalArgumentException("Invalid 'preparedStatement': null."); + } + // + this.preparedStatement = preparedStatement; + this.localTypesMode = localTypesMode; + // + try { + this.resultSet = this.getPreparedStatement().getResultSet(); + if (null != this.getResultSet()) { + while (this.getResultSet().next()) { + ResultSetMetaData resultSetMetaData = this.getResultSet().getMetaData(); + LinkedHashMap record = new LinkedHashMap(); + // + for (int f = 0; resultSetMetaData.getColumnCount() > f; f++) { + String field = resultSetMetaData.getColumnLabel(f + 1); + Object value = this.getResultSet().getObject(field); + // + if ((value instanceof java.sql.Date) && (this.getLocalTypesMode())) { + value = ((java.sql.Date)value).toLocalDate(); + } + else if ((value instanceof java.sql.Time) && (this.getLocalTypesMode())) { + value = ((java.sql.Time)value).toLocalTime(); + } + else if ((value instanceof java.sql.Timestamp) && (this.getLocalTypesMode())) { + value = ((java.sql.Timestamp)value).toLocalDateTime(); + } + // + record.put(field, value); + } + // + this.getRecords().add(record); + } + } + } + catch (SQLException exception) { + throw new MSqlStatementException("Could not retrieve statement records.", exception); + } + // + try { + ResultSet generatedKeysResultSet = this.getPreparedStatement().getGeneratedKeys(); + while (generatedKeysResultSet.next()) { + this.getGeneratedKeys().add(generatedKeysResultSet.getObject(1)); + } + generatedKeysResultSet.close(); + } + catch (SQLException exception) { + throw new MSqlStatementException("Could not retrieve generated keys.", exception); + } + } + + public void close() throws MDatabaseConnectionFailureException { + try { + if (null != this.getResultSet()) { + this.getResultSet().close(); + } + this.getPreparedStatement().close(); + } + catch (SQLException exception) { + throw new MDatabaseConnectionFailureException("Could not close statement and/or result set references.", exception); + } + } + + @Override + protected void finalize() { + try { + this.close(); + } + catch (Exception exception) { + } + } + + /* References. */ + + protected PreparedStatement getPreparedStatement() { + return this.preparedStatement; + } + + public ResultSet getResultSet() { + return this.resultSet; + } + + public boolean getLocalTypesMode() { + return this.localTypesMode; + } + + /* Records. */ + + public LinkedList> getRecords() { + return this.records; + } + + public LinkedList getRecordsByField(String field) { + if (MText.isBlank(field)) { + throw new IllegalArgumentException("Invalid 'field': null or empty."); + } + // + LinkedList recordsByField = new LinkedList(); + for (LinkedHashMap record: this.getRecords()) { + Object r = record.get(field); + if (null == r) { + throw new IllegalArgumentException(String.format("Invalid 'field': %s.", field)); + } + recordsByField.add(r); + } + // + return recordsByField; + } + + /* Generated keys. */ + + public LinkedHashSet getGeneratedKeys() { + return this.generatedKeys; + } + +} diff --git a/10.x/src/main/java/com/marcozanon/macaco/databases/MSqlTable.java b/10.x/src/main/java/com/marcozanon/macaco/databases/MSqlTable.java new file mode 100644 index 0000000..fb46fd1 --- /dev/null +++ b/10.x/src/main/java/com/marcozanon/macaco/databases/MSqlTable.java @@ -0,0 +1,177 @@ +/** + * Macaco + * Copyright (c) 2009-2024 Marco Zanon . + * See LICENSE for details. + */ + +package com.marcozanon.macaco.databases; + +import com.marcozanon.macaco.MObject; +import com.marcozanon.macaco.text.MText; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.StringJoiner; + +public class MSqlTable extends MObject { + + protected MDatabaseConnection databaseConnection = null; + protected String table = null; + protected String primaryKey = null; + + /* */ + + public MSqlTable(MDatabaseConnection databaseConnection, String table, String primaryKey) { + super(); + // + if (null == databaseConnection) { + throw new IllegalArgumentException("Invalid 'databaseConnection': null."); + } + if (MText.isBlank(table)) { + throw new IllegalArgumentException("Invalid 'table': null or empty."); + } + if (MText.isBlank(primaryKey)) { + throw new IllegalArgumentException("Invalid 'primaryKey': null or empty."); + } + // + this.databaseConnection = databaseConnection; + this.table = table; + this.primaryKey = primaryKey; + } + + /* Database connection. */ + + protected MDatabaseConnection getDatabaseConnection() { + return this.databaseConnection; + } + + /* Table. */ + + public String getTable() { + return this.table; + } + + /* Primary key. */ + + public String getPrimaryKey() { + return this.primaryKey; + } + + /* Statements. */ + + protected MSqlStatementResults insertRecord(LinkedHashMap map, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException { + if (null == map) { + throw new IllegalArgumentException("Invalid 'map': null."); + } + // + LinkedList p = new LinkedList(); + StringBuilder fields = new StringBuilder(""); + StringBuilder s = new StringBuilder(""); + // + for (String field: map.keySet()) { + fields.append("\"" + this.getTable() + "\".\"" + field + "\", "); + s.append("?, "); + p.add(map.get(field)); + } + fields.delete(fields.length() - 2, fields.length()); + s.delete(s.length() - 2, s.length()); + // + return this.getDatabaseConnection().executePreparedStatement(" INSERT INTO \"" + this.getTable() + "\"" + + " (" + fields + ")" + + " VALUES (" + s + ")", + p, true, loggableStatement); + } + + protected MSqlStatementResults updateRecord(LinkedHashMap map, Object id, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException { + if (null == map) { + throw new IllegalArgumentException("Invalid 'map': null."); + } + if (null == id) { + throw new IllegalArgumentException("Invalid 'id': null."); + } + // + LinkedList p = new LinkedList(); + StringBuilder s = new StringBuilder(""); + // + for (String field: map.keySet()) { + s.append("\"" + this.getTable() + "\".\"" + field + "\" = ?, "); + p.add(map.get(field)); + } + s.delete(s.length() - 2, s.length()); + p.add(id); + // + return this.getDatabaseConnection().executePreparedStatement(" UPDATE \"" + this.getTable() + "\"" + + " SET " + s + + " WHERE (\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" = ?)", + p, true, loggableStatement); + } + + public MSqlStatementResults setRecord(LinkedHashMap map, Object id) throws MDatabaseConnectionFailureException, MSqlStatementException { + return this.setRecord(map, id, true); + } + + public MSqlStatementResults setRecord(LinkedHashMap map, Object id, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException { + if (null == id) { + return this.insertRecord(map, loggableStatement); + } + else { + return this.updateRecord(map, id, loggableStatement); + } + } + + public MSqlStatementResults getRecord(Object id) throws MDatabaseConnectionFailureException, MSqlStatementException { + if (null == id) { + throw new IllegalArgumentException("Invalid 'id': null."); + } + // + LinkedList p = new LinkedList(); + p.add(id); + // + return this.getDatabaseConnection().executePreparedStatement(" SELECT *" + + " FROM \"" + this.getTable() + "\"" + + " WHERE (\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" = ?)", + p); + } + + public MSqlStatementResults deleteRecord(Object id) throws MDatabaseConnectionFailureException, MSqlStatementException { + if (null == id) { + throw new IllegalArgumentException("Invalid 'id': null."); + } + // + LinkedList p = new LinkedList(); + p.add(id); + // + return this.getDatabaseConnection().executePreparedStatement(" DELETE FROM \"" + this.getTable() + "\"" + + " WHERE (\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" = ?)", + p); + } + + public MSqlStatementResults deleteRecords(LinkedHashSet idSet) throws MDatabaseConnectionFailureException, MSqlStatementException { + if (null == idSet) { + throw new IllegalArgumentException("Invalid 'idSet': null."); + } + // + String whereClause = null; + LinkedList p = null; + // + if (0 == idSet.size()) { + whereClause = "0"; + } + else { + StringJoiner s = new StringJoiner(", "); + p = new LinkedList(); + // + for (Object id: idSet) { + s.add("?"); + p.add(id); + } + // + whereClause = "\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" IN (" + s.toString() + ")"; + } + // + return this.getDatabaseConnection().executePreparedStatement(" DELETE FROM \"" + this.getTable() + "\"" + + " WHERE (" + whereClause + ")", + p); + } + +} diff --git a/10.x/src/main/java/com/marcozanon/macaco/databases/MSqlTransactionException.java b/10.x/src/main/java/com/marcozanon/macaco/databases/MSqlTransactionException.java new file mode 100644 index 0000000..fcc3311 --- /dev/null +++ b/10.x/src/main/java/com/marcozanon/macaco/databases/MSqlTransactionException.java @@ -0,0 +1,30 @@ +/** + * Macaco + * Copyright (c) 2009-2024 Marco Zanon . + * See LICENSE for details. + */ + +package com.marcozanon.macaco.databases; + +@SuppressWarnings("serial") +public class MSqlTransactionException extends MDatabaseException { + + /* */ + + public MSqlTransactionException() { + super(); + } + + public MSqlTransactionException(String message) { + super(message); + } + + public MSqlTransactionException(Throwable error) { + super(error); + } + + public MSqlTransactionException(String message, Throwable error) { + super(message, error); + } + +} diff --git a/10.x/src/main/java/com/marcozanon/macaco/logging/MLogDatabaseTable.java b/10.x/src/main/java/com/marcozanon/macaco/logging/MLogDatabaseTable.java index c6ce3ef..d6f7efe 100644 --- a/10.x/src/main/java/com/marcozanon/macaco/logging/MLogDatabaseTable.java +++ b/10.x/src/main/java/com/marcozanon/macaco/logging/MLogDatabaseTable.java @@ -6,11 +6,11 @@ package com.marcozanon.macaco.logging; -import com.marcozanon.macaco.database.MDatabaseConnection; -import com.marcozanon.macaco.database.MDatabaseConnectionFailureException; -import com.marcozanon.macaco.database.MDatabaseConnectionPool; -import com.marcozanon.macaco.database.MSqlStatementException; -import com.marcozanon.macaco.database.MSqlTable; +import com.marcozanon.macaco.databases.MDatabaseConnection; +import com.marcozanon.macaco.databases.MDatabaseConnectionFailureException; +import com.marcozanon.macaco.databases.MDatabaseConnectionPool; +import com.marcozanon.macaco.databases.MSqlStatementException; +import com.marcozanon.macaco.databases.MSqlTable; import com.marcozanon.macaco.text.MText; import java.text.SimpleDateFormat; import java.util.Date;