package org.sfm.jdbc;

import org.sfm.utils.ErrorHelper;
import org.sfm.utils.RowHandler;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Crud<T, K> {

    private final QueryPreparer<T> insertQueryPreparer;
    private final QueryPreparer<T> updateQueryPreparer;
    private final QueryPreparer<K> selectQueryPreparer;
    private final QueryPreparer<K> deleteQueryPreparer;
    private final JdbcMapper<T> selectQueryMapper;
    private final JdbcMapper<K> keyMapper;

    public Crud(QueryPreparer<T> insertQueryPreparer,
                QueryPreparer<T> updateQueryPreparer,
                QueryPreparer<K> selectQueryPreparer,
                JdbcMapper<T> selectQueryMapper,
                QueryPreparer<K> deleteQueryPreparer, JdbcMapper<K> keyMapper) {
        this.insertQueryPreparer = insertQueryPreparer;
        this.updateQueryPreparer = updateQueryPreparer;
        this.selectQueryPreparer = selectQueryPreparer;
        this.deleteQueryPreparer = deleteQueryPreparer;
        this.selectQueryMapper = selectQueryMapper;
        this.keyMapper = keyMapper;
    }

    public void create(Connection connection, T value) throws SQLException {
        create(connection, value, null);
    }

    public <RH extends RowHandler<? super K>> RH create(Connection connection, T value, RH keyConsumer) throws SQLException {
        PreparedStatement preparedStatement = insertQueryPreparer.prepare(connection).bind(value);
        try {
            preparedStatement.executeUpdate();
            if (keyConsumer != null) {
                handeGeneratedKeys(keyConsumer, preparedStatement);
            }
            return  keyConsumer;
        } finally {
            try { preparedStatement.close(); }
            catch (SQLException e) {
                // IGNORE
            }
        }
    }

    private void handeGeneratedKeys(RowHandler<? super K> keyConsumer, PreparedStatement preparedStatement) throws SQLException {
        ResultSet keys = preparedStatement.getGeneratedKeys();
        try {
            if (keys.next()) {
                try {
                    keyConsumer.handle(keyMapper.map(keys));
                } catch (Exception e) {
                    ErrorHelper.rethrow(e);
                }
            }
        } finally {
            keys.close();
        }
    }


    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);
            }
            return null;
        } finally {
            try { preparedStatement.close(); }
            catch (SQLException e) {
                // IGNORE
            }
        }
    }

    public void update(Connection connection, T value) throws SQLException {
        PreparedStatement preparedStatement = updateQueryPreparer.prepare(connection).bind(value);
        try {
            preparedStatement.executeUpdate();
        } finally {
            try { preparedStatement.close(); }
            catch (SQLException e) {
                // IGNORE
            }
        }
    }

    public void delete(Connection connection, K key) throws SQLException {
        PreparedStatement preparedStatement = deleteQueryPreparer.prepare(connection).bind(key);
        try {
            preparedStatement.executeUpdate();
        } finally {
            try { preparedStatement.close(); }
            catch (SQLException e) {
                // IGNORE
            }
        }
    }
}

@@ -205,16 +205,84 @@
 ption;%0A%0A
+/**%0A *%0A * @param %3CT%3E the target type%0A * @param %3CK%3E the key type%0A */%0A
 public c
@@ -1238,128 +1238,687 @@
     
-public void create(Connection connection, T value) throws SQLException %7B%0A        create(connection, value, null);%0A    %7D%0A
+/**%0A     * insert value into the db through the specified connection.%0A     * @param connection the connection%0A     * @param value the value%0A     * @throws SQLException if an error occurs%0A     */%0A    public void create(Connection connection, T value) throws SQLException %7B%0A        create(connection, value, null);%0A    %7D%0A%0A    /**%0A     * insert value into the db through the specified connection.%0A     * Callback keyConsumer with the generated key if one was.%0A     * @param connection the connection%0A     * @param value the value%0A     * @param keyConsumer the key consumer%0A     * @param %3CRH%3E the type of keyConsumer%0A     * @return the keyConsumer%0A     * @throws SQLException%0A     */
 %0A   
@@ -3016,16 +3016,243 @@
     %7D%0A%0A%0A
+    /**%0A     * retrieve the object with the specified key.%0A     * @param connection the connection%0A     * @param key the key%0A     * @return the object or null if not found%0A     * @throws SQLException if an error occurs%0A     */%0A
     publ
@@ -3777,32 +3777,192 @@
        %7D%0A    %7D%0A%0A
+    /**%0A     * update the object.%0A     * @param connection the connection%0A     * @param value the object%0A     * @throws SQLException if an error occurs%0A     */%0A
     public void 
@@ -4339,24 +4339,202 @@
    %7D%0A    %7D%0A%0A
+    /**%0A     * delete the object with the specified key.%0A     * @param connection the connection%0A     * @param key the key%0A     * @throws SQLException if an error occurs%0A     */%0A
     public v

update javadoc