/* * Copyright 2014 - 2017 Blazebit. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.blazebit.persistence.impl.hibernate; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import com.blazebit.persistence.spi.DbmsDialect; import org.hibernate.ScrollMode; import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.engine.jdbc.spi.StatementPreparer; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor; public class StatementPreparerImpl implements StatementPreparer { private JdbcCoordinator jdbcCoordinator; private SessionFactoryImplementor sessionFactoryImplementor; private DbmsDialect dbmsDialect; private String[][] columns; private int[] returningSqlTypes; private HibernateReturningResult<?> returningResult; public StatementPreparerImpl(JdbcCoordinator jdbcCoordinator, SessionFactoryImplementor sessionFactoryImplementor, DbmsDialect dbmsDialect, String[][] columns, int[] returningSqlTypes, HibernateReturningResult<?> returningResult) { this.jdbcCoordinator = jdbcCoordinator; this.sessionFactoryImplementor = sessionFactoryImplementor; this.dbmsDialect = dbmsDialect; this.columns = columns; this.returningSqlTypes = returningSqlTypes; this.returningResult = returningResult; } protected final SessionFactoryOptions settings() { return sessionFactoryImplementor.getSessionFactoryOptions(); } protected final Connection connection() { return logicalConnection().getPhysicalConnection(); } protected final LogicalConnectionImplementor logicalConnection() { return jdbcCoordinator.getLogicalConnection(); } protected final SqlExceptionHelper sqlExceptionHelper() { return getJdbcService().getSqlExceptionHelper(); } @Override public Statement createStatement() { throw new UnsupportedOperationException("Not yet implemented!"); } @Override public PreparedStatement prepareStatement(String sql) { throw new UnsupportedOperationException("Not yet implemented!"); } @Override public PreparedStatement prepareStatement(String sql, final boolean isCallable) { throw new UnsupportedOperationException("Not yet implemented!"); } private void checkAutoGeneratedKeysSupportEnabled() { // Not sure if we should respect this // if (!settings().isGetGeneratedKeysEnabled()) { // throw new AssertionFailure("getGeneratedKeys() support is not enabled"); // } } @Override public PreparedStatement prepareStatement(String sql, final int autoGeneratedKeys) { throw new UnsupportedOperationException("Not yet implemented!"); } @Override public PreparedStatement prepareStatement(String sql, final String[] columnNames) { throw new UnsupportedOperationException("Not yet implemented!"); } @Override public PreparedStatement prepareQueryStatement(String sql, final boolean isCallable, final ScrollMode scrollMode) { checkAutoGeneratedKeysSupportEnabled(); jdbcCoordinator.executeBatch(); PreparedStatement ps = new QueryStatementPreparationTemplate(sql) { public PreparedStatement doPrepare() throws SQLException { PreparedStatement ps; ps = connection().prepareStatement(sql, dbmsDialect.getPrepareFlags()); return dbmsDialect.prepare(ps, returningSqlTypes); } }.prepareStatement(); ps = (PreparedStatement) Proxy.newProxyInstance(ps.getClass().getClassLoader(), new Class[]{PreparedStatement.class}, new PreparedStatementInvocationHandler(ps, dbmsDialect, columns, returningResult)); jdbcCoordinator.registerLastQuery(ps); return ps; } private abstract class StatementPreparationTemplate { protected final String sql; protected StatementPreparationTemplate(String incomingSql) { final String inspectedSql = jdbcCoordinator.getJdbcSessionOwner().getJdbcSessionContext().getStatementInspector().inspect(incomingSql); this.sql = inspectedSql == null ? incomingSql : inspectedSql; } public PreparedStatement prepareStatement() { try { getJdbcService().getSqlStatementLogger().logStatement(sql); final PreparedStatement preparedStatement; try { jdbcCoordinator.getJdbcSessionOwner().getJdbcSessionContext().getObserver().jdbcPrepareStatementStart(); preparedStatement = doPrepare(); setStatementTimeout(preparedStatement); } finally { jdbcCoordinator.getJdbcSessionOwner().getJdbcSessionContext().getObserver().jdbcPrepareStatementEnd(); } postProcess(preparedStatement); return preparedStatement; } catch (SQLException e) { throw sqlExceptionHelper().convert(e, "could not prepare statement", sql); } } protected abstract PreparedStatement doPrepare() throws SQLException; public void postProcess(PreparedStatement preparedStatement) throws SQLException { jdbcCoordinator.getResourceRegistry().register(preparedStatement, true); // logicalConnection().notifyObserversStatementPrepared(); } private void setStatementTimeout(PreparedStatement preparedStatement) throws SQLException { final int remainingTransactionTimeOutPeriod = jdbcCoordinator.determineRemainingTransactionTimeOutPeriod(); if (remainingTransactionTimeOutPeriod > 0) { preparedStatement.setQueryTimeout(remainingTransactionTimeOutPeriod); } } } private JdbcServices getJdbcService() { return jdbcCoordinator .getJdbcSessionOwner() .getJdbcSessionContext() .getServiceRegistry() .getService(JdbcServices.class); } private abstract class QueryStatementPreparationTemplate extends StatementPreparationTemplate { protected QueryStatementPreparationTemplate(String sql) { super(sql); } public void postProcess(PreparedStatement preparedStatement) throws SQLException { super.postProcess(preparedStatement); setStatementFetchSize(preparedStatement); } } private void setStatementFetchSize(PreparedStatement statement) throws SQLException { if (settings().getJdbcFetchSize() != null) { statement.setFetchSize(settings().getJdbcFetchSize()); } } }