MyBatis 原理分析手書き永続層フレームワーク


1 JDBC操作データベースの問題の分析

ここに画像の説明を挿入します

JDBC API を使用すると、アプリケーションはあらゆる形式の表形式のデータ、特にリレーショナル データベースに格納されているデータにアクセスできます。

ここに画像の説明を挿入します

コード例:

public static void main(String[] args) {
    
     
      Connection connection = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;
  try {
    
    
  // 加载数据库驱动
  Class.forName("com.mysql.jdbc.Driver");
  // 通过驱动管理类获取数据库链接
  connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis? characterEncoding=utf-8", "root", "root");
  // 定义sql语句?表示占位符
  String sql = "select * from user where username = ?";
  // 获取预处理statement
  preparedStatement = connection.prepareStatement(sql);
  // 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值 preparedStatement.setString(1, "tom");
  // 向数据库发出sql执行查询,查询出结果集
  resultSet = preparedStatement.executeQuery();
  // 遍历查询结果集
  while (resultSet.next()) {
    
    
    int id = resultSet.getInt("id");
    String username = resultSet.getString("username");
  // 封装User
    user.setId(id);
    user.setUsername(username);
  }
    System.out.println(user);
  }
  } catch (Exception e) {
    
    
    e.printStackTrace();
  } finally {
    
    
      // 释放资源
        if (resultSet != null) {
    
    
            try {
    
    
                resultSet.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
        if (preparedStatement != null) {
    
    
            try {
    
    
                preparedStatement.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
        if (connection != null) {
    
    
            try {
    
    
                connection.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
  }
}

2 JDBC の問題分析と解決策のアイデア

コードを切り取って 1 つずつ分析します。

(1) ドライバーをロードし、リンクを取得します。

ここに画像の説明を挿入します

  • 問題 1:データベース構成情報にハードコーディングの問題があります。

    最適化のアイデア: 構成ファイルを使用してください!

  • 問題 2:データベース接続の作成と解放が頻繁に行われる。

    最適化のアイデア: データ接続プールを使用してください!

(2) SQL を定義し、パラメータを設定し、クエリを実行します。

ここに画像の説明を挿入します

  • 問題 3: SQL ステートメント、パラメータの設定、および結果セット パラメータの取得にハードコーディングの問題があります。

    最適化のアイデア: 構成ファイルを使用してください!

(2) クエリ結果セットをスキャンします。

ここに画像の説明を挿入します

  • 問題 4:返された結果セットを手動でカプセル化するのは面倒です

    最適化のアイデア: Java のリフレクションとイントロスペクションを使用してください。

JDBC のさまざまな側面の欠点を考慮して、対応する最適化のアイデアを整理し、統一した方法でまとめました。

問題があります 最適化のアイデア
データベース構成情報にハードコーディングの問題があります 設定ファイルを使用する
データベース接続を頻繁に作成および解放する場合の問題 データ接続プールを使用する
SQL ステートメント、パラメータの設定、および結果セット パラメータの取得にはハードコーディングの問題があります。 設定ファイルを使用する
返された結果セットを手動でカプセル化するのはより面倒です Java のリフレクションとイントロスペクションの使用

3 カスタム永続層フレームワーク_アイデア分析

JDBC は個人的な操作です。すべてを自分で行うため、非効率的でリスクが高くなります。ドライバーを自分でロードし、接続を自分で構築し、...

永続層フレームワークは、明確な分業と効率的な実行による、複数の種類の作業のコラボレーションのようなものです。登録ドライバーの解析と接続の確立を担当するもの、特にデータ接続プールの管理を担当するもの、およびSQL ステートメントの実行、およびパラメータの前処理に特化したものもあります。特別にアセンブルされた結果セット...

最適化のアイデア: フレームワークの役割は、開発の詳細や冗長なコードを削減して、ビジネス アプリケーションの開発にさらに集中できるようにすることです。

3.1 JDBC の使用と永続層フレームワークの使用の違い

ここに画像の説明を挿入します

このような永続層フレームワークが非常に快適であることがわかりましたか? 必要なことは次の 2 つだけです。

  • データソースの設定(アドレス/データ名/ユーザー名/パスワード)
  • SQLの記述とパラメータの準備(SQL文・パラメータの型・戻り値の型)

フレームワークは、独自のエンジニアリング設計を考えることに加えて、実際のプロジェクト側での使用シナリオも考慮する必要があります。

  • ユーザー側(実際のプロジェクト)
  • 永続化フレームワーク自体

上記の 2 つのステップは、アーキテクチャ図「手書き永続層フレームワークの基本的なアイデア」を通じて明確にできます。

ここに画像の説明を挿入します

3.2 コアインターフェイス/クラスの主な説明

F 役割の位置付け クラス名の定義
構成ファイルの読み取りを担当します リソース支援クラス リソース
データベース接続情報の保存を担当します データベースリソースクラス 構成
SQL マッピング定義と結果セット マッピング定義の保存を担当します。 SQL および結果セットのリソース クラス マップされたステートメント
構成ファイルの解析とセッション ファクトリの作成を担当します SqlSessionFactory セッションファクトリービルダー SqlSessionFactoryBuilder
セッションの作成を担当します SqlSession セッションファクトリー Sqlセッションファクトリー
実行者の割り当て 実行者 セッション SQLセッション
SQLの実行を担当します(指定されたリソースのMapped Statementと連携) アクチュエーター 執行者

通常、プロジェクトは 1 つのデータベース環境セットにのみ対応し、通常は 1 つの SqlSessionFactory インスタンス オブジェクトに対応します。シングルトン モードを使用して SqlSessionFactory インスタンスを 1 つだけ作成します。

複数のデータベース環境を構成する必要がある場合は、何らかの拡張を行う必要があります。たとえば、Mybatis は、環境やその他の構成を通じて、複数のテスト/運用データベース環境間の切り替えをサポートできます。

3.3 プロジェクトのユーザー側

(1) カスタム永続層フレームワークのjarパッケージの導入に加え、フレームワークAPIを呼び出す

(2) 構成情報の 2 つの部分を指定します。 1.sqlMapConfig.xml: データベース構成情報 (アドレス/データ名/ユーザー名/パスワード)、および Mapper.xml のフル パス

                                            2.mapper.xml : SQL配置信息,存放SQL语句、参数类型、返回值类型相关信息

3.4 フレームワーク自体をカスタマイズする

1. 設定ファイルをロードします。設定ファイルのパスに従って、設定ファイルをバイト入力ストリームにロードし、メモリに保存します。

ここに画像の説明を挿入します

2. 2 つの javaBeans (コンテナ オブジェクト) を作成します。構成ファイルから解析されたコンテンツを保存します。

ここに画像の説明を挿入します

3. 構成ファイルを解析し (dom4j を使用)、SqlSession セッション オブジェクトを作成します。

ここに画像の説明を挿入します

4. SqlSessionFactory インターフェイスを作成し、クラス DefaultSqlSessionFactory を実装します。

ここに画像の説明を挿入します

5. SqlSession インターフェイスを作成し、クラス DefaultSqlSession を実装します。

ここに画像の説明を挿入します

6. Executor インターフェイスを作成し、SimpleExecutor クラスを実装します。

ここに画像の説明を挿入します

基本的なプロセスはすでに明確にしましたので、実際のコーディングに役立つようにクラス図を改良してみましょう。

ここに画像の説明を挿入します

3.5 最終的な手書き永続層フレームワーク構造のリファレンス

ここに画像の説明を挿入します

4 カスタム永続層の Framework_encoding

  <properties>
        <!-- Encoding -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>11</java.version>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

  <!--引入ipersistent的依赖-->

コンシューマ プロジェクトで構成プロファイルを作成する

sqlMapConfig.xmlを作成する

<configuration> 
    <!--1.配置数据库配置信息-->
    <dataSource>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql:///zdy_mybatis?useSSL=false&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </dataSource>


    <!--2.引入映射配置文件-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"></mapper>
    </mappers>

</configuration> 

マッパー.xml

<mapper namespace="User">
    
    <!--根据条件查询单个-->
    <select id="selectOne" resultType="com.oldlu.pojo.User" parameterType="com.oldlu.pojo.User">
        select * from user where id = #{id} and username = #{username}
    </select>

  
  <!--查询所有-->
    <select id="selectList" resultType="com.oldlu.pojo.User">
        select * from user
    </select>
</mapper>

ユーザーエンティティ

public class User {
    
    
  //主键标识
  private Integer id;
  //用户名
  private String username;
    
  public Integer getId() {
    
     
        return id;
  }
  public void setId(Integer id) {
    
     
        this.id = id;
  }
  public String getUsername() {
    
     
        return username;
  }
  public void setUsername(String username) {
    
     
        this.username = username;
  }

    @Override
  public String toString() {
    
    
    return "User{" +
    "id=" + id +
    ", username='" + username + '\'' + '}';
  }
}

別の Maven サブプロジェクトを作成し、必要な依存関係座標をインポートします

  <properties>
        <!-- Encoding -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>11</java.version>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- mysql 依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

        <!--junit 依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <!--作用域测试范围-->
            <scope>test</scope>
        </dependency>

        <!--dom4j 依赖-->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>

        <!--xpath 依赖-->
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>


        <!--druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>

        <!-- log日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
       
    </dependencies>

リソース

public class Resources {
    
    

    /**
     * 根据配置文件的路径,加载成字节输入流,存到内存中
     * @param path
     * @return
     */
    public static InputStream getResourceAsSteam(String path){
    
    
        InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
        return resourceAsStream;
    }

構成

/**
 * 存放核心配置文件解析的内容
 */
public class Configuration {
    
    

    // 数据源对象
    private DataSource dataSource;

    // map : key :statementId  value : 封装好的MappedStatement
    private Map<String,MappedStatement> mappedStatementMap = new HashMap<>();

    public DataSource getDataSource() {
    
    
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
    
    
        this.dataSource = dataSource;
    }

    public Map<String, MappedStatement> getMappedStatementMap() {
    
    
        return mappedStatementMap;
    }

    public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) {
    
    
        this.mappedStatementMap = mappedStatementMap;
    }
}

マップされたステートメント

/**
 *  存放解析映射配置文件的内容
 *     <select id="selectOne" resultType="com.oldlu.pojo.User" parameterType="com.oldlu.pojo.User">
 *         select * from user where id = #{id} and username = #{username}
 *     </select>
 */
public class MappedStatement {
    
    

    // 1.唯一标识 (statementId namespace.id)
    private String statementId;
    // 2.返回结果类型
    private String resultType;
    // 3.参数类型
    private String parameterType;
    // 4.要执行的sql语句
    private String sql;

    // 5.mapper代理:sqlcommandType
    private String sqlcommandType;

    public String getSqlcommandType() {
    
    
        return sqlcommandType;
    }

    public void setSqlcommandType(String sqlcommandType) {
    
    
        this.sqlcommandType = sqlcommandType;
    }

    public String getStatementId() {
    
    
        return statementId;
    }

    public void setStatementId(String statementId) {
    
    
        this.statementId = statementId;
    }

    public String getResultType() {
    
    
        return resultType;
    }

    public void setResultType(String resultType) {
    
    
        this.resultType = resultType;
    }

    public String getParameterType() {
    
    
        return parameterType;
    }

    public void setParameterType(String parameterType) {
    
    
        this.parameterType = parameterType;
    }

    public String getSql() {
    
    
        return sql;
    }

    public void setSql(String sql) {
    
    
        this.sql = sql;
    }
}

SqlSessionFactoryBuilder

public class SqlSessionFactoryBuilder {
    
    

    /**
     * 1.解析配置文件,封装Configuration 2.创建SqlSessionFactory工厂对象
     * @return
     */
    public SqlSessionFactory build(InputStream inputStream) throws DocumentException {
    
    
        // 1.解析配置文件,封装Configuration
        XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
        Configuration configuration = xmlConfigBuilder.parse(inputStream);

        SqlSessionFactory defatultSqlSessionFactory = new DefatultSqlSessionFactory(configuration);
        return  defatultSqlSessionFactory;
    }

}

XMLConfigerBuilder

public class XMLConfigBuilder {
    
    
    
    private Configuration configuration;

    public XMLConfigBuilder() {
    
    
        configuration = new Configuration();
    }

    /**
     * 使用dom4j解析xml文件,封装configuration对象
     * @param inputStream
     * @return
     */
    public Configuration parse(InputStream inputStream) throws DocumentException {
    
    

        Document document = new SAXReader().read(inputStream);
        Element rootElement = document.getRootElement();

        // 解析核心配置文件中数据源部分
        List<Element> list = rootElement.selectNodes("//property");
        //  <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

        Properties properties = new Properties();
        for (Element element : list) {
    
    
            String name = element.attributeValue("name");
            String value = element.attributeValue("value");
            properties.setProperty(name,value);
        }

        // 创建数据源对象(连接池)
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(properties.getProperty("driverClassName"));
        druidDataSource.setUrl(properties.getProperty("url"));
        druidDataSource.setUsername(properties.getProperty("username"));
        druidDataSource.setPassword(properties.getProperty("password"));

        // 创建好的数据源对象封装进configuration中、
        configuration.setDataSource(druidDataSource);


        // 解析映射配置文件
        // 1.获取映射配置文件的路径  2.解析  3.封装好mappedStatement
        List<Element> mapperList = rootElement.selectNodes("//mapper");
        for (Element element : mapperList) {
    
    
            String mapperPath = element.attributeValue("resource");
            InputStream resourceAsSteam = Resources.getResourceAsSteam(mapperPath);
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
            xmlMapperBuilder.parse(resourceAsSteam);
        }

        return configuration;
    }
}

XMLマッパービルダー

public class XMLMapperBuilder {
    
    
 private Configuration configuration;

    public XMLMapperBuilder(Configuration configuration) {
    
    
        this.configuration = configuration;
    }

    public void parse(InputStream inputStream) throws DocumentException, ClassNotFoundException {
    
    
        Document document = new SAXReader().read(inputStream);
        Element rootElement = document.getRootElement();

        String namespace = rootElement.attributeValue("namespace");
        List<Element> select = rootElement.selectNodes("select");
        for (Element element : select) {
    
     //id的值
            String id = element.attributeValue("id");
            String paramterType = element.attributeValue("paramterType");
            String resultType = element.attributeValue("resultType"); //输入参数class
            Class<?> paramterTypeClass = getClassType(paramterType);
            //返回结果class
            Class<?> resultTypeClass = getClassType(resultType);
            //statementId
            String key = namespace + "." + id;
            //sql语句
            String textTrim = element.getTextTrim();
            //封装 mappedStatement
            MappedStatement mappedStatement = new MappedStatement();
            mappedStatement.setId(id);
            mappedStatement.setParamterType(paramterTypeClass);
            mappedStatement.setResultType(resultTypeClass);
            mappedStatement.setSql(textTrim);
            //填充 configuration
            configuration.getMappedStatementMap().put(key, mappedStatement);

            private Class<?> getClassType (String paramterType) throws ClassNotFoundException {
    
    
                Class<?> aClass = Class.forName(paramterType);
                return aClass;
    }
}

sqlSessionFactory インターフェイスと DefaultSqlSessionFactory 実装クラス

public interface SqlSessionFactory {
    
    

    /**
     * 生产sqlSession :封装着与数据库交互的方法
     * @return
     */
    public SqlSession openSession();

}

public class DefatultSqlSessionFactory implements SqlSessionFactory {
    
    

    private Configuration configuration;

    public DefatultSqlSessionFactory(Configuration configuration) {
    
    
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
    
    

        // 执行器创建出来
        Executor executor = new SimpleExecutor();

        DefaultSqlSession defaultSqlSession = new DefaultSqlSession(configuration,executor);
        return defaultSqlSession;
    }
}

sqlSession インターフェイスと DefaultSqlSession 実装クラス

public interface SqlSession {
    
    

    /**
     * 查询所有的方法 select * from user where username like '%aaa%' and sex = ''
     * 参数1:唯一标识
     * 参数2:入参
     */
    public <E> List<E> selectList(String statementId,Object parameter) throws Exception;


    /**
     * 查询单个的方法
     */
    public <T> T selectOne(String statementId,Object parameter) throws Exception;
}

public class DefaultSqlSession implements SqlSession {
    
    

    private Configuration configuration;

    private Executor executor;

    public DefaultSqlSession(Configuration configuration, Executor executor) {
    
    
        this.configuration = configuration;
        this.executor = executor;
    }

    @Override                    // user.selectList      1 tom user
    public <E> List<E> selectList(String statementId, Object params) throws Exception {
    
    

        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        // 将查询操作委派给底层的执行器
        List<E> list = executor.query(configuration,mappedStatement,params);

        return list;
    }

    @Override
    public <T> T selectOne(String statementId, Object params) throws Exception {
    
    
        List<Object> list = this.selectList(statementId, params);
        if(list.size() == 1){
    
    
            return (T) list.get(0);
        }else if(list.size() > 1){
    
    
            throw new RuntimeException("返回结果过多");
        }else {
    
    
            return null;
        }
    }
}   

執行者

public interface Executor {
    
    

    <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object params) throws Exception;
}

シンプルエグゼキュータ

public class SimpleExecutor implements Executor {
    
    

    /**
     * 执行JDBC操作
     * @param configuration
     * @param mappedStatement
     * @param params
     * @param <E>
     * @return
     */
    @Override                                                                               // user product
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object params) throws Exception {
    
    

        // 1. 加载驱动,获取连接
        Connection connection = configuration.getDataSource().getConnection();

        // 2. 获取prepareStatement预编译对象
        /*
             select * from user where id = #{id} and username = #{username}
             select * from user where id = ? and username = ?
             占位符替换 :#{}替换成? 注意:#{id}里面的id名称要保存
         */
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSQL(sql);
        String finaLSql = boundSql.getFinaLSql();
        PreparedStatement preparedStatement = connection.prepareStatement(finaLSql);

        // 3.设置参数
         // 问题1: Object param(类型不确定 user/product/map/String)
         // 问题2:该把对象中的哪一个属性赋值给哪一个占位符呢?
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if(parameterMappings.size() > 0){
    
    
        // com.oldlu.pojo.User
        String parameterType = mappedStatement.getParameterType();
        Class<?> parameterTypeClass = Class.forName(parameterType);

        for (int i = 0; i < parameterMappings.size(); i++) {
    
    
            ParameterMapping parameterMapping = parameterMappings.get(i);
            // id
            String content = parameterMapping.getContent();
            // 反射
            Field declaredField = parameterTypeClass.getDeclaredField(content);
            // 暴力访问
            declaredField.setAccessible(true);
            Object value = declaredField.get(params);
            preparedStatement.setObject(i+1 ,value);
        }
        }

        // 4.执行sql,发起查询
        ResultSet resultSet = preparedStatement.executeQuery();
        String resultType = mappedStatement.getResultType();
        Class<?> resultTypeClass = Class.forName(resultType);

        ArrayList<E> list = new ArrayList<>();
        // 5.遍历封装
        while (resultSet.next()){
    
    
            // 元数据信息中包含了字段名 字段的值
            ResultSetMetaData metaData = resultSet.getMetaData();
            Object obj = resultTypeClass.newInstance();
            for (int i = 1; i <= metaData.getColumnCount() ; i++) {
    
    

                // id  username
                String columnName = metaData.getColumnName(i);
                Object value = resultSet.getObject(columnName);
                // 属性描述器
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,resultTypeClass);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(obj,value);
            }
            list.add((E) obj);
        }
        return list;
    }

    /**
     *  1.将sql中#{}替换成? 2.将#{}里面的值保存
     * @param sql
     * @return
     */
    private BoundSql getBoundSQL(String sql) {
    
    
        // 标记处理器:配合标记解析器完成标记的解析工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();

        // 标记解析器
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        String finalSql = genericTokenParser.parse(sql);

        // #{}里面的值的集合
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

        BoundSql boundSql = new BoundSql(finalSql, parameterMappings);

        return boundSql;
    }
}

バインドされたSQL

public class BoundSql {
    
    
   //解析过后的sql语句
    private String sqlText;
    //解析出来的参数
    private List<ParameterMapping> parameterMappingList = new ArrayList<ParameterMapping>();

    public BoundSql(String sqlText, List<ParameterMapping>
            parameterMappingList) {
    
    
        this.sqlText = sqlText;
        this.parameterMappingList = parameterMappingList;
    }

    public String getSqlText() {
    
    
        return sqlText;
    }

    public void setSqlText(String sqlText) {
    
    
        this.sqlText = sqlText;
    }

    public List<ParameterMapping> getParameterMappingList() {
    
    
        return parameterMappingList;
    }

    public void setParameterMappingList(List<ParameterMapping> parameterMappingList) {
    
    
        this.parameterMappingList = parameterMappingList;
    }
}

5 カスタマイズされた永続層フレームワークの最適化

上記のカスタム フレームワークを通じて、JDBC オペレーティング データベースによって引き起こされるいくつかの問題 (データベース接続の頻繁な作成と解放、ハード コーディング、返された結果セットの手動カプセル化など) を解決しましたが、現在はカスタム フレームワークの分析を続けています。コードを完成させたところですが、何か問題はありますか?

質問は次のとおりです。

  • daoの実装クラスに重複したコードがあり、操作プロセステンプレート全体が繰り返されます(sqlsessionの作成、sqlsessionメソッドの呼び出し、sqlsessionのクローズ)
  • daoの実装クラスにはハードコーディングがあり、sqlsessionメソッドを呼び出す際にパラメータ文のidがハードコーディングされています。

解決策: プロキシ パターンを使用して、インターフェイスのプロキシ オブジェクトを作成します。

  @Test
    public void test2() throws Exception {
    
    
        InputStream resourceAsSteam = Resources.getResourceAsSteam(path: "sqlMapConfig.xml")
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsSteam);
        SqlSession sqlSession = build.openSession();
        User user = new User();
        user.setld(l);
        user.setUsername("tom");
        //代理对象
        UserMapper userMapper = sqlSession.getMappper(UserMapper.class);
        User userl = userMapper.selectOne(user);
        System・out.println(userl);
    }

sqlSessionにメソッドを追加

public interface SqlSession {
   public <T> T getMappper(Class<?> mapperClass);

実装クラス

package com.oldlu.sqlSession;

import com.oldlu.executor.Executor;
import com.oldlu.pojo.Configuration;
import com.oldlu.pojo.MappedStatement;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.List;

public class DefaultSqlSession implements SqlSession {
    
    

    private Configuration configuration;
    private Executor executor;

    public DefaultSqlSession(Configuration configuration, Executor executor) {
    
    
        this.configuration = configuration;
        this.executor = executor;
    }

    @Override
    public <E> List<E> selectList(String statementId, Object param) throws Exception {
    
    

        // 要传递什么参数呢?
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        List<E> list = executor.query(configuration,mappedStatement,param);
        return list;
    }

    @Override
    public <T> T selectOne(String statementId, Object param) throws Exception {
    
    
        // 调用selectList方法
        List<Object> list = selectList(statementId, param);
        if(list.size() == 1){
    
    
            return (T) list.get(0);
        }else if(list.size() > 1){
    
    
            throw new RuntimeException("返回结果过多...");
        }
        return null;
    }

    /**
     * 生成代理对象
     * @param mapperClass
     * @param <T>
     * @return
     */
    @Override
    public <T> T getMapper(Class<?> mapperClass) {
    
    

        // 使用JDK动态代理生成代理对象
        Object proxyInstance = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{
    
    mapperClass}, new InvocationHandler() {
    
    

            // 参数1:Object o:代理对象的引用,很少用
            // 参数2:Method method :当前被调用的方法对象
            // 参数3:Object[] objects:被调用的方法的参数
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
    
    

                // 具体的逻辑:执行底层的JDBC
                // 思路:通过调用sqlSession的方法来完成执行
                // 问题1:如何获取statementId 根据method获取
                Class<?> declaringClass = method.getDeclaringClass();
                // 类全路径= namespace的值
                String className = declaringClass.getName();
                String methodName = method.getName();
                String statementId = className + "." + methodName;
                MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);

                // 问题2:该调用增删改查什么方法呢? 优化:sqlCommandType
                String sqlCommandType = mappedStatement.getSqlCommandType();
                switch (sqlCommandType){
    
    
                    case "select":
                        //查询操作
                        //问题3:调selectOne还是调selectAll呢?
                        Class<?> returnType = method.getReturnType();
                        boolean assignableFrom = Collection.class.isAssignableFrom(returnType);
                        if(assignableFrom){
    
    
                            if(mappedStatement.getParameterType() !=null) {
    
    
                              return   selectList(statementId, objects[0]);
                            }
                            return selectList(statementId, null);
                        }
                        return selectOne(statementId,objects[0]);

                    case "update":
                        // 更新操作
                        break;
                    case "insert":
                        // 更新操作
                        break;
                    case "delete":
                        // 更新操作
                        break;
                }


                return null;
            }
        });


        return (T) proxyInstance;
    }
}

おすすめ

転載: blog.csdn.net/ZGL_cyy/article/details/132775193