package org.simpleflatmapper.jdbc.impl; import org.simpleflatmapper.jdbc.Crud; import org.simpleflatmapper.jdbc.JdbcMapper; import org.simpleflatmapper.jdbc.QueryPreparer; import org.simpleflatmapper.jdbc.SelectQuery; import org.simpleflatmapper.map.Mapper; import org.simpleflatmapper.util.ErrorHelper; import org.simpleflatmapper.util.CheckedConsumer; import java.lang.reflect.Type; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; public final class DefaultCrud<T, K> implements Crud<T,K> { protected final QueryPreparer<T> insertQueryPreparer; protected final QueryPreparer<T> updateQueryPreparer; protected final QueryPreparer<K> selectQueryPreparer; protected final QueryPreparer<K> deleteQueryPreparer; protected final QueryPreparer<T> upsertQueryPreparer; protected final KeyTupleQueryPreparer<K> keyTupleQueryPreparer; protected final JdbcMapper<T> selectQueryMapper; protected final JdbcMapper<K> keyMapper; protected final String table; protected final boolean hasGeneratedKeys; protected final SelectQueryWhereFactory<T> selectQueryWhereFactory; public DefaultCrud(QueryPreparer<T> insertQueryPreparer, QueryPreparer<T> updateQueryPreparer, QueryPreparer<K> selectQueryPreparer, QueryPreparer<T> upsertQueryPreparer, KeyTupleQueryPreparer<K> keyTupleQueryPreparer, JdbcMapper<T> selectQueryMapper, QueryPreparer<K> deleteQueryPreparer, JdbcMapper<K> keyMapper, String table, boolean hasGeneratedKeys, SelectQueryWhereFactory<T> selectQueryWhereFactory) { this.insertQueryPreparer = insertQueryPreparer; this.updateQueryPreparer = updateQueryPreparer; this.selectQueryPreparer = selectQueryPreparer; this.upsertQueryPreparer = upsertQueryPreparer; this.keyTupleQueryPreparer = keyTupleQueryPreparer; this.deleteQueryPreparer = deleteQueryPreparer; this.selectQueryMapper = selectQueryMapper; this.keyMapper = keyMapper; this.table = table; this.hasGeneratedKeys = hasGeneratedKeys; this.selectQueryWhereFactory = selectQueryWhereFactory; } @Override public void create(Connection connection, T value) throws SQLException { create(connection, value, null); } @Override public void create(Connection connection, Collection<T> values) throws SQLException { create(connection, values, null); } @Override public <RH extends CheckedConsumer<? super K>> RH create(Connection connection, T value, RH keyConsumer) throws SQLException { return executeQueryPreparer(connection, value, keyConsumer, insertQueryPreparer); } @Override public <RH extends CheckedConsumer<? super K>> RH create(Connection connection, Collection<T> values, RH keyConsumer) throws SQLException { return executeQueryPreparerInBatchMode(connection, values, keyConsumer, insertQueryPreparer); } @Override public T read(Connection connection, K key) throws SQLException { PreparedStatement preparedStatement = selectQueryPreparer.prepare(connection).bind(key); try { ResultSet resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { return selectQueryMapper.map(resultSet); } } finally { safeClose(preparedStatement); } return null; } private void safeClose(PreparedStatement preparedStatement) { try { preparedStatement.close(); } catch (SQLException e) {} } @Override public <RH extends CheckedConsumer<? super T>> RH read(Connection connection, Collection<K> keys, RH consumer) throws SQLException { PreparedStatement preparedStatement = keyTupleQueryPreparer.prepareStatement("SELECT * FROM " + table + " WHERE ", connection, keys.size()); try { keyTupleQueryPreparer.bindTo(keys, preparedStatement, 0); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { consumer.accept(selectQueryMapper.map(resultSet)); } return consumer; } catch(Exception e) { return ErrorHelper.rethrow(e); } finally { safeClose(preparedStatement); } } @Override public void update(Connection connection, T value) throws SQLException { executeQueryPreparer(connection, value, null, updateQueryPreparer); } @Override public void update(Connection connection, Collection<T> values) throws SQLException { executeQueryPreparerInBatchMode(connection, values, null, updateQueryPreparer); } @Override public void delete(Connection connection, K key) throws SQLException { executeQueryPreparer(connection, key, null, deleteQueryPreparer); } @Override public void delete(Connection connection, Collection<K> keys) throws SQLException { PreparedStatement preparedStatement = keyTupleQueryPreparer.prepareStatement("DELETE FROM " + table + " WHERE ", connection, keys.size()); try { keyTupleQueryPreparer.bindTo(keys, preparedStatement, 0); preparedStatement.executeUpdate(); } catch (Exception e) { ErrorHelper.rethrow(e); } finally { safeClose(preparedStatement); } } @Override public void createOrUpdate(Connection connection, T value) throws SQLException { createOrUpdate(connection, value, null); } @Override public void createOrUpdate(Connection connection, Collection<T> values) throws SQLException { createOrUpdate(connection, values, null); } @Override public <RH extends CheckedConsumer<? super K>> RH createOrUpdate(Connection connection, T value, RH keyConsumer) throws SQLException { return executeQueryPreparer(connection, value, keyConsumer, upsertQueryPreparer); } @Override public <RH extends CheckedConsumer<? super K>> RH createOrUpdate(Connection connection, Collection<T> values, RH keyConsumer) throws SQLException { return executeQueryPreparerInBatchMode(connection, values, keyConsumer, upsertQueryPreparer); } @Override public <P> SelectQuery<T, P> where(String whereClause, Type paramClass) { return selectQueryWhereFactory.where(whereClause, paramClass); } protected <RH extends CheckedConsumer<? super K>> RH executeQueryPreparerInBatchMode(Connection connection, Collection<T> values, RH keyConsumer, QueryPreparer<T> queryPreparer) throws SQLException { PreparedStatement preparedStatement = queryPreparer.prepareStatement(connection); try { Mapper<T, PreparedStatement> mapper = queryPreparer.mapper(); for (T value : values) { mapper.mapTo(value, preparedStatement, null); preparedStatement.addBatch(); } preparedStatement.executeBatch(); if (hasGeneratedKeys && keyConsumer != null) { handleGeneratedKeys(keyConsumer, preparedStatement); } return keyConsumer; } catch(Exception e) { ErrorHelper.rethrow(e); } finally { safeClose(preparedStatement); } return keyConsumer; } protected <RH extends CheckedConsumer<? super K>, QPT> RH executeQueryPreparer(Connection connection, QPT value, RH keyConsumer, QueryPreparer<QPT> queryPreparer) throws SQLException { PreparedStatement preparedStatement = queryPreparer.prepare(connection).bind(value); try { preparedStatement.executeUpdate(); if (hasGeneratedKeys && keyConsumer != null) { handleGeneratedKeys(keyConsumer, preparedStatement); } return keyConsumer; } finally { safeClose(preparedStatement); } } protected void handleGeneratedKeys(CheckedConsumer<? super K> keyConsumer, PreparedStatement preparedStatement) throws SQLException { ResultSet keys = preparedStatement.getGeneratedKeys(); try { while (keys.next()) { try { keyConsumer.accept(keyMapper.map(keys)); } catch (Exception e) { ErrorHelper.rethrow(e); } } } finally { keys.close(); } } }