Transaction.java
- /*
- * Copyright (c) 2002-2022, City of 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.lang3.StringUtils;
- import org.apache.logging.log4j.LogManager;
- import org.apache.logging.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 MESSAGE_PLUGIN = "Plugin : '";
- 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
- {
- return prepareStatement( strSQL, null, true );
- }
- /**
- * 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, Integer autoGeneratedKeys ) throws SQLException
- {
- return prepareStatement( strSQL, autoGeneratedKeys, true );
- }
- /**
- * 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, boolean bLogQueries ) throws SQLException
- {
- return prepareStatement( strSQL, null, bLogQueries );
- }
- /**
- * Gets a prepared statement
- *
- * @since 6.0.0
- * @param strSQL
- * The SQL statement
- * @param autoGeneratedKeys
- * a flag indicating whether auto-generated keys should be returned; For example one of <code>Statement.RETURN_GENERATED_KEYS</code> or
- * <code>Statement.NO_GENERATED_KEYS</code>. See {@link PreparedStatement#prepareStatement(String, int)}
- * @return The prepared statement
- * @throws SQLException
- * If an SQL error occurs
- */
- public PreparedStatement prepareStatement( String strSQL, Integer autoGeneratedKeys, boolean bLogQueries ) 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( MESSAGE_PLUGIN + _strPluginName + "' - Connection has been closed. The new prepared statement can not be created : "
- + ( bLogQueries ? _strSQL : "(query log disabled)" ) );
- }
- if ( autoGeneratedKeys != null )
- {
- _statement = _connection.prepareStatement( _strSQL, autoGeneratedKeys );
- }
- else
- {
- _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( "{} {}' - EXECUTE STATEMENT : {}", MESSAGE_PLUGIN, _strPluginName, _strSQL );
- _statement.executeUpdate( );
- }
- /**
- * Commit the transaction
- */
- public void commit( )
- {
- try
- {
- if ( _connection == null )
- {
- throw new SQLException( MESSAGE_PLUGIN + _strPluginName + "' - Transaction has already been closed and can not be committed" );
- }
- _connection.commit( );
- _logger.debug( "{} {}' - COMMIT TRANSACTION", MESSAGE_PLUGIN, _strPluginName );
- 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( "{} {}' - ROLLBACK TRANSACTION", MESSAGE_PLUGIN, _strPluginName );
- }
- else
- {
- _logger.debug( "{} {}' - TRANSACTION HAS ALREADY BEEN ROLLED BACK", MESSAGE_PLUGIN, _strPluginName );
- }
- }
- catch( SQLException ex )
- {
- _logger.error( "Transaction Error - Rollback error : {}", ex.getMessage( ), ex );
- }
- 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 = LogManager.getLogger( LOGGER_DEBUG_SQL + _strPluginName );
- _logger.debug( "{}{}' - BEGIN TRANSACTION", MESSAGE_PLUGIN, _strPluginName );
- 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 );
- }
- 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( );
- }
- }