package net.bull.javamelody;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.hibernate.Interceptor;
import org.hibernate.ScrollMode;
import org.hibernate.cfg.Settings;
import org.hibernate.jdbc.Batcher;
import org.hibernate.jdbc.BatcherFactory;
import org.hibernate.jdbc.BatchingBatcher;
import org.hibernate.jdbc.ConnectionManager;
import org.hibernate.jdbc.NonBatchingBatcher;

/**
 * Alternative pour monitorer les requêtes sql.
 * Cette classe se configure avec la propriété hibernate "hibernate.jdbc.factory_class"
 *
 * Par exemple: hibernate.jdbc.factory_class=net.bull.javamelody.HibernateBatcherFactory
 *
 * @author Emeric Vernat
 */
public class HibernateBatcherFactory implements BatcherFactory {

	private static class HibernateBatchingBatcher extends BatchingBatcher {
		HibernateBatchingBatcher(ConnectionManager connectionManager, Interceptor interceptor) {
			super(connectionManager, interceptor);
		}

		/** {@inheritDoc} */
		@Override
		public PreparedStatement prepareStatement(String sql) throws SQLException {
			// super.prepareStatement utilisera prepareStatement(String, boolean)
			// donc override a priori inutile mais on laisse createProxy vérifier
			return createPreparedStatementProxy(sql, super.prepareStatement(sql));
		}

		/** {@inheritDoc} */
		@Override
		public PreparedStatement prepareBatchStatement(String sql) throws SQLException {
			// prepareBatchStatement utilisera prepareStatement(String, boolean)
			// donc override a priori inutile mais on laisse createProxy vérifier
			return createPreparedStatementProxy(sql, super.prepareBatchStatement(sql));
		}

		/** {@inheritDoc} */
		@Override
		public PreparedStatement prepareStatement(String sql, boolean getGeneratedKeys)
				throws SQLException {
			return createPreparedStatementProxy(sql, super.prepareStatement(sql, getGeneratedKeys));
		}

		/** {@inheritDoc} */
		@Override
		public PreparedStatement prepareSelectStatement(String sql) throws SQLException {
			return createPreparedStatementProxy(sql, super.prepareSelectStatement(sql));
		}

		/** {@inheritDoc} */
		@Override
		public PreparedStatement prepareStatement(String sql, String[] columnNames)
				throws SQLException {
			return createPreparedStatementProxy(sql, super.prepareStatement(sql, columnNames));
		}

		/** {@inheritDoc} */
		@Override
		public PreparedStatement prepareQueryStatement(String sql, boolean scrollable,
				ScrollMode scrollMode) throws SQLException {
			return createPreparedStatementProxy(sql,
					super.prepareQueryStatement(sql, scrollable, scrollMode));
		}

		/** {@inheritDoc} */
		@Override
		public CallableStatement prepareCallableStatement(String sql) throws SQLException {
			return createCallableStatementProxy(sql, super.prepareCallableStatement(sql));
		}

		/** {@inheritDoc} */
		@Override
		public CallableStatement prepareCallableQueryStatement(String sql, boolean scrollable,
				ScrollMode scrollMode) throws SQLException {
			return createCallableStatementProxy(sql,
					super.prepareCallableQueryStatement(sql, scrollable, scrollMode));
		}

		/** {@inheritDoc} */
		@Override
		public CallableStatement prepareBatchCallableStatement(String sql) throws SQLException {
			// prepareBatchCallableStatement utilisera prepareCallableStatement(String)
			// donc override a priori inutile mais on laisse createProxy vérifier
			return createCallableStatementProxy(sql, super.prepareBatchCallableStatement(sql));
		}
	}

	private static class HibernateNonBatchingBatcher extends NonBatchingBatcher {
		HibernateNonBatchingBatcher(ConnectionManager connectionManager, Interceptor interceptor) {
			super(connectionManager, interceptor);
		}

		/** {@inheritDoc} */
		@Override
		public PreparedStatement prepareStatement(String sql) throws SQLException {
			// super.prepareStatement utilisera prepareStatement(String, boolean)
			// donc override a priori inutile mais on laisse createProxy vérifier
			return createPreparedStatementProxy(sql, super.prepareStatement(sql));
		}

		/** {@inheritDoc} */
		@Override
		public PreparedStatement prepareBatchStatement(String sql) throws SQLException {
			// prepareBatchStatement utilisera prepareStatement(String, boolean)
			// donc override a priori inutile mais on laisse createProxy vérifier
			return createPreparedStatementProxy(sql, super.prepareBatchStatement(sql));
		}

		/** {@inheritDoc} */
		@Override
		public PreparedStatement prepareSelectStatement(String sql) throws SQLException {
			return createPreparedStatementProxy(sql, super.prepareSelectStatement(sql));
		}

		/** {@inheritDoc} */
		@Override
		public PreparedStatement prepareStatement(String sql, boolean getGeneratedKeys)
				throws SQLException {
			return createPreparedStatementProxy(sql, super.prepareStatement(sql, getGeneratedKeys));
		}

		/** {@inheritDoc} */
		@Override
		public PreparedStatement prepareStatement(String sql, String[] columnNames)
				throws SQLException {
			return createPreparedStatementProxy(sql, super.prepareStatement(sql, columnNames));
		}

		/** {@inheritDoc} */
		@Override
		public PreparedStatement prepareQueryStatement(String sql, boolean scrollable,
				ScrollMode scrollMode) throws SQLException {
			return createPreparedStatementProxy(sql,
					super.prepareQueryStatement(sql, scrollable, scrollMode));
		}

		/** {@inheritDoc} */
		@Override
		public CallableStatement prepareBatchCallableStatement(String sql) throws SQLException {
			// prepareBatchCallableStatement utilisera prepareCallableStatement(String)
			// donc override a priori inutile mais on laisse createProxy vérifier
			return createCallableStatementProxy(sql, super.prepareBatchCallableStatement(sql));
		}

		/** {@inheritDoc} */
		@Override
		public CallableStatement prepareCallableStatement(String sql) throws SQLException {
			return createCallableStatementProxy(sql, super.prepareCallableStatement(sql));
		}

		/** {@inheritDoc} */
		@Override
		public CallableStatement prepareCallableQueryStatement(String sql, boolean scrollable,
				ScrollMode scrollMode) throws SQLException {
			return createCallableStatementProxy(sql,
					super.prepareCallableQueryStatement(sql, scrollable, scrollMode));
		}
	}

	static PreparedStatement createPreparedStatementProxy(String query, PreparedStatement statement) {
		return (PreparedStatement) JdbcWrapper.SINGLETON.createStatementProxy(query, statement);
	}

	static CallableStatement createCallableStatementProxy(String query, CallableStatement statement) {
		return (CallableStatement) JdbcWrapper.SINGLETON.createStatementProxy(query, statement);
	}

	/** {@inheritDoc} */
	public Batcher createBatcher(ConnectionManager connectionManager, Interceptor interceptor) {
		final boolean sqlMonitoringDisabled = JdbcWrapper.SINGLETON.isSqlMonitoringDisabled();
		final Settings settings = connectionManager.getFactory().getSettings();
		if (sqlMonitoringDisabled) {
			if (settings.getJdbcBatchSize() == 0) {
				return new NonBatchingBatcher(connectionManager, interceptor);
			}
			return new BatchingBatcher(connectionManager, interceptor);
		}

		final Batcher result;
		if (settings.getJdbcBatchSize() == 0) {
			result = new HibernateBatchingBatcher(connectionManager, interceptor);
		} else {
			result = new HibernateNonBatchingBatcher(connectionManager, interceptor);
		}
		LOG.debug("hibernate batcher factory initialized");
		return result;
	}
}

@@ -7381,32 +7381,35 @@
  = new Hibernate
+Non
 BatchingBatcher(
@@ -7471,35 +7471,32 @@
  = new Hibernate
-Non
 BatchingBatcher(

fix pour issue 107: Hibernate factory doesn't work with HSQL with batching turned off