您现在的位置是:首页 > 正文

MyBatis原理分析手写持久层框架

2024-04-01 04:13:22阅读 3


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:数据库配置信息存在硬编码问题。

    优化思路:使用配置文件!

  • 存在问题2:频繁创建、释放数据库连接问题。

    优化思路:使用数据连接池!

(2)定义sql、设置参数、执行查询:

在这里插入图片描述

  • 存在问题3:SQL语句、设置参数、获取结果集参数均存在硬编码问题 。

    优化思路:使用配置文件!

(2)遍历查询结果集:

在这里插入图片描述

  • 存在问题4:手动封装返回结果集,较为繁琐

    优化思路:使用Java反射、内省!

针对JDBC各个环节中存在的不足,现在,我们整理出对应的优化思路,统一汇总:

存在问题 优化思路
数据库配置信息存在硬编码问题 使用配置文件
频繁创建、释放数据库连接问题 使用数据连接池
SQL语句、设置参数、获取结果集参数均存在硬编码问题 使用配置文件
手动封装返回结果集,较为繁琐 使用Java反射、内省

3 自定义持久层框架_思路分析

JDBC是个人作战,凡事亲力亲为,低效而高险,自己加载驱动,自己建连接,自己 …

而持久层框架好比是多工种协作,分工明确,执行高效,有专门负责解析注册驱动建立连接的,有专门管理数据连接池的,有专门执行sql语句的,有专门做预处理参数的,有专门装配结果集的 …

优化思路: 框架的作用,就是为了帮助我们减去繁重开发细节与冗余代码,使我们能更加专注于业务应用开发。

3.1 使用JDBC和使用持久层框架区别

在这里插入图片描述

是不是发现,拥有这么一套持久层框架是如此舒适,我们仅仅需要干两件事:

  • 配置数据源(地址/数据名/用户名/密码)
  • 编写SQL与参数准备(SQL语句/参数类型/返回值类型)

框架,除了思考本身的工程设计,还需要考虑到实际项目端的使用场景,干系方涉及两端:

  • 使用端(实际项目)
  • 持久层框架本身

以上两步,我们通过一张架构图《 手写持久层框架基本思路 》来梳理清楚:

在这里插入图片描述

3.2 核心接口/类重点说明

分工协作 角色定位 类名定义
负责读取配置文件 资源辅助类 Resources
负责存储数据库连接信息 数据库资源类 Configuration
负责存储SQL映射定义、存储结果集映射定义 SQL与结果集资源类 MappedStatement
负责解析配置文件,创建会话工厂SqlSessionFactory 会话工厂构建者 SqlSessionFactoryBuilder
负责创建会话SqlSession 会话工厂 SqlSessionFactory
指派执行器Executor 会话 SqlSession
负责执行SQL (配合指定资源Mapped Statement) 执行器 Executor

正常来说项目只对应一套数据库环境,一般对应一个SqlSessionFactory实例对象,我们使用单例模式只创建一个SqlSessionFactory实例。

如果需要配置多套数据库环境,那需要做一些拓展,例如Mybatis中通过environments等配置就可以支持多套测试/生产数据库环境进行切换。

3.3 项目使用端

(1)调用框架API,除了引入自定义持久层框架的jar包

(2)提供两部分配置信息:1.sqlMapConfig.xml : 数据库配置信息(地址/数据名/用户名/密码),以及mapper.xml的全路径

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

3.4 自定义框架本身

1、加载配置文件:根据配置文件的路径,加载配置文件成字节输入流,存储在内存中。

在这里插入图片描述

2、 创建两个javaBean(容器对象):存放配置文件解析出来的内容

在这里插入图片描述

3、解析配置文件(使用dom4j) ,并创建SqlSession会话对象

在这里插入图片描述

4、创建SqlSessionFactory接口以及实现类DefaultSqlSessionFactory

在这里插入图片描述

5、创建SqlSession接口以及实现类DefaultSqlSession

在这里插入图片描述

6、创建Executor接口以及实现类SimpleExecutor

在这里插入图片描述

基本过程我们已经清晰,我们再细化一下类图,更好的助于我们实际编码:

在这里插入图片描述

3.5 最终手写的持久层框架结构参考

在这里插入图片描述

4 自定义持久层框架_编码

  <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> 

mapper.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>

User实体

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>

Resources

public class Resources {

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

Configuration

/**
 * 存放核心配置文件解析的内容
 */
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;
    }
}

MappedStatement

/**
 *  存放解析映射配置文件的内容
 *     <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;
    }
}

XMLMapperBuilder

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 接口及D efaultSqlSessionFactory 实现类

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;
        }
    }
}   

Executor

public interface Executor {

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

SimpleExecutor

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;
    }
}

BoundSql

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的方法时,参数statement的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;
    }
}

网站文章

  • 软件测试期末考试复习--性能测试大题详解

    软件测试期末考试复习--性能测试大题详解

    我有好多题,刷刷刷、哈哈哈。。。。。。。 第一题 【说明】 性能测试在系统质量保证中起重要作用。某项目组对一个电子政务平台系统执行了负载压力性能测试,重点评估其效率质量特性中的时间特性和资源利用性两个质量子特性。性能需求可以概括为:业务成功率达到 100%;响应时间在 8 秒之内;内存页面交换速率低于80pagein/s;服务器资源利用合理。测试环境逻辑部署图如下图。 【问...

    2024-04-01 04:13:14
  • 【必看】机器学习人才必备 —— TensorFlow 开发者证书

    【必看】机器学习人才必备 —— TensorFlow 开发者证书

    在当今的 AI 世界中,越来越多的公司正在寻求与聘请机器学习方面的人才,与此同时,越来越多的学生和开发者也正在寻求获取和展示 ML 知识的渠道。 除了在线课程和学习资源之外,我们希望帮助开发者展示自身...

    2024-04-01 04:13:07
  • redis 性能优化管理

    内存碎片是由操作系统低效的分配/回收物理内存导致的(不连续的物理内存分配)。当达到设置的最大阀值时,需选择一种key的回收策略,默认情况下回收策略是禁止删除的。使用LRU算法从已设置过期时间的数据集合...

    2024-04-01 04:12:26
  • oracle savepoint

    Oracle之savepoint什么是savepoint?Use the SAVEPOINT statement to identify a point in a transaction to which you can later roll back.例如: SQL> SELECT * FROM SCOTT.DEPT  ;DEPTNO DNAME

    2024-04-01 04:11:35
  • hive中一些基础的函数

    ###日期相关函数 1.regexp_replace(order_time, ‘-’, ‘’) 将字符串中的 - 替换为空格` 2.from_unixtime(unix_timestamp(),‘yy...

    2024-04-01 04:11:27
  • win11右键默认显示更多选项【转载】

    win11右键默认显示更多选项【转载】

    【代码】win11右键默认显示更多选项【转载】

    2024-04-01 04:11:20
  • 爱了爱了!美团大牛手码MySQL知识体系全解全网首发,性能优化+架构设计+面试全都有!

    爱了爱了!美团大牛手码MySQL知识体系全解全网首发,性能优化+架构设计+面试全都有!

    MySQL是什么?关于Mysql想必大家都知道它是最流行的关系型数据库管理系统之一,现在包括facebook. google、 Adobe、 alcatel Lucent和zappos在内的世界上许多...

    2024-04-01 04:10:41
  • nginx转发学习记录

    nginx转发学习记录

    一、转发页面请求,获取对应的参数目标结果:原地址:https://cx.shouji.360.cn/phonearea.php?number=13590431825转发后访问地址:http://119...

    2024-04-01 04:10:30
  • 7-13 出栈序列的合法性(25 分)

    给定一个最大容量为 M 的堆栈,将 N 个数字按 1, 2, 3, ..., N 的顺序入栈,允许按任何顺序出栈,则哪些数字序列是不可能得到的?例如给定 M=5、N=7,则我们有可能得到{ 1, 2, 3, 4, 5, 6, 7 },但不可能得到{ 3, 2, 1, 7, 5, 6, 4 }。输入格式:输入第一行给出 3 个不超过 1000 的正整数:M(堆栈最大容量)、N(入栈元素...

    2024-04-01 04:10:27
  • 数组侦听(vue.js笔记)

    数组的侦听

    2024-04-01 04:09:46