Java-22 深入浅出 MyBatis - 手写ORM框架3 手写SqlSession、Executor 工作 ...

打印 上一主题 下一主题

主题 880|帖子 880|积分 2644

点一下关注吧!!!非常感谢!!持续更新!!!

大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html


目前已经更新到了:



  • MyBatis(正在更新)
框架实现

上节已经实现了部分内容 下面我们继承

SqlSession 相干

SqlSessionFactoryBuilder

  1. public class SqlSessionFactoryBuilder {
  2.     private Configuration configuration;
  3.     public SqlSessionFactoryBuilder() {
  4.         configuration = new Configuration();
  5.     }
  6.     public SqlSessionFactory build(InputStream inputStream) throws DocumentException, PropertyVetoException, ClassNotFoundException {
  7.         XMLConfigerBuilder xmlConfigerBuilder = new XMLConfigerBuilder(configuration);
  8.         Configuration conf = xmlConfigerBuilder.parseConfiguration(inputStream);
  9.         return new DefaultSqlSessionFactory(conf);
  10.     }
  11. }
复制代码
SqlSessionFactory

  1. public interface SqlSessionFactory {
  2.     SqlSession openSession();
  3. }
复制代码
SqlSession

  1. public interface SqlSession {
  2.     <E> List<E> selectList(String statementId, Object ...params) throws Exception;
  3.     <T> T selectOne(String statementId, Object ...params) throws Exception;
  4.     void close() throws Exception;
  5. }
复制代码
DefaultSqlSession

  1. @AllArgsConstructor
  2. public class DefaultSqlSession implements SqlSession {
  3.     private Configuration configuration;
  4.     private Executor simpleExecutor = new SimpleExecutor();
  5.     public DefaultSqlSession(Configuration configuration) {
  6.         this.configuration = configuration;
  7.     }
  8.     @Override
  9.     public <E> List<E> selectList(String statementId, Object... params) throws Exception {
  10.         MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
  11.         // System.out.println("DefaultSqlSession => selectList => + " + "statementId: " + statementId + ", mappedStatement: " + mappedStatement);
  12.         return simpleExecutor.query(configuration, mappedStatement, params);
  13.     }
  14.     @Override
  15.     public <T> T selectOne(String statementId, Object... params) throws Exception {
  16.         List<Object> objects = selectList(statementId, params);
  17.         if (objects.size() == 1) {
  18.             return (T) objects.get(0);
  19.         }
  20.         throw new RuntimeException("DefaultSqlSession selectOne 返回结果不唯一: " + statementId);
  21.     }
  22.     @Override
  23.     public void close() throws Exception {
  24.         simpleExecutor.close();
  25.     }
  26.     @Override
  27.     public <T> T getMapper(Class<?> mapperClass) {
  28.         Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
  29.             @Override
  30.             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  31.                 String methodName = method.getName();
  32.                 if (method.getDeclaringClass() == Object.class) {
  33.                     return method.invoke(this, args);
  34.                 }
  35.                 String className = method.getDeclaringClass().getName();
  36.                 String statementId = className + "." + methodName;
  37.                 Type genericReturnType = method.getGenericReturnType();
  38.                 if(genericReturnType instanceof ParameterizedType){
  39.                     List<Object> objects = selectList(statementId, args);
  40.                     return objects;
  41.                 }
  42.                 return selectOne(statementId,args);
  43.             }
  44.         });
  45.         return (T) proxyInstance;
  46.     }
  47. }
复制代码

类定义与注解

AllArgsConstructor:
用于生成包罗所有字段的构造方法,简化代码。
表示可以用所有字段直接构造一个 DefaultSqlSession 对象。
DefaultSqlSession:
实现了 SqlSession 接口,作为 MyBatis 的核心会话管理类。
包罗设置 (Configuration) 和执行器 (Executor) 的实例。
属性



  • private Configuration configuration:保存设置信息,例如 MappedStatement 映射等,负责管理 SQL 的元数据。
  • private Executor simpleExecutor = new SimpleExecutor();默认执行器,用于执行 SQL 语句并返回结果。初始化为 SimpleExecutor 实例。
工作原理总结

查询流程:


  • 用户通过 Mapper 接口调用方法。
  • 动态署理拦截调用,根据方法署名生成 statementId。
  • 调用 selectList 或 selectOne 执行查询。
  • 返回查询结果。
核心组件:


  • Configuration:管理设置信息。
  • MappedStatement:描述 SQL 语句和其映射信息。
  • Ezecutor:负责执行 SQL 并返回结果。
  • Dynamic Proxy:动态生成 Mapper 接口实现,简化用户调用。
异常处置惩罚:


  • 当查询结果不唯一时,selectOne 方法会抛出异常。
  • 这种约束确保单条查询返回的结果始终是明确的。
构造方法

DefaultSqlSession(Configuration configuration):


  • 担当一个 Configuration 对象并赋值给类中的 configuration 属性。
  • 确保该对象的实例在初始化时具备必要的设置信息。
Executor 相干

Executor

  1. public interface Executor {
  2.     <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception;
  3.     void close() throws Exception;
  4. }
复制代码

DefaultExecutor

  1. public class SimpleExecutor implements Executor {
  2.     private Connection connection;
  3.     @Override
  4.     public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception {
  5.         connection = configuration.getDataSource().getConnection();
  6.         String sql = mappedStatement.getSql();
  7.         BoundSql boundSql = getBoundSql(sql);
  8.         PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
  9.         String parameterType = mappedStatement.getParameterType();
  10.         Class<?> parameterTypeClass = getClassType(parameterType);
  11.         List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
  12.         int n = 0;
  13.         for (ParameterMapping pm : parameterMappingList) {
  14.             String content = pm.getName();
  15.             Field declaredField = parameterTypeClass.getDeclaredField(content);
  16.             declaredField.setAccessible(true);
  17.             Object object = declaredField.get(params[0]);
  18.             preparedStatement.setObject(n + 1, object);
  19.         }
  20.         // 执行sql
  21.         ResultSet resultSet = preparedStatement.executeQuery();
  22.         String resultType = mappedStatement.getResultType();
  23.         Class<?> resultTypeClass = getClassType(resultType);
  24.         List<Object> objects = new ArrayList<>();
  25.         // 封装返回结果集
  26.         while (resultSet.next()) {
  27.             Object o = resultTypeClass.newInstance();
  28.             // 元数据
  29.             ResultSetMetaData metaData = resultSet.getMetaData();
  30.             for (int i = 1; i <= metaData.getColumnCount(); i ++) {
  31.                 // 字段名
  32.                 String columnName = metaData.getColumnName(i);
  33.                 // 字段的值
  34.                 Object value = resultSet.getObject(columnName);
  35.                 // 反射 根据数据库和实体 完成
  36.                 PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
  37.                 Method writeMethod = propertyDescriptor.getWriteMethod();
  38.                 writeMethod.invoke(o, resultTypeClass.getDeclaredField(columnName).getType().cast(value));
  39.             }
  40.             objects.add(o);
  41.         }
  42.         return (List<E>) objects;
  43.     }
  44.     private BoundSql getBoundSql(String sql) {
  45.         // 标记处理类 配置标记解析器来完成对占位符的解析处理工作
  46.         ParameterMappingTokenHandler parameterMappingHandler = new ParameterMappingTokenHandler();
  47.         GenericTokenParser genericTokenParser = new GenericTokenParser(
  48.                 "#{", "}",
  49.                 parameterMappingHandler
  50.         );
  51.         // 解析出来的sql
  52.         String parseSql = genericTokenParser.parse(sql);
  53.         // #{} 里边的参数
  54.         List<ParameterMapping> parameterMapping = parameterMappingHandler.getParameterMappings();
  55.         BoundSql boundSql = new BoundSql(parseSql, parameterMapping);
  56.         System.out.println("SimpleExecutor getBoundSql: " + boundSql.getSqlText());
  57.         return boundSql;
  58.     }
  59.     private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
  60.         if(parameterType != null){
  61.             return Class.forName(parameterType);
  62.         }
  63.         return null;
  64.     }
  65.     @Override
  66.     public void close() throws Exception {
  67.         connection.close();
  68.     }
  69. }
复制代码

类的作用

SimpleExecutor 类的主要作用是:


  • 根据传入的 Configuration 和 MappedStatement 来执行 SQL 查询。
  • 通过 JDBC 操作数据库,并使用反射将查询结果映射为指定范例的 Java 对象。
  • 支持参数绑定和动态 SQL 剖析(如 #{} 占位符的处置惩罚)。
代码逻辑剖析



  • 类成员:Connection connection: 用于管理数据库毗连。
核心方法:query

实现 Executor 接口的查询功能,逻辑分为以下几步:
建立数据库毗连:
  1. connection = configuration.getDataSource().getConnection();
复制代码
获取并剖析 SQL:


  • 通过 MappedStatement 获取 SQL 模板(含占位符 #{})。
  • 调用 getBoundSql 方法,将 #{} 更换为 ? 并获取参数信息。
创建 PreparedStatement 并绑定参数:


  • 根据参数范例信息,通过反射获取参数值。
  • 使用 JDBC 的 PreparedStatement 完成 SQL 的参数设置。
执行查询并处置惩罚结果集:



  • 通过 ResultSet 获取查询结果。
  • 使用反射动态构建结果对象,将结果集中的数据填充到指定的 Java 范例中。
适用场景

该类是 MyBatis 的核心实现之一,适适用来:


  • 实现数据库操作的封装。
  • 提供动态署理支持的 Mapper 接口。
  • 管理 SQL 查询的执行过程。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

刘俊凯

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表