Java tutorial
/* * Copyright (c) 2002-2014, Mairie de Paris * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright notice * and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice * and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * License 1.0 */ package fr.paris.lutece.util.sql; import fr.paris.lutece.portal.service.database.AppConnectionService; import fr.paris.lutece.portal.service.database.PluginConnectionService; import fr.paris.lutece.portal.service.plugin.Plugin; import fr.paris.lutece.portal.service.util.AppException; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; /** * Transaction */ public class Transaction { /** * Status for opened transactions */ public static final int OPENED = -1; /** * Status for committed transactions */ public static final int COMMITTED = 0; /** * Status for roll backed transactions */ public static final int ROLLEDBACK = 1; private static final String DEFAULT_MODULE_NAME = "core"; private static final String LOGGER_DEBUG_SQL = "lutece.debug.sql."; /** * The last SQL query executed by this transaction */ private String _strSQL = StringUtils.EMPTY; /** JDBC Connection */ private Connection _connection; /** Connection Service providing connection from a defined pool */ private PluginConnectionService _connectionService; /** Plugin name */ private String _strPluginName; /** The debug logger */ private Logger _logger; private PreparedStatement _statement; private int _nStatus = OPENED; private boolean _bAutoCommit; /** * Constructor */ public Transaction() { beginTransaction(null); } /** * Constructor * @param plugin The plugin owner of the transaction */ public Transaction(Plugin plugin) { beginTransaction(plugin); } /** * Gets a prepared statement * @param strSQL The SQL statement * @return The prepared statement * @throws SQLException If an SQL error occurs */ public PreparedStatement prepareStatement(String strSQL) throws SQLException { // Close the previous statement if exists if (_statement != null) { _statement.close(); } // Get a new statement _strSQL = strSQL; if (_connection == null) { throw new SQLException("Plugin : '" + _strPluginName + "' - Connection has been closed. The new prepared statement can not be created : " + strSQL); } _statement = _connection.prepareStatement(_strSQL); return _statement; } /** * The current prepared statement * @return The current statement */ public PreparedStatement getStatement() { return _statement; } /** * Execute the current statement * @throws SQLException If an SQL error occurs */ public void executeStatement() throws SQLException { _logger.debug("Plugin : '" + _strPluginName + "' - EXECUTE STATEMENT : " + _strSQL); _statement.executeUpdate(); } /** * Commit the transaction */ public void commit() { try { if (_connection == null) { throw new SQLException("Plugin : '" + _strPluginName + "' - Transaction has already been closed and can not be committed"); } _connection.commit(); _logger.debug("Plugin : '" + _strPluginName + "' - COMMIT TRANSACTION"); closeTransaction(COMMITTED); } catch (SQLException e) { rollback(e); } } /** * Rollback the transaction */ public void rollback() { rollback(null); } /** * Rollback the transaction * @param e The exception that cause the rollback */ public void rollback(Exception e) { if (e != null) { _logger.error("Transaction Error - Rollback in progress " + e.getMessage(), e.getCause()); } try { if (_connection != null) { _connection.rollback(); _logger.debug("Plugin : '" + _strPluginName + "' - ROLLBACK TRANSACTION"); } else { _logger.debug("Plugin : '" + _strPluginName + "' - TRANSACTION HAS ALREADY BEEN ROLLED BACK"); } } catch (SQLException ex) { _logger.error("Transaction Error - Rollback error : " + ex.getMessage(), ex.getCause()); } finally { closeTransaction(ROLLEDBACK); } } /** * Return the transaction status * @return The transaction status */ public int getStatus() { return _nStatus; } /** * Get the underlying connection. * @return The connection of this transaction. If the transaction has not * begin, then return null. */ protected Connection getConnection() { return _connection; } /** * Begin a transaction * @param plugin The plugin owner of the transaction */ protected void beginTransaction(Plugin plugin) { if (plugin != null) { _strPluginName = plugin.getName(); _connectionService = plugin.getConnectionService(); } else { _strPluginName = DEFAULT_MODULE_NAME; _connectionService = AppConnectionService.getDefaultConnectionService(); } if (_connectionService == null) { throw new AppException( "Database access error. Please check component installations and db.properties."); } _logger = Logger.getLogger(LOGGER_DEBUG_SQL + _strPluginName); _logger.debug("Plugin : '" + _strPluginName + "' - BEGIN TRANSACTION"); try { _connection = _connectionService.getConnection(); // Save the autocommit configuration of the connection _bAutoCommit = _connection.getAutoCommit(); _connection.setAutoCommit(false); } catch (SQLException e) { rollback(e); } } /** * Close the transaction * @param nStatus The status of the transaction */ private void closeTransaction(int nStatus) { _nStatus = nStatus; try { if (_statement != null) { _statement.close(); } // Restore the autocommit configuration of the connection if (_connection != null) { _connection.setAutoCommit(_bAutoCommit); } } catch (SQLException ex) { _logger.error("Transaction Error - Unable to close transaction " + ex.getMessage(), ex.getCause()); } finally { _connectionService.freeConnection(_connection); _connection = null; } } /** * Checks that the transaction has been committed (or rolled back) before * being destroyed and release all transaction resources (statement, * connection, ...) if not. {@inheritDoc } */ @Override protected void finalize() throws Throwable { if (_nStatus == OPENED) { _logger.error("The transaction has not been commited"); closeTransaction(OPENED); } super.finalize(); } }