===================
10.0.0 (2024-03-02)
===================
+* Renamed 'database' package to 'databases'.
* Renamed 'conversion' package to 'conversions'.
* Renamed some methods and variables.
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
- * 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<Object>());
- }
-
- public MSqlStatementResults executePreparedStatement(String statement, LinkedList<Object> parameters) throws MDatabaseConnectionFailureException, MSqlStatementException {
- return this.executePreparedStatement(statement, parameters, this.getLocalTypesMode());
- }
-
- public MSqlStatementResults executePreparedStatement(String statement, LinkedList<Object> parameters, boolean localTypesMode) throws MDatabaseConnectionFailureException, MSqlStatementException {
- return this.executePreparedStatement(statement, parameters, this.getLocalTypesMode(), true);
- };
-
- public MSqlStatementResults executePreparedStatement(String statement, LinkedList<Object> 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<LinkedHashMap<String, Object>> resultList = results.getRecords();
- //
- return (String)resultList.get(0).get("VERSION()");
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
- * 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);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
- * 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;
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
- * 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<MDatabaseConnection> databaseConnections = new LinkedList<MDatabaseConnection>();
- 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<MDatabaseConnection> 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<MDatabaseConnection> getDatabaseConnections() {
- return this.databaseConnections;
- }
-
- protected int getMinimumSize() {
- return this.minimumSize;
- }
-
- protected int getMaximumSize() {
- return this.maximumSize;
- }
-
- public synchronized MDatabaseConnection popDatabaseConnection() throws MDatabaseConnectionFailureException {
- LinkedList<MDatabaseConnection> 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<MDatabaseConnection> 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);
- }
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
- * 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);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
- * 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);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
- * 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<LinkedHashMap<String, Object>> records = new LinkedList<LinkedHashMap<String, Object>>();
-
- protected LinkedHashSet<Object> generatedKeys = new LinkedHashSet<Object>();
-
- /* */
-
- 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<String, Object> record = new LinkedHashMap<String, Object>();
- //
- 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<LinkedHashMap<String, Object>> getRecords() {
- return this.records;
- }
-
- public LinkedList<Object> getRecordsByField(String field) {
- if (MText.isBlank(field)) {
- throw new IllegalArgumentException("Invalid 'field': null or empty.");
- }
- //
- LinkedList<Object> recordsByField = new LinkedList<Object>();
- for (LinkedHashMap<String, Object> 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<Object> getGeneratedKeys() {
- return this.generatedKeys;
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
- * 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<String, Object> map, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException {
- if (null == map) {
- throw new IllegalArgumentException("Invalid 'map': null.");
- }
- //
- LinkedList<Object> p = new LinkedList<Object>();
- 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<String, Object> 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<Object> p = new LinkedList<Object>();
- 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<String, Object> map, Object id) throws MDatabaseConnectionFailureException, MSqlStatementException {
- return this.setRecord(map, id, true);
- }
-
- public MSqlStatementResults setRecord(LinkedHashMap<String, Object> 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<Object> p = new LinkedList<Object>();
- 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<Object> p = new LinkedList<Object>();
- 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<Object> p = null;
- //
- if (0 == idSet.size()) {
- whereClause = "0";
- }
- else {
- StringJoiner s = new StringJoiner(", ");
- p = new LinkedList<Object>();
- //
- 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);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
- * 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);
- }
-
-}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
+ * 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<Object>());
+ }
+
+ public MSqlStatementResults executePreparedStatement(String statement, LinkedList<Object> parameters) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ return this.executePreparedStatement(statement, parameters, this.getLocalTypesMode());
+ }
+
+ public MSqlStatementResults executePreparedStatement(String statement, LinkedList<Object> parameters, boolean localTypesMode) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ return this.executePreparedStatement(statement, parameters, this.getLocalTypesMode(), true);
+ };
+
+ public MSqlStatementResults executePreparedStatement(String statement, LinkedList<Object> 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<LinkedHashMap<String, Object>> resultList = results.getRecords();
+ //
+ return (String)resultList.get(0).get("VERSION()");
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
+ * 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);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
+ * 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;
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
+ * 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<MDatabaseConnection> databaseConnections = new LinkedList<MDatabaseConnection>();
+ 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<MDatabaseConnection> 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<MDatabaseConnection> getDatabaseConnections() {
+ return this.databaseConnections;
+ }
+
+ protected int getMinimumSize() {
+ return this.minimumSize;
+ }
+
+ protected int getMaximumSize() {
+ return this.maximumSize;
+ }
+
+ public synchronized MDatabaseConnection popDatabaseConnection() throws MDatabaseConnectionFailureException {
+ LinkedList<MDatabaseConnection> 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<MDatabaseConnection> 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);
+ }
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
+ * 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);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
+ * 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);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
+ * 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<LinkedHashMap<String, Object>> records = new LinkedList<LinkedHashMap<String, Object>>();
+
+ protected LinkedHashSet<Object> generatedKeys = new LinkedHashSet<Object>();
+
+ /* */
+
+ 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<String, Object> record = new LinkedHashMap<String, Object>();
+ //
+ 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<LinkedHashMap<String, Object>> getRecords() {
+ return this.records;
+ }
+
+ public LinkedList<Object> getRecordsByField(String field) {
+ if (MText.isBlank(field)) {
+ throw new IllegalArgumentException("Invalid 'field': null or empty.");
+ }
+ //
+ LinkedList<Object> recordsByField = new LinkedList<Object>();
+ for (LinkedHashMap<String, Object> 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<Object> getGeneratedKeys() {
+ return this.generatedKeys;
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
+ * 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<String, Object> map, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ if (null == map) {
+ throw new IllegalArgumentException("Invalid 'map': null.");
+ }
+ //
+ LinkedList<Object> p = new LinkedList<Object>();
+ 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<String, Object> 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<Object> p = new LinkedList<Object>();
+ 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<String, Object> map, Object id) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ return this.setRecord(map, id, true);
+ }
+
+ public MSqlStatementResults setRecord(LinkedHashMap<String, Object> 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<Object> p = new LinkedList<Object>();
+ 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<Object> p = new LinkedList<Object>();
+ 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<Object> p = null;
+ //
+ if (0 == idSet.size()) {
+ whereClause = "0";
+ }
+ else {
+ StringJoiner s = new StringJoiner(", ");
+ p = new LinkedList<Object>();
+ //
+ 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);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2024 Marco Zanon <info@marcozanon.com>.
+ * 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);
+ }
+
+}
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;