手写Mybatis:第5章-数据源的解析、创建和使用

文章目录

  • 一、目标:数据源的解析、创建和使用
  • 二、设计:数据源的解析、创建和使用
  • 三、实现:数据源的解析、创建和使用
    • 3.1 引入依赖
    • 3.2 工程结构
    • 3.3 数据源解析、创建和使用关系图
    • 3.4 事务管理接口和事务工厂
      • 3.4.1 事务的隔离级别
      • 3.4.2 定义事务接口
      • 3.4.3 事务接口实现类
      • 3.4.4 事务工厂
      • 3.4.5 JDBC事务工厂
    • 3.5 数据源工厂创建
      • 3.5.1 数据源工厂
      • 3.5.2 阿里的druid数据库连接池
    • 3.6 创建配置环境基础类
      • 3.6.1 绑定的SQL对象
      • 3.6.2 参数映射对象
      • 3.6.3 环境配置类
      • 3.6.4 映射器语句类
    • 3.7 类型别名注册器
      • 3.7.1 JDBC枚举类型
      • 3.7.2 类型别名注册机
      • 3.7.3 配置项
    • 3.8 解析数据源配置
      • 3.8.1 构建器基类
      • 3.8.2 XML配置构建器
    • 3.9 SQL执行和结果封装
      • 3.9.1 默认sqlSession实现类
  • 四、测试:数据源的解析、创建和使用
    • 4.1 创建 mybatis 数据库并添加数据库表
    • 4.2 提供 DAO 接口 和 User 实体类
      • 4.2.1 用户持久层
      • 4.2.2 用户类
    • 4.3 配置数据源和配置Mapper
      • 4.3.1 配置文件
      • 4.3.2 用户接口配置文件
    • 4.4 单元测试
    • 4.5 功能验证
  • 五、总结:数据源的解析、创建和使用

一、目标:数据源的解析、创建和使用

💡 解析 XML 中关于 dataSource 数据源信息配置,并建立事务管理和连接池的启动和使用,并将这部分能力在 DefaultSqlSession 执行 SQL 语句时进行调用。

二、设计:数据源的解析、创建和使用

💡 怎么完成对数据源的解析?

  • 建立数据源连接池和 JDBC 事务工厂操作,并以 XML 配置数据源信息为入口,在 XMLConfigBuilder 中添加数据源解析和构建操作。
  • 向配置类 configuration 添加 JDBC 操作环境信息,以便在 DefaultSqlSession 完成对 JDBC 执行 SQL 操作。

在这里插入图片描述

  • parse 中解析 XML DB 链接配置信息,并完成事务工厂和连接池的注册环境到配置类的操作。
  • 调用 selectOne 方法的处理,把 SQL 语句放到 DB 连接池中进行执行,以及完成简单的结果封装。

三、实现:数据源的解析、创建和使用

3.1 引入依赖

pom.xml

<dependencies><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.48</version></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.9</version></dependency>
</dependencies>

3.2 工程结构

mybatis-step-04
|-src|-main|   |-java|       |-com.lino.mybatis|           |-binding|           |   |-MapperMethod.java|           |   |-MapperProxy.java|           |   |-MapperProxyFactory.java|           |   |-MapperRegistry.java|           |-builder|           |   |-xml|           |   |   |-XMLConfigBuilder.java|           |   |-BaseBuilder.java|           |-datasource|           |   |-druid|           |   |   |-DruidDataSourceFacroty.java|           |   |-DataSourceFactory.java|           |-io|           |   |-Resources.java|           |-mapping|           |   |-BoundSql.java|           |   |-Environment.java|           |   |-MappedStatement.java|           |   |-ParameterMapping.java|           |   |-SqlCommandType.java|           |-session|           |   |-defaults|           |   |   |-DefaultSqlSession.java|           |   |   |-DefaultSqlSessionFactory.java|           |   |-Configuration.java|           |   |-SqlSession.java|           |   |-SqlSessionFactory.java|           |   |-SqlSessionFactoryBuilder.java|           |   |-TransactionIsolationLevel.java|           |-transaction|           |   |-jdbc|           |   |   |-JdbcTransaction.java|           |   |   |-JdbcTransactionFactory.java|           |   |-Transaction.java|           |   |-TransactionFactory.java|           |-type|           |   |-JdbcType.java|           |   |-TypeAliasRegistry.java|-test|-java|   |-com.lino.mybatis.test|   |-dao|   |   |-IUserDao.java|   |-po|   |   |-User.java|   |-ApiTest.java|-resources|-mapper|   |-User_Mapper.xml|-mybatis-config-datasource.xml

3.3 数据源解析、创建和使用关系图

在这里插入图片描述

  • 以事务接口 Transaction 和事务工厂 TransactionFactory 的实现,包装数据源 DruidDataSourceFactory 的功能。数据源采用阿里的 druid
  • 当所有的数据源相关功能准备好之后,在 XMLConfigBuilder 解析 XML 配置操作中,对数据源的配置进行解析以及创建出相应的服务,存在到 Configuration 的环境配置中。
  • 最后在 DefaultSqlSession#selectOne 方法中完成 SQL 的执行和结果封装,最终就把整个 Mybatis 的核心脉络串联起来。

在这里插入图片描述

3.4 事务管理接口和事务工厂

💡 一次数据库的操作应该具备事务管理能力,而不是通过 JDBC 获取链接后直接执行,还应该把控链接、提交、回滚和关闭的操作处理。结合 JDBC 的能力封装事务管理。

3.4.1 事务的隔离级别

TransactionIsolationLevel.java

package com.lino.mybatis.session;import java.sql.Connection;/*** @description: 事务的隔离级别*/
public enum TransactionIsolationLevel {//包括JDBC支持的5个级别NONE(Connection.TRANSACTION_NONE),READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);private final int level;TransactionIsolationLevel(int level) {this.level = level;}public int getLevel() {return level;}
}

3.4.2 定义事务接口

Transaction.java

package com.lino.mybatis.transaction;import java.sql.Connection;
import java.sql.SQLException;/*** @description: 事务接口*/
public interface Transaction {/*** 获取数据库连接** @return 数据库连接* @throws SQLException SQL异常*/Connection getConnection() throws SQLException;/*** 提交** @throws SQLException SQL异常*/void commit() throws SQLException;/*** 回滚** @throws SQLException SQL异常*/void rollback() throws SQLException;/*** 关闭** @throws SQLException SQL异常*/void close() throws SQLException;
}
  • 定义标准的事务接口,连接、提交、回滚、关闭,具体可以由不同的事务方式进行实现。
  • 包括:JDBC 和托管事务,托管事务是交给 Spring 容器管理。

3.4.3 事务接口实现类

JdbcTransaction.java

package com.lino.mybatis.transaction.jdbc;import com.lino.mybatis.session.TransactionIsolationLevel;
import com.lino.mybatis.transaction.Transaction;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;/*** @description: JDBC 事务,直接利用 JDBC 的commit、rollback。依赖于数据源获得的连接管理事务范围*/
public class JdbcTransaction implements Transaction {protected Connection connection;protected DataSource dataSource;protected TransactionIsolationLevel level = TransactionIsolationLevel.NONE;protected boolean autoCommit;public JdbcTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {this.dataSource = dataSource;this.level = level;this.autoCommit = autoCommit;}public JdbcTransaction(Connection connection) {this.connection = connection;}@Overridepublic Connection getConnection() throws SQLException {connection = dataSource.getConnection();connection.setTransactionIsolation(level.getLevel());connection.setAutoCommit(autoCommit);return connection;}@Overridepublic void commit() throws SQLException {if (connection != null && !connection.getAutoCommit()) {connection.commit();}}@Overridepublic void rollback() throws SQLException {if (connection != null && !connection.getAutoCommit()) {connection.rollback();}}@Overridepublic void close() throws SQLException {if (connection != null && !connection.getAutoCommit()) {connection.close();}}
}
  • JDBC 事务实现类中,封装了获取连接、提交事务等操作,其实使用的就是 JDBC 本身提供的能力。

3.4.4 事务工厂

TransactionFactory.java

package com.lino.mybatis.transaction;import com.lino.mybatis.session.TransactionIsolationLevel;import javax.sql.DataSource;
import java.sql.Connection;/*** @description: 事务工厂*/
public interface TransactionFactory {/*** 根据 Connection 创建 事务** @param conn 连接* @return 事务对象*/Transaction newTransaction(Connection conn);/*** 根据数据源和事务隔离级别创建事务** @param dataSource 数据源* @param level      事务隔离级别* @param autoCommit 是否自动提交* @return 事务*/Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
  • 以工厂方法模式包装 JDBC 事务实现,为每一个事务实现都提供一个对应的工厂。
  • 与简单工厂的接口包装不同。

3.4.5 JDBC事务工厂

JdbcTransactionFactory.java

package com.lino.mybatis.transaction.jdbc;import com.lino.mybatis.session.TransactionIsolationLevel;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.TransactionFactory;import javax.sql.DataSource;
import java.sql.Connection;/*** @description: JDBC 事务工厂*/
public class JdbcTransactionFactory implements TransactionFactory {@Overridepublic Transaction newTransaction(Connection conn) {return new JdbcTransaction(conn);}@Overridepublic Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {return new JdbcTransaction(dataSource, level, autoCommit);}
}

3.5 数据源工厂创建

3.5.1 数据源工厂

DataSourceFactory.java

package com.lino.mybatis.datasource;import javax.sql.DataSource;
import java.util.Properties;/*** @description: 数据源工厂*/
public interface DataSourceFactory {/*** 添加数据源** @param props 数据源信息*/void setProperties(Properties props);/*** 获取数据源** @return 数据源*/DataSource getDataSource();
}

3.5.2 阿里的druid数据库连接池

DruidDataSourceFactory.java

package com.lino.mybatis.datasource.druid;import com.alibaba.druid.pool.DruidDataSource;
import com.lino.mybatis.datasource.DataSourceFactory;
import javax.sql.DataSource;
import java.util.Properties;/*** @description: Druid 数据源工厂*/
public class DruidDataSourceFactory implements DataSourceFactory {private Properties props;@Overridepublic void setProperties(Properties props) {this.props = props;}@Overridepublic DataSource getDataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(props.getProperty("driver"));dataSource.setUrl(props.getProperty("url"));dataSource.setUsername(props.getProperty("username"));dataSource.setPassword(props.getProperty("password"));return dataSource;}
}

3.6 创建配置环境基础类

3.6.1 绑定的SQL对象

BoundSql.java

package com.lino.mybatis.mapping;import java.util.Map;/*** @description: 绑定的SQL,是从SqlSource而来,将动态内容都处理完成得到的SQL语句字符串,其中包括?,还有绑定的参数*/
public class BoundSql {private String sql;private Map<Integer, String> parameterMappings;private String parameterType;private String resultType;public BoundSql(String sql, Map<Integer, String> parameterMappings, String parameterType, String resultType) {this.sql = sql;this.parameterMappings = parameterMappings;this.parameterType = parameterType;this.resultType = resultType;}public String getSql() {return sql;}public Map<Integer, String> getParameterMappings() {return parameterMappings;}public String getParameterType() {return parameterType;}public String getResultType() {return resultType;}
}

3.6.2 参数映射对象

ParameterMapping.java

package com.lino.mybatis.mapping;import cn.hutool.db.meta.JdbcType;
import com.lino.mybatis.session.Configuration;/*** @description: 参数映射 #{property,javaType=int,jdbcType=NUMERIC}*/
public class ParameterMapping {private Configuration configuration;/*** property*/private String property;/*** javaType = int*/private Class<?> javaType = Object.class;/*** javaType = NUMERIC*/private JdbcType jdbcType;public ParameterMapping() {}public static class Builder {private ParameterMapping parameterMapping = new ParameterMapping();private Builder(Configuration configuration, String property) {parameterMapping.configuration = configuration;parameterMapping.property = property;}public Builder javaType(Class<?> javaType) {parameterMapping.javaType = javaType;return this;}public Builder jdbcType(JdbcType jdbcType) {parameterMapping.jdbcType = jdbcType;return this;}}public Configuration getConfiguration() {return configuration;}public String getProperty() {return property;}public Class<?> getJavaType() {return javaType;}public JdbcType getJdbcType() {return jdbcType;}
}

3.6.3 环境配置类

Environment.java

package com.lino.mybatis.mapping;import com.lino.mybatis.transaction.TransactionFactory;
import javax.sql.DataSource;/*** @description: 环境*/
public final class Environment {/*** 环境id*/private final String id;/*** 事务工厂*/private final TransactionFactory transactionFactory;/*** 数据源*/private final DataSource dataSource;public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {this.id = id;this.transactionFactory = transactionFactory;this.dataSource = dataSource;}public static class Builder {private String id;private TransactionFactory transactionFactory;private DataSource dataSource;public Builder(String id) {this.id = id;}public Builder transactionFactory(TransactionFactory transactionFactory) {this.transactionFactory = transactionFactory;return this;}public Builder dataSource(DataSource dataSource) {this.dataSource = dataSource;return this;}public String id() {return this.id;}public Environment build() {return new Environment(this.id, this.transactionFactory, this.dataSource);}}public String getId() {return id;}public TransactionFactory getTransactionFactory() {return transactionFactory;}public DataSource getDataSource() {return dataSource;}
}

3.6.4 映射器语句类

MappedStatement.java

package com.lino.mybatis.mapping;import com.lino.mybatis.session.Configuration;
import java.util.Map;/*** @description: 映射器语句类*/
public class MappedStatement {private Configuration configuration;private String id;private SqlCommandType sqlCommandType;private BoundSql boundSql;public MappedStatement() {}public static class Builder {private MappedStatement mappedStatement = new MappedStatement();public Builder(Configuration configuration, String id, SqlCommandType sqlCommandType, BoundSql boundSql) {mappedStatement.configuration = configuration;mappedStatement.id = id;mappedStatement.sqlCommandType = sqlCommandType;mappedStatement.boundSql = boundSql;}public MappedStatement build() {assert mappedStatement.configuration != null;assert mappedStatement.id != null;return mappedStatement;}}public Configuration getConfiguration() {return configuration;}public String getId() {return id;}public SqlCommandType getSqlCommandType() {return sqlCommandType;}public BoundSql getBoundSql() {return boundSql;}
}
  • 去除 sql 多个参数字段,添加 BoundSql SQL 对象

3.7 类型别名注册器

💡 Mybatis 框架中我们所需要的基本类型、数组类型以及自定定义的事务实现和事务工厂都需要注册到类型别名注册器中进行管理。
在我们需要使用的时候可以从注册器中获取到具体的对象类型,之后再进行实例化的方式进行使用。

3.7.1 JDBC枚举类型

JdbcType.java

package com.lino.mybatis.type;import java.sql.Types;
import java.util.HashMap;
import java.util.Map;/*** @description: JDBC枚举类型*/
public enum JdbcType {// JDBC枚举类型INTEGER(Types.INTEGER),FLOAT(Types.FLOAT),DOUBLE(Types.DOUBLE),DECIMAL(Types.DECIMAL),VARCHAR(Types.VARCHAR),TIMESTAMP(Types.TIMESTAMP);public final int TYPE_CODE;private static Map<Integer, JdbcType> codeLookup = new HashMap<>();static {for (JdbcType type : JdbcType.values()) {codeLookup.put(type.TYPE_CODE, type);}}JdbcType(int code) {this.TYPE_CODE = code;}public static JdbcType forCode(int code) {return codeLookup.get(code);}
}

3.7.2 类型别名注册机

TypeAliasRegistry.java

package com.lino.mybatis.type;import java.util.HashMap;
import java.util.Locale;
import java.util.Map;/*** @description: 类型别名注册机*/
public class TypeAliasRegistry {private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<>();public TypeAliasRegistry() {// 构造函数里注册系统内置的类型别名registerAlias("string", String.class);// 基本包装类型registerAlias("byte", Byte.class);registerAlias("long", Long.class);registerAlias("short", Short.class);registerAlias("int", Integer.class);registerAlias("integer", Integer.class);registerAlias("double", Double.class);registerAlias("float", Float.class);registerAlias("boolean", Boolean.class);}public void registerAlias(String alias, Class<?> value) {String key = alias.toLowerCase(Locale.ENGLISH);TYPE_ALIASES.put(key, value);}public <T> Class<T> resolveAlias(String string) {String key = string.toLowerCase(Locale.ENGLISH);return (Class<T>) TYPE_ALIASES.get(key);}
}
  • TypeAliasRegistry 类型别名注册器中先做一些基本的类型注册,以及提供 registerAlias 注册方法和 resolveAlias 获取方法。

3.7.3 配置项

Configuration.java

package com.lino.mybatis.session;import com.lino.mybatis.binding.MapperRegistry;
import com.lino.mybatis.datasource.druid.DruidDataSourceFactory;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.transaction.jdbc.JdbcTransaction;
import com.lino.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.lino.mybatis.type.TypeAliasRegistry;import java.util.HashMap;
import java.util.Map;/*** @description: 配置项* @author: lingjian* @createDate: 2022/11/5 16:27*/
public class Configuration {/*** 环境*/protected Environment environment;/*** 映射注册机*/protected MapperRegistry mapperRegistry = new MapperRegistry(this);/*** 映射的语句,存在Map里*/protected final Map<String, MappedStatement> mappedStatements = new HashMap<>(16);/*** 类型别名注册机*/protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();public Configuration() {typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);typeAliasRegistry.registerAlias("DRUID", DruidDataSourceFactory.class);}public void addMappers(String packageName) {mapperRegistry.addMappers(packageName);}public <T> void addMapper(Class<T> type) {mapperRegistry.addMapper(type);}public <T> T getMapper(Class<T> type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);}public boolean hasMapper(Class<?> type) {return mapperRegistry.hasMapper(type);}public void addMappedStatement(MappedStatement ms) {mappedStatements.put(ms.getId(), ms);}public MappedStatement getMappedStatement(String id) {return mappedStatements.get(id);}public TypeAliasRegistry getTypeAliasRegistry() {return typeAliasRegistry;}public Environment getEnvironment() {return environment;}public void setEnvironment(Environment environment) {this.environment = environment;}
}
  • 添加 Environment 配置环境
  • 添加 TypeAliasRegistry 类型别名注册机,添加初始化添加 JDBC 事务工厂和 DRUID 数据源工厂
  • Configuration 配置选项中,添加类型别名注册机,通过构造函数添加 JDBCDRUID 注册操作。
  • 整个 Mybatis 的操作都是使用 Configuration 配置项进行串联流程,所有的内容都会在 Configuration 中进行连接。

3.8 解析数据源配置

💡 通过在 XML 解析器 XMLConfigBuilder 中,扩展对环境信息的解析。这里把数据源、事务类内容成为操作 SQL 环境。
解析后把配置信息写入到 Configuration 配置项中,便于后续使用。

3.8.1 构建器基类

BaseBuilder.java

package com.lino.mybatis.builder;import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.type.TypeAliasRegistry;/*** @description: 构建器的基类,建造者模式*/
public class BaseBuilder {protected final Configuration configuration;protected final TypeAliasRegistry typeAliasRegistry;public BaseBuilder(Configuration configuration) {this.configuration = configuration;this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();}public Configuration getConfiguration() {return configuration;}
}
  • 添加 TypeAliasRegistry 类型别名注册机

3.8.2 XML配置构建器

XMLConfigBuilder.java

package com.lino.mybatis.builder.xml;import com.lino.mybatis.builder.BaseBuilder;
import com.lino.mybatis.datasource.DataSourceFactory;
import com.lino.mybatis.io.Resources;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.SqlCommandType;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.transaction.TransactionFactory;
import com.lino.mybatis.type.TypeAliasRegistry;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;
import javax.sql.DataSource;
import java.io.Reader;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @description: XML配置构建器,建造者模式,集成BaseBuilder*/
public class XMLConfigBuilder extends BaseBuilder {private Element root;private static final Pattern pattern = Pattern.compile("(#\\{(.*?)})");public XMLConfigBuilder(Reader reader) {// 1.调用父类初始化Configurationsuper(new Configuration());// 2.dom4j 处理xmlSAXReader saxReader = new SAXReader();try {Document document = saxReader.read(new InputSource(reader));root = document.getRootElement();} catch (DocumentException e) {e.printStackTrace();}}/*** 解析配置:类型别名、插件、对象工厂、对象包装工厂、设置、环境、类型转换、映射器** @return Configuration*/public Configuration parse() {try {// 环境environmentsElement(root.element("environments"));// 解析映射器mapperElement(root.element("mappers"));} catch (Exception e) {throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}return configuration;}private void environmentsElement(Element context) throws Exception {String environment = context.attributeValue("default");List<Element> environmentList = context.elements("environment");for (Element e : environmentList) {String id = e.attributeValue("id");if (environment.equals(id)) {// 事务管理器TransactionFactory txFactory = (TransactionFactory) typeAliasRegistry.resolveAlias(e.element("transactionManager").attributeValue("type")).newInstance();// 数据源Element dataSourceElement = e.element("dataSource");DataSourceFactory dataSourceFactory = (DataSourceFactory) typeAliasRegistry.resolveAlias(dataSourceElement.attributeValue("type")).newInstance();List<Element> propertyList = dataSourceElement.elements("property");Properties props = new Properties();for (Element property : propertyList) {props.setProperty(property.attributeValue("name"), property.attributeValue("value"));}dataSourceFactory.setProperties(props);DataSource dataSource = dataSourceFactory.getDataSource();// 构建环境Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);configuration.setEnvironment(environmentBuilder.build());}}}private void mapperElement(Element mappers) throws Exception {List<Element> mapperList = mappers.elements("mapper");for (Element e : mapperList) {String resource = e.attributeValue("resource");Reader reader = Resources.getResourceAsReader(resource);SAXReader saxReader = new SAXReader();Document document = saxReader.read(new InputSource(reader));Element root = document.getRootElement();// 命名空间String namespace = root.attributeValue("namespace");// SELECTList<Element> selectNodes = root.elements("select");for (Element node : selectNodes) {String id = node.attributeValue("id");String parameterType = node.attributeValue("parameterType");String resultType = node.attributeValue("resultType");String sql = node.getText();// ? 匹配Map<Integer, String> parameter = new HashMap<>(16);Matcher matcher = pattern.matcher(sql);for (int i = 1; matcher.find(); i++) {String g1 = matcher.group(1);String g2 = matcher.group(2);parameter.put(i, g2);sql = sql.replace(g1, "?");}String msId = namespace + "." + id;String nodeName = node.getName();SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));// SQL语句初识化BoundSql boundSql = new BoundSql(sql, parameter, parameterType, resultType);MappedStatement mappedStatement = new MappedStatement.Builder(configuration, msId, sqlCommandType, boundSql).build();// 添加解析SQLconfiguration.addMappedStatement(mappedStatement);}// 注册Mapper映射器configuration.addMapper(Resources.classForName(namespace));}}
}
  • 添加 environmentsElement 配置环境处理
  • 修改 mapperElement 中创建 MappedStatement 映射器语句类的初始化
  • XMLConfigBuilder#parse 解析扩展对数据源解析操作, 在 environmentsElement 方法中包括事务管理器解析和从注册器中读取到事务工程的实现类,同理数据源也是从类型注册器中获取。
  • 最后把事务管理器和数据源的处理,通过环境构建 Environment.Builder 存放到 Configuration 配置项中,也就可以通过 Configuration 存在的地方都可以获取到数据源了。

3.9 SQL执行和结果封装

3.9.1 默认sqlSession实现类

DefaultSqlSession.java

package com.lino.mybatis.session.defaults;import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.SqlSession;
import java.lang.reflect.Method;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;/*** @description: 默认sqlSession实现类*/
public class DefaultSqlSession implements SqlSession {private Configuration configuration;public DefaultSqlSession(Configuration configuration) {this.configuration = configuration;}@Overridepublic <T> T selectOne(String statement) {return (T) ("你被代理了!" + statement);}@Overridepublic <T> T selectOne(String statement, Object parameter) {try {MappedStatement mappedStatement = configuration.getMappedStatement(statement);Environment environment = configuration.getEnvironment();Connection connection = environment.getDataSource().getConnection();BoundSql boundSql = mappedStatement.getBoundSql();PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());preparedStatement.setLong(1, Long.parseLong(((Object[]) parameter)[0].toString()));ResultSet resultSet = preparedStatement.executeQuery();List<T> objList = resultSet2Obj(resultSet, Class.forName(boundSql.getResultType()));return objList.get(0);} catch (Exception e) {e.printStackTrace();return null;}}private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {List<T> list = new ArrayList<>();try {ResultSetMetaData metaData = resultSet.getMetaData();int columnCount = metaData.getColumnCount();// 每次遍历值while (resultSet.next()) {T obj = (T) clazz.newInstance();for (int i = 1; i <= columnCount; i++) {Object value = resultSet.getObject(i);String columnName = metaData.getColumnName(i);String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);Method method;if (value instanceof Timestamp) {method = clazz.getMethod(setMethod, LocalDateTime.class);} else {method = clazz.getMethod(setMethod, value.getClass());}method.invoke(obj, value);}list.add(obj);}} catch (Exception e) {e.printStackTrace();}return list;}@Overridepublic <T> T getMapper(Class<T> type) {return configuration.getMapper(type, this);}@Overridepublic Configuration getConfiguration() {return configuration;}
}
  • 修改 selectOne,引入配置环境和数据源,将之前打印改为调用 JDBC 连接数据查询 SQL
  • 添加 resultSet2Obj 返回结果处理方法
  • selectOne 方法中获取 Connection 数据源连接,并简单的执行 SQL 语句,并对执行的结果进行封装处理。

四、测试:数据源的解析、创建和使用

4.1 创建 mybatis 数据库并添加数据库表

CREATE TABLEUSER(id bigint NOT NULL AUTO_INCREMENT COMMENT '自增ID',userId VARCHAR(9) COMMENT '用户ID',userHead VARCHAR(16) COMMENT '用户头像',createTime TIMESTAMP NULL COMMENT '创建时间',updateTime TIMESTAMP NULL COMMENT '更新时间',userName VARCHAR(64),PRIMARY KEY (id))ENGINE=InnoDB DEFAULT CHARSET=utf8;insert into user (id, userId, userHead, createTime, updateTime, userName) values (1, '10001', '1_04', '2022-11-07 00:00:00', '2022-11-07 00:00:00', '小零');
  • 创建一个数据库名为 mybatis 的数据库
  • 在数据库 mybatis 中创建表 user,并添加测试数据

4.2 提供 DAO 接口 和 User 实体类

4.2.1 用户持久层

IUserDao.java

package com.lino.mybatis.test.dao;import com.lino.mybatis.test.po.User;/*** @Description: 用户持久层*/
public interface IUserDao {/*** 根据ID查询用户信息** @param uId ID* @return String 名称*/User queryUserInfoById(Long uId);
}
  • 返回结果改为 User 实体类

4.2.2 用户类

User.java

package com.lino.mybatis.test.po;import cn.hutool.core.date.DateTime;
import java.time.LocalDateTime;
import java.util.Date;/*** @description: 用户实例类*/
public class User {private Long id;/*** 用户ID*/private String userId;/*** 头像*/private String userHead;/*** 用户名称*/private String userName;/*** 创建时间*/private Date createTime;/*** 更新时间*/private Date updateTime;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getUserId() {return userId;}public void setUserId(String userId) {this.userId = userId;}public String getUserHead() {return userHead;}public void setUserHead(String userHead) {this.userHead = userHead;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public Date getCreateTime() {return createTime;}public void setCreateTime(Date createTime) {this.createTime = createTime;}public Date getUpdateTime() {return updateTime;}public void setUpdateTime(Date updateTime) {this.updateTime = updateTime;}
}
  • 添加 userName 属性

4.3 配置数据源和配置Mapper

4.3.1 配置文件

mybatis-config-datasource.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="DRUID"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><mappers><mapper resource="mapper/User_Mapper.xml"/></mappers>
</configuration>
  • 添加 environments 数据库配置信息。
  • 通过 mybatis-config-datasource.xml 配置数据源信息,包括:driver、url、username、password
  • DataSource 配置的是 DRUID,目前只实现了这个数据源。

4.3.2 用户接口配置文件

User_Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lino.mybatis.test.dao.IUserDao"><select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.lino.mybatis.test.po.User">SELECT id, userId, userHead, userNameFROM userWHERE id = #{id}</select>
</mapper>
  • 去除 createTime 查询字段, 添加 userName 查询字段。

4.4 单元测试

ApiTest

/*** 测试映射器注册机*/
@Test
public void test_SqlSessionFactory() throws IOException {// 1.从SqlSessionFactory中获取SqlSessionSqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));SqlSession sqlSession = sqlSessionFactory.openSession();// 2.获取映射器对象IUserDao userDao = sqlSession.getMapper(IUserDao.class);// 3.测试验证User user = userDao.queryUserInfoById(1L);logger.info("测试结果:{}", JSON.toJSONString(user));
}

测试结果

08:32:18.875 [main] INFO  c.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
08:32:19.623 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小零哥"}
  • 从测试结果看,通过对数据源的解析、包装和使用,已经可以对 SQL 语句进行执行和包装返回的结果信息了。

4.5 功能验证

ApiTest

@Test
public void test_selectOne() throws IOException {// 解析XMLReader reader = Resources.getResourceAsReader("mybatis-config-datasource.xml");XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(reader);Configuration configuration = xmlConfigBuilder.parse();// 获取 DefaultSqlSessionSqlSession sqlSession = new DefaultSqlSession(configuration);// 执行查询:默认是一个集合参数Object[] req = {1L};Object result = sqlSession.selectOne("com.lino.mybatis.test.dao.IUserDao.queryUserInfoById", req);logger.info("测试结果:{}", JSON.toJSONString(result));
}
  • 对本章节新增的内容进行提取,进行测试验证。
  • 新增内容:解析内容的添加、处理 XML 配置中的数据源信息,以及解析后可以在 DefaultSqlSession 中调用数据源执行 SQL 语句并返回结果。

测试结果

10:19:43.519 [main] INFO  c.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
10:19:44.306 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
  • 测试结果是通过的。
  • XML 数据源元素配置的解析,到 Configuration 资源的注册以及写入相关配置到配置项,并在 DefaultSqlSession 中进行使用。
  • 同时这里跳过代理方式获取 Mapper 而是直接拿到 SqlSession 执行 selectOne 方法的方式进行处理,这样更容易观察整个功能的迭代开发。

请添加图片描述

五、总结:数据源的解析、创建和使用

  • 以解析 XML 配置解析为入口,添加数据源的整合和包装,引出事务工厂对 JDBC 事务的处理,并加载到环境配置中进行运用。
  • 通过数据源的引入就可以在 DefaultSqlSession 中从 Configuration 配置引入环境信息,把对应的 SQL 语句提交给 JDBC 进行处理并简单封装结果数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/95111.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据分析作业2

中国在 2020 年开展第七次全国人口普查&#xff0c;截止 2021 年 5 月 11 日普查结果公布&#xff0c;全国人口共1411778724人。单从数据表格看相关数据不够直观&#xff0c;需要进行数据可视化展示&#xff0c;方便查看数据结果。 任务一&#xff1a;链接 MySQL 数据库&#x…

Web_单一视频文件mp4转换为m3u分段ts文件实现边下边播

一、下载ffmpeg: Builds - CODEX FFMPEG @ gyan.dev 二、转换视频文件: 先解压缩,会看到如下结构: 进入bin目录,把需要转换的视频文件复制过来,同时新建一个文件夹用来存放转换后的文件,然后按住Shift键同时单击鼠标右键,选择打开Powershell窗口: 输入以下命令(根据…

R语言随机波动模型SV:马尔可夫蒙特卡罗法MCMC、正则化广义矩估计和准最大似然估计上证指数收益时间序列...

全文链接&#xff1a;http://tecdat.cn/?p31162 最近我们被客户要求撰写关于SV模型的研究报告&#xff0c;包括一些图形和统计输出&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 相关视频 本文做SV模型&#xff0c;选取马尔可夫蒙特卡罗法(MCMC)、正则化广…

移除链表元素_每日一题

“路虽远&#xff0c;行则将至” ❤️主页&#xff1a;小赛毛 ☕今日份刷题&#xff1a;移除链表元素 题目描述&#xff1a; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例1&…

Linux系统Ubuntu以非root用户身份操作Docker的方法

本文介绍在Linux操作系统Ubuntu版本中&#xff0c;通过配置&#xff0c;实现以非root用户身份&#xff0c;进行Docker各项操作的具体方法。 在文章Linux系统Ubuntu配置Docker详细流程&#xff08;https://blog.csdn.net/zhebushibiaoshifu/article/details/132612560&#xff0…

在VScode中使用sftp传输本地文件到服务器端

安装SFTP 在VScode的扩展中安装sftp 注意这里需要在你没连接服务器的状态下安装&#xff0c;即本机需要有sftp 配置传输端口 安装成功后&#xff0c;使用快捷键"ctrlshiftp",输入sftp&#xff0c;选择Config 根据自己的实际情况修改配置文件&#xff0c;主要改h…

【zookeeper】zookeeper介绍

分布式协调技术 在学习ZooKeeper之前需要先了解一种技术——分布式协调技术。那么什么是分布式协调技术&#xff1f;其实分布式协调技术主要用来解决分布式环境当中多个进程之间的同步控制&#xff0c;让他们有序的去访问某种临界资源&#xff0c;防止造成"脏数据"的…

leetcode 189. 轮转数组

2023.9.3 k的取值范围为0~100000&#xff0c;此时需要考虑到两种情况&#xff0c;当k为0时&#xff0c;此时数组不需要轮转&#xff0c;因此直接return返回&#xff1b;当k大于等于数组nums的大小时&#xff0c;数组将会转为原来的数组&#xff0c;然后再接着轮转&#xff0c;此…

vue3中axios的使用方法

在Vue 3中使用axios发送HTTP请求的方法与Vue 2中基本相同。首先&#xff0c;需要安装axios库&#xff1a; npm install axios然后&#xff0c;在Vue组件中引入axios&#xff1a; import axios from axios;接下来&#xff0c;可以在Vue组件的方法中使用axios发送HTTP请求。例如…

vue 从后端取图片返回发现是两张,但自己只要一张图片 怎么操作

1.用JavaScript里边常用的分隔符去操作 item.original_status.user.profile_image_url.split(,)[0] <van-imagewidth"100"height"100":src"item.original_status.user.photo_domain item.original_status.user.profile_image_url.split(,)[0]&quo…

2023年高教社杯 国赛数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 最短时…

阿里云效和阿里在线idea使用

阿里云效 https://flow.aliyun.com/all?page1 阿里在线idea&#xff1a;https://ide.aliyun.com/ 在云效中创建的项目可以在在线idea 打开 运行中的项目 设置ssh 设置以后可以使用云效率的代码构建来构建代码 设置 添加自有云或者体验5h