万能 CURD 接口

在实际的使用时,经常遇到这么一个问题,我要从某种数据库中查询出数据,但是不告诉你字段/属性。
乍一看,这个怎么可能,不告诉我字段/属性,要把数据查出来,后来在github上看见一段代码,读了这段代码后恍然大悟。还是 too young to simple

原理是通过JDBC的getMetaData()方法来获取表结构,里面用到了反射 (进行了处理,高并发下还是不建议使用)
比较重要的一点是查询的时候数据库有数据字典,可以根据数据字典获取所有字段/属性

实测,MySQL 可用
代码如下


import java.sql.Connection;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 通用接口<br>
 *
 * @author weikeqin.cn@gmail.com
 * @version V1.0
 * @date 2017-07-11 15:12
 */
public interface Executor {

    /**
     * 查询接口
     *
     * @param sql    查询语句
     * @param params 占位符对应的参数
     * @param conn   连接
     * @return Iterator<LinkedHashMap<String, Object>>
     * @throws SQLException
     */
    Iterator<Map<String, Object>> query(String sql, Map<Integer, Object> params, Connection conn) throws SQLException;

    /**
     * (单条)增删改接口
     *
     * @param sql    增删改语句
     * @param params 占位符对应的参数
     * @param conn   连接
     * @return 操作的条数
     * @throws SQLException
     */
    int operat(String sql, Map<Integer, Object> params, Connection conn) throws SQLException;

    /**
     * 批量操作<br>
     * 大于等于0是成功处理的
     *
     * @param sql  增删改语句
     * @param list 占位符对应的参数
     * @param conn 连接
     * @return
     * @throws SQLException
     * @author : weikeqin.cn@gmail.com
     * @date : 2017-09-08 14:47:15
     */
    int[] betchOperat(String sql, List<Map<Integer, Object>> list, Connection conn) throws SQLException;

}
import java.sql.*;
import java.util.*;

/**
 * @author weikeqin.cn@gmail.com
 * @version V1.0
 * @date 2017-07-11 15:31
 */
public class ExecutorImpl implements Executor {

    /**
     * @param sql  查询语句
     * @param params 占位符 参数
     * @param conn   连接
     * @return
     */
    @Override
    public Iterator<Map<String, Object>> query(String sql, Map<Integer, Object> params,
                                               Connection conn) throws SQLException {

        final PreparedStatement statement = conn.prepareStatement(sql);
        // 设置参数
        setParameters(statement, params);
        // 执行查询并获得结果
        final ResultSet result = statement.executeQuery();
        // 封装返回
        return new Iterator<Map<String, Object>>() {

            boolean hasNext = result.next();
            // 所有字段
            public List<String> columns;
            // 字段个数
            public int columnsCount;

            /**
             *
             *
             * @return
             */
            @Override
            public boolean hasNext() {
                return hasNext;
            }

            /**
             * 获得所有字段<br>
             *     第一次会查询出所有字段,第二 第三次 直接用columns
             *
             * @return
             * @throws SQLException
             */
            private List<String> getColumns() throws SQLException {
                if (columns != null) {
                    return columns;
                }

                ResultSetMetaData metaData = result.getMetaData();
                // 查询出的字段
                int count = metaData.getColumnCount();
                List<String> cols = new ArrayList<>(count);
                for (int i = 1; i <= count; i++) {
                    cols.add(metaData.getColumnName(i));
                }

                columnsCount = cols.size();
                return columns = cols;
            }

            /**
             *
             * @return
             */
            @Override
            public Map<String, Object> next() {
                try {
                    if (hasNext) {
                        //
                        Map<String, Object> map = new LinkedHashMap<>(columnsCount);
                        for (String col : getColumns()) {
                            map.put(col, result.getObject(col));
                        }
                        hasNext = result.next();
                        if (!hasNext) {
                            result.close();
                            statement.close();
                        }
                        return map;
                    } else {
                        throw new NoSuchElementException();
                    }

                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }

            /**
             *
             */
            @Override
            public void remove() {
            }
        };

    }

    /**
     * 增删改操作
     *
     * @param sql 增删改语句
     * @param params 占位符参数
     * @param conn   连接
     * @return
     * @throws SQLException
     */
    @Override
    public int operat(String sql, Map<Integer, Object> params, Connection conn) throws SQLException {
        PreparedStatement statement = conn.prepareStatement(sql);
        setParameters(statement, params);
        int count = statement.executeUpdate();
        statement.close();
        return count;
    }

    /**
     * 大于等于0是成功处理的
     *
     * @param sql
     * @param list
     * @param conn
     * @author : weikeqin.cn@gmail.com
     * @date : 2017-09-08 14:48:50
     */
    @Override
    public int[] betchOperat(String sql, List<Map<Integer, Object>> list, Connection conn)
        throws SQLException {

        conn.setAutoCommit(false);
        PreparedStatement statement = conn.prepareStatement(sql);

        int size = list.size();
        for (int i = 0; i < size; i++) {
            // 一条语句里的多个占位符参数
            Map<Integer, Object> map = list.get(i);
            setParameters(statement, map);
            statement.addBatch();
        }

        int[] array = statement.executeBatch();
        conn.commit();
        statement.close();
        return array;
    }

    /**
     * 赋值 给占位符赋值
     *
     * @param statement
     * @param params
     * @throws SQLException
     * @author weikeqin.cn@gmail.com
     */
    private void setParameters(PreparedStatement statement, Map<Integer, Object> params)
        throws SQLException {

        // 校验
        if (params == null || params.isEmpty()) {
            return;
        }

        for (Map.Entry<Integer, Object> entry : params.entrySet()) {
            statement.setObject(entry.getKey(), entry.getValue());
        }
    } // end method

}