ORM フレームワークの進化

1. ORM フレームワークの開発経緯

画像.png

1.JDBCの操作

1.1 JDBC操作の特徴

最初的时候我们肯定是直接通过jdbc来直接操作数据库的,本地数据库我们有一张t_user表,那么我们的操作流程是
复制代码
// 注册 JDBC 驱动
Class.forName("com.mysql.cj.jdbc.Driver");

// 打开连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8&serverTimezone=UTC", "root", "123456");

// 执行查询
stmt = conn.createStatement();
String sql = "SELECT id,user_name,real_name,password,age,d_id from t_user where id = 1";
ResultSet rs = stmt.executeQuery(sql);

// 获取结果集
while (rs.next()) {
    Integer id = rs.getInt("id");
    String userName = rs.getString("user_name");
    String realName = rs.getString("real_name");
    String password = rs.getString("password");
    Integer did = rs.getInt("d_id");
    user.setId(id);
    user.setUserName(userName);
    user.setRealName(realName);
    user.setPassword(password);
    user.setDId(did);

    System.out.println(user);
}
复制代码

具体的な操作手順は、最初に pom.xml で MySQL ドライバーの依存関係を導入することです。MySQL データベースのバージョンに注意してください。

  1. Class.forName 登録ドライバー
  2. 接続オブジェクトを取得する
  3. ステートメント オブジェクトを作成する
  4. execute() メソッドは SQL ステートメントを実行し、ResultSet 結果セットを取得します。
  5. ResultSet 結果セットを介して POJO プロパティに値を割り当てる
  6. 最後に関連リソースを閉じる
这种实现方式首先给我们的感觉就是操作步骤比较繁琐,在复杂的业务场景中会更麻烦。尤其是我们需要自己来维护管理资源的连接,如果忘记了,就很可能造成数据库服务连接耗尽。同时我们还能看到具体业务的SQL语句直接在代码中写死耦合性增强。每个连接都会经历这几个步骤,重复代码很多,总结上面的操作的特点:
复制代码
  1. コードの重複
  2. 資源管理
  3. 結果セットの処理
  4. SQL結合

私たちはこれらの問題を自分で解決しようとすることができます

1.2 JDBC 最適化 1.0

针对常规jdbc操作的特点,我们可以先从代码重复和资源管理方面来优化,我们可以创建一个工具类来专门处理这个问题
复制代码
public class DBUtils {

    private static final String JDBC_URL = "jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8&serverTimezone=UTC";
    private static final String JDBC_NAME = "root";
    private static final String JDBC_PASSWORD = "123456";

    private static  Connection conn;

    /**
     * 对外提供获取数据库连接的方法
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception {
        if(conn == null){
            try{
                conn = DriverManager.getConnection(JDBC_URL,JDBC_NAME,JDBC_PASSWORD);
            }catch (Exception e){
                e.printStackTrace();
                throw new Exception();
            }
        }
        return conn;
    }

    /**
     * 关闭资源
     * @param conn
     */
    public static void close(Connection conn ){
        close(conn,null);
    }

    public static void close(Connection conn, Statement sts ){
        close(conn,sts,null);
    }

    public static void close(Connection conn, Statement sts , ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }

        if(sts != null){
            try {
                sts.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }

        if(conn != null){
            try {
                conn.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

复制代码

対応する jdbc 操作コードは、次のように簡略化できます。

    /**
     *
     * 通过JDBC查询用户信息
     */
    public void queryUser(){
        Connection conn = null;
        Statement stmt = null;
        User user = new User();
        ResultSet rs = null;
        try {
            // 注册 JDBC 驱动
            // Class.forName("com.mysql.cj.jdbc.Driver");

            // 打开连接
            conn = DBUtils.getConnection();
            // 执行查询
            stmt = conn.createStatement();
            String sql = "SELECT id,user_name,real_name,password,age,d_id from t_user where id = 1";
            rs = stmt.executeQuery(sql);

            // 获取结果集
            while (rs.next()) {
                Integer id = rs.getInt("id");
                String userName = rs.getString("user_name");
                String realName = rs.getString("real_name");
                String password = rs.getString("password");
                Integer did = rs.getInt("d_id");
                user.setId(id);
                user.setUserName(userName);
                user.setRealName(realName);
                user.setPassword(password);
                user.setDId(did);
                System.out.println(user);
            }

        } catch (SQLException se) {
            se.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DBUtils.close(conn,stmt,rs);
        }
    }

   /**
     * 通过JDBC实现添加用户信息的操作
     */
    public void addUser(){
        Connection conn = null;
        Statement stmt = null;
        try {
            // 打开连接
            conn = DBUtils.getConnection();
            // 执行查询
            stmt = conn.createStatement();
            String sql = "INSERT INTO T_USER(user_name,real_name,password,age,d_id)values('wangwu','王五','111',22,1001)";
            int i = stmt.executeUpdate(sql);
            System.out.println("影响的行数:" + i);
        } catch (SQLException se) {
            se.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DBUtils.close(conn,stmt);
        }
    }
复制代码

ただし、全体的な操作手順はまだ複雑に見えるため、さらに最適化することができます

1.3 JDBC 最適化 2.0

DML 操作の方法を最適化し、最初に SQL カップリングの問題を解決し、DBUtils で DML 操作の方法をカプセル化できます。

    /**
     * 执行数据库的DML操作
     * @return
     */
    public static Integer update(String sql,Object ... paramter) throws Exception{
        conn = getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        if(paramter != null && paramter.length > 0){
            for (int i = 0; i < paramter.length; i++) {
                ps.setObject(i+1,paramter[i]);
            }
        }
        int i = ps.executeUpdate();
        close(conn,ps);
        return i;
    }
复制代码

次に、DML 操作で、次の手順に簡略化できます。

    /**
     * 通过JDBC实现添加用户信息的操作
     */
    public void addUser(){
        String sql = "INSERT INTO T_USER(user_name,real_name,password,age,d_id)values(?,?,?,?,?)";
        try {
            DBUtils.update(sql,"wangwu","王五","111",22,1001);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
复制代码

明らかに、この方法は最初の使用よりもはるかに単純になりますが、クエリ処理中の ResultSet 結果セット処理の問題はまだ解決されていないため、引き続き最適化する必要があります。

1.4 JDBC 最適化 3.0

针对ResultSet的优化我们需要从反射和元数据两方面入手,具体如下
复制代码
    /**
     * 查询方法的简易封装
     * @param sql
     * @param clazz
     * @param parameter
     * @param <T>
     * @return
     * @throws Exception
     */
    public static <T> List<T> query(String sql, Class clazz, Object ... parameter) throws  Exception{
        conn = getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        if(parameter != null && parameter.length > 0){
            for (int i = 0; i < parameter.length; i++) {
                ps.setObject(i+1,parameter[i]);
            }
        }
        ResultSet rs = ps.executeQuery();
        // 获取对应的表结构的元数据
        ResultSetMetaData metaData = ps.getMetaData();
        List<T> list = new ArrayList<>();
        while(rs.next()){
            // 根据 字段名称获取对应的值 然后将数据要封装到对应的对象中
            int columnCount = metaData.getColumnCount();
            Object o = clazz.newInstance();
            for (int i = 1; i < columnCount+1; i++) {
                // 根据每列的名称获取对应的值
                String columnName = metaData.getColumnName(i);
                Object columnValue = rs.getObject(columnName);
                setFieldValueForColumn(o,columnName,columnValue);
            }
            list.add((T) o);
        }
        return list;
    }

    /**
     * 根据字段名称设置 对象的属性
     * @param o
     * @param columnName
     */
    private static void setFieldValueForColumn(Object o, String columnName,Object columnValue) {
        Class<?> clazz = o.getClass();
        try {
            // 根据字段获取属性
            Field field = clazz.getDeclaredField(columnName);
            // 私有属性放开权限
            field.setAccessible(true);
            field.set(o,columnValue);
            field.setAccessible(false);
        }catch (Exception e){
            // 说明不存在 那就将 _ 转换为 驼峰命名法
            if(columnName.contains("_")){
                Pattern linePattern = Pattern.compile("_(\\w)");
                columnName = columnName.toLowerCase();
                Matcher matcher = linePattern.matcher(columnName);
                StringBuffer sb = new StringBuffer();
                while (matcher.find()) {
                    matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
                }
                matcher.appendTail(sb);
                // 再次调用复制操作
                setFieldValueForColumn(o,sb.toString(),columnValue);
            }
        }
    }
复制代码

上記のメソッドをカプセル化した後、クエリ操作は次のように簡略化できます。

    /**
     *
     * 通过JDBC查询用户信息
     */
    public void queryUser(){
        try {
            String sql = "SELECT id,user_name,real_name,password,age,d_id from t_user where id = ?";
            List<User> list = DBUtils.query(sql, User.class,2);
            System.out.println(list);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
复制代码

このように、データベース内のデータを操作するときは、中核となる SQL 操作に集中するだけで済みます。もちろん、上記の設計はまだ比較的ラフですが、現時点では、Apache の下の DbUtils が適切な選択です。

一緒に創造し、成長するために一緒に働きましょう!「ナゲッツデイリー新プラン・8月アップデートチャレンジ」に参加して24日目、クリックでイベントの詳細が表示されます

おすすめ

転載: juejin.im/post/7134306563874881549