【简写Mybatis】04-数据源的解析、创建和使用

news2024/9/23 13:17:09

前言

在学习MyBatis源码文章中,斗胆想将其讲明白;故有此文章,如有问题,不吝指教!

注意:

学习源码一定一定不要太关注代码的编写,而是注意代码实现思想;

通过设问方式来体现代码中的思想;方法:5W+1H

源代码:https://gitee.com/xbhog/mybatis-xbhoghttps://github.com/xbhog/mybatis-xbhog;交个朋友,欢迎star。
也可关注笔者公众号:

回顾&分析

在上一局的实现中【简写Mybatis】03-Mapper xml的注册和使用;我们完善了XML的解析、读取、封装;新增Configuration配置类进行数据流转,最后对Mapper select方法进行了封装等操作;但最后还是没有数据库的执行操作;

本章节就不看测试类,来看下上一局的测试输出。

正在查看资源:mapper/User_Mapper.xml
18:49:23.642 [main] INFO  c.xbhog.builder.xml.XmlConfigBuilder - 匹配出来的信息为:#{id}:id
18:49:23.668 [main] DEBUG c.x.binding.MapperMethod$SqlCommand - 查看当前方法【SqlCommand】参数传进来的信息:接口信息:interface com.xbhog.IUserDao,方法信息:public abstract java.lang.String com.xbhog.IUserDao.queryUserInfoById(java.lang.String)
18:49:23.670 [main] INFO  com.xbhog.AppTest - 测试结果:你被代理了!方法:com.xbhog.IUserDao.queryUserInfoById 入参:[Ljava.lang.Object;@71c7db30,待执行的SQl:
        SELECT id, userId, userHead, createTime
        FROM user
        where id = ?

上述测试用例只输出了SQL没有执行;再看下实际Mybatis中数据库使用;前置条件是需要配置相关的数据库信息;

 <!--连接数据库环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value=" jdbc:mysql://xxx.xxx:3306/mybatis"/>
                <property name="username" value="mybatis"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

目的

  1. 解析数据库相关数据(XML解析)
  2. 数据源的设置与使用
  3. 初步对 SQL 的执行结果进行简单包装
  4. 事务管理(扩展)

实现

项目结构如下:

├─src
│  ├─main
│  │  └─java
│  │      └─com
│  │          └─xbhog
│  │              ├─binding
│  │              │      MapperMethod.java
│  │              │      MapperProxy.java
│  │              │      MapperProxyFactory.java
│  │              │      MapperRegistry.java
│  │              │      
│  │              ├─builder
│  │              │  │  BaseBuilder.java
│  │              │  │  
│  │              │  └─xml
│  │              │          XmlConfigBuilder.java
│  │              │          
│  │              ├─datasource
│  │              │      DataSourceFactory.java
│  │              │      DruidDataSourceFactory.java
│  │              │      
│  │              ├─mapping
│  │              │      Environment.java
│  │              │      MappedStatement.java
│  │              │      SqlCommandType.java
│  │              │      
│  │              ├─session
│  │              │  │  Configuration.java
│  │              │  │  SqlSession.java
│  │              │  │  SqlSessionFactory.java
│  │              │  │  SqlSessionFactoryBuilder.java
│  │              │  │  TransactionIsolationLevel.java
│  │              │  │  
│  │              │  └─defaults
│  │              │          DefaultSqlSession.java
│  │              │          DefaultSqlSessionFactory.java
│  │              │          
│  │              └─transaction
│  │                  │  Transaction.java
│  │                  │  TransactionFactory.java
│  │                  │  
│  │                  └─jdbc
│  │                          JdbcTransaction.java
│  │                          JdbcTransactionFactory.java
│  │                          
│  └─test
│      ├─java
│      │  └─com
│      │      └─xbhog
│      │              AppTest.java
│      │              IUserDao.java
│      │              User.java
│      │              
│      └─resources
│          │  mybatis-config-datasource.xml
│          │  
│          └─mapper
│                  User_Mapper.xml

数据库信息的解析

跟过文章的朋友应该清楚Mybatis中文件配置解析是在哪个地方实现;

这里简单提一下路径:

\mybatis-simple-04\src\main\java\com\xbhog\builder\xml\XmlConfigBuilder.java

parse方法中实现;

看下读取操作:

public void parseXml() throws DocumentException {
        // xml文件内容转换为字符串流
        Reader reader = ResourceUtil.getUtf8Reader("mybatis-config-datasource.xml");

        // 从字符串流中读取并创建XML Document对象
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(new InputSource(reader));

        // 获取XML文档的根元素
        Element rootElement = document.getRootElement();
        Element environments = rootElement.element("environments");

        //获取默认的环境信息
        String envDefault = environments.attributeValue("default");
        //获取环境所有的信息
        List<Element> elements = environments.elements("environment");
        for(Element element : elements){
            String id = element.attributeValue("id");
            if(!Objects.equals(id,envDefault)){
                return;
            }
            //获取事务类型
            String transactionType = element.element("transactionManager").attributeValue("type");
            //获取数据源类型
            Element dataSource = element.element("dataSource");
            String dataSourceType = dataSource.attributeValue("type");
            //参数列表
            Properties properties = new Properties();
            List<Element> propertys = dataSource.elements("property");
            for(Element property : propertys){
                properties.setProperty(property.attributeValue("name"),property.attributeValue("value"));
            }
            logger.info("事务类型:{},数据源类型:{},数据源参数列表:{}",transactionType,dataSourceType,JSONUtil.parse(properties));
        }

    }

结果测试如下:

Connected to the target VM, address: '127.0.0.1:62810', transport: 'socket'
16:25:38.000 [main] INFO  com.xbhog.AppTest - 事务类型:JDBC,数据源类型:DRUID,数据源参数列表:{"url":"jdbc:mysql://127.0.0.1:3306/dev?useUnicode=true","password":"123456","driver":"com.mysql.jdbc.Driver","username":"root"}

Disconnected from the target VM, address: '127.0.0.1:62810', transport: 'socket'

有效数据拿到了,接下来进行属性的映射以及数据流转。

既然是对数据库的操作,那么避免不了数据源、事务的设置。

设置数据源

设置DataSourceFactory来创建和初始化数据源,这里采用工厂模式实现,使得数据源的实例化过程与MyBatis核心逻辑解耦,增强了程序的灵活性和可扩展性;并且工厂可以生产多种类型的数据源,既满足职责分离也具有很强的扩展。

既然是创建和初始化数据源,那么需要的两个方法;

  1. 属性装配(赋值)
  2. 生成数据源并获取

代码如下:

public class DruidDataSourceFactory implements DataSourceFactory {

    private Properties props;

    @Override
    public void setProperties(Properties props) {
        this.props = props;
    }

    @Override
    public 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;
    }

}

这里串联到数据源环境解析的XmlConfigBuilder中。

遍历xml中<property>标签数据赋值到Spring中Properties类,为了后续的使用,将env信息进行实体保存并放到Configuration中,用于数据流转。

代码如下:

private void environmentElement(Element environments) throws Exception{
        String env= environments.attributeValue("default");
        List<Element> elements = environments.elements("environment");
        for(Element e : elements){
            String id = e.attributeValue("id");
            if(env.equals(id)){
                Element dataSourceElement = e.element("dataSource");
                //获取数据库连接池
                DruidDataSourceFactory dataSourceFactory = new DruidDataSourceFactory();
                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).dataSource(dataSource);
                configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }

SQL封装

上述操作都是数据准备阶段,完成后通过数据源进行sql执行并进行结果包装。在select方法的实现中通过Configuation获取数据源的连接方法,通过查询参数的设置以及sql的执行获取返回结果。

MappedStatement mappedStatements = configuration.getMappedStatements(statement);
Environment environment = configuration.getEnvironment();
Connection connection = environment.getDataSource().getConnection();
String sql = mappedStatements.getSql();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setLong(1,Long.parseLong(((Object[]) parameter)[0].toString()));
ResultSet resultSet = preparedStatement.executeQuery();

返回结果的处理;

入参方法解释:对数据库结果数据进行清洗;入参jdbc执行结果以及结果类型(对应于SQL查询结果映射到的Java类型的全限定名-User)

List<T> resultSet2Obj = resultSet2Obj(resultSet, Class.forName(mappedStatements.getResultType()));

对SQL结果的处理:

  1. 获取SQL结果MeatData数据
  2. 获取字段数量
  3. 类属性处理以及实体反射赋值

代码如下:

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, Date.class);
            } else {
                //会寻找clazz类(User)中名字为setMethod(setId),并且只接受一个参数的方法,且该参数类型与value.getClass()所返回的类型一致
                method = clazz.getMethod(setMethod, value.getClass());
            }
            //实体类属性赋值
            method.invoke(obj, value);
        }
        list.add(obj);
    }
} catch (Exception e) {
    e.printStackTrace();
}

事务处理

在平时接触最多的数据库连接就是JDBC,需要明确的是数据库连接应该在事务中处理,这样才能保证数据的提交、回滚、关闭的原子性,在代码实现上也是如此。

不过一般我们并不会使用 Mybatis 管理事务,而是将 Mybatis 集成到 Spring,由 Spring 进行事务的管理。

事务的类型也有几个,这里先处理JDBC的事务,同数据源一样开工厂模式进行封装。

JDBC 事务直接使用JDBC的commit、rollback,依赖于数据源获得的连接来管理事务范围。

代码如下:

public class JdbcTransaction implements Transaction {

    protected Connection connection;

    protected DataSource dataSource;

    protected boolean autoCommit;

    protected TransactionIsolationLevel level = TransactionIsolationLevel.NONE;


    public JdbcTransaction(DataSource dataSource, boolean autoCommit, TransactionIsolationLevel level) {
        this.dataSource = dataSource;
        this.autoCommit = autoCommit;
        this.level = level;
    }

    public JdbcTransaction(Connection connection) {
        this.connection = connection;
    }

    @Override
    public Connection getConnection() throws SQLException {
        //获取一个新的数据库连接实例
        connection = dataSource.getConnection();
        //设置当前数据库连接的事务隔离级别
        connection.setTransactionIsolation(level.getLevel());
        //控制数据库连接的自动提交行为
        connection.setAutoCommit(autoCommit);
        return connection;
    }

    @Override
    public void commit() throws SQLException {
        if(Objects.nonNull(connection) && !connection.getAutoCommit()){
            connection.commit();
        }
    }

    @Override
    public void rollback() throws SQLException {
        if(Objects.nonNull(connection) && !connection.getAutoCommit()){
            connection.rollback();
        }
    }

    @Override
    public void close() throws SQLException {
        if (connection != null && !connection.getAutoCommit()) {
            connection.close();
        }
    }
}

测试

新建User表,user_mapper.xml对照:

<select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.xbhog.User">
        SELECT id, userId, userHead
        FROM user
        where id = #{id}
    </select>
public void testApp() throws Exception {
        // 1. 从SqlSessionFactory中获取SqlSession
        Reader reader = ResourceUtil.getUtf8Reader("mybatis-config-datasource.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 2. 获取映射器对象
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);

        // 3. 测试验证
        User user = userDao.queryUserInfoById(1L);
        logger.info("测试结果:{}", JSONUtil.parse(user));
    }
11:33:24.257 [main] INFO  com.xbhog.AppTest - 测试结果:{"userId":"10001","userHead":"1_04","id":1}

学习&参考

https://mp.weixin.qq.com/s/g9cZOsYSFJtp6FoFtMg6RQ

mybatis源码

AI大模型

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis中的String编码转换底层原理及6.0新特性

String编码转换底层原理 String对象为什么把大于39字节或者44字节的字符串编码为raw&#xff0c;小于的时候编码为embstr? 在Redis3.2以前的版本中,SDS作为字符串类型中存储字符串内容的结构&#xff0c;源码如下&#xff1a; 3.2版本SDS结构 struct sdshdr {// 记录buf数…

【DFS】树的重心

树的邻接表 存储方式 int N; int h[N];//存以N为编号的节点的下一个节点的idx int e[2N];//存idx的节点的编号 int nex[2N];//存idx节点的下一个节点的idxvoid add(int a, int b){ e[idx] b; nex[idx] h[a]; h[a]idx; }dfs遍历方式&#xff1a; void dfs(int u){state[u]…

day6 3/18

2.试编程&#xff1a; 封装一个动物的基类&#xff0c;类中有私有成员&#xff1a;姓名&#xff0c;颜色&#xff0c;指针成员年纪 再封装一个狗这样类&#xff0c;共有继承于动物类&#xff0c;自己拓展的私有成员有&#xff1a;指针成员&#xff1a;腿的个数&#xff08;整…

sqllab第二十四关通关笔记

知识点&#xff1a; 二次注入 先埋一个炸弹&#xff0c;然后通过其他路径引爆它 查看界面发现是一个登录框&#xff0c;尝试进行登录框的注入发现这里不存在注入点 那么就注册一个新的账户吧 通过点击注册&#xff0c;进入注册面板&#xff0c;注册一个新的账户 用户名为 re…

【算法】多路归并(鱼塘钓鱼)

有 N 个鱼塘排成一排&#xff0c;每个鱼塘中有一定数量的鱼&#xff0c;例如&#xff1a;N5 时&#xff0c;如下表&#xff1a; 鱼塘编号12345第1分钟能钓到的鱼的数量&#xff08;1..1000&#xff09;101420169每钓鱼1分钟钓鱼数的减少量&#xff08;1..100)24653当前鱼塘到下…

【Java 并发】AbstractQueuedSynchronizer 中的 Condition

1 简介 任何一个 Java 对象都天然继承于 Object 类, 在线程间实现通信的往往会应用到 Object 的几个方法, 比如 wait(), wait(long timeout), wait(long timeout, int nanos) 与 notify(), notifyAll() 几个方法实现等待 / 通知机制。同样的, 在 Java Lock 体系下也有同样的方…

代码随想录算法训练营第day27|93.复原IP地址 、 78.子集 、 90.子集II

93.复原IP地址 93. 复原 IP 地址 - 力扣&#xff08;LeetCode&#xff09; 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 …

AI论文速读 | UniTS:构建统一的时间序列模型

题目&#xff1a;UniTS: Building a Unified Time Series Model 作者&#xff1a;Shanghua Gao&#xff08;高尚华&#xff09;, Teddy Koker, Owen Queen, Thomas Hartvigsen, Theodoros Tsiligkaridis, Marinka Zitnik 机构&#xff1a;哈佛大学&#xff08;Harvard&#x…

学完排序算法,终于知道用什么方法给监考完收上来的试卷排序……

由于每个老师批改完卷子之后装袋不一定是有序的&#xff0c;鼠鼠我被拉去当给试卷排序的苦力。面对堆积成山的试卷袋&#xff0c;每一份试卷袋的试卷集又很重&#xff0c;鼠鼠我啊为了尽早下班&#xff0c;决定用一种良好的办法进行排序。 1.插入排序 首先考虑的是插入排序。…

Python 井字棋游戏

井字棋是一种在3 * 3格子上进行的连珠游戏&#xff0c;又称井字游戏。井字棋的游戏有两名玩家&#xff0c;其中一个玩家画圈&#xff0c;另一个玩家画叉&#xff0c;轮流在3 * 3格子上画上自己的符号&#xff0c;最先在横向、纵向、或斜线方向连成一条线的人为胜利方。如图1所示…

阿里云-零基础入门NLP【基于机器学习的文本分类】

文章目录 学习过程赛题理解学习目标赛题数据数据标签评测指标解题思路TF-IDF介绍TF-IDF 机器学习分类器TF-IDF LinearSVCTF-IDF LGBMClassifier 学习过程 20年当时自身功底是比较零基础(会写些基础的Python[三个科学计算包]数据分析)&#xff0c;一开始看这块其实挺懵的&am…

【C语言】数9的个数

编写程序数一下 1到 100 的所有整数中出现多少个数字9 1&#xff0c;首先产生1~100的数字。然猴设法得到数9个数&#xff0c;例如个位&#xff1a;19%109&#xff0c;十位&#xff1a;91/109。 2&#xff0c;每次得到数九的时候&#xff0c;就用一个变量来进行计数。 代码如…

Python--成员方法、@staticmethod将成员方法静态化、self参数释义

在 Python 中&#xff0c;成员方法是指定义在类中的函数&#xff0c;用于操作类的实例对象。成员方法通过第一个参数通常命名为 self&#xff0c;用来表示调用该方法的实例对象本身。通过成员方法&#xff0c;可以实现类的行为和功能。 成员方法的定义 在类中定义成员…

苍穹外卖-day10:Spring Task、订单状态定时处理、来单提醒(WebSocket的应用)、客户催单(WebSocket的应用)

苍穹外卖-day10 课程内容 Spring Task订单状态定时处理WebSocket来单提醒客户催单 功能实现&#xff1a;订单状态定时处理、来单提醒和客户催单 订单状态定时处理&#xff1a; 来单提醒&#xff1a; 客户催单&#xff1a; 1. Spring Task 1.1 介绍 Spring Task 是Spring框…

电脑装win11(作si版)

装win11经历 前言&#xff1a;因为我的u盘今天到了&#xff0c;迫不及待试试装机 然后在一系列准备好工具后&#xff0c;便是开始拿学校的机房电脑来试试手了~~ 前期准备 下载好win11镜像&#xff08;可以去微软官网下载&#xff09; 下载Rufus工具 https://www.lanzoue.com/…

2023年度VSCode主题推荐(个人常用主题存档)

前言 早在2018年的时候发了一篇关于VSCode主题风格推荐——VS Code 主题风格设置&#xff0c;时过境迁&#xff0c;如今常用的主题皮肤早已更替。 今天下午在整理VSCode插件的时候&#xff0c;不小心把常用的那款&#xff08;亮色&#xff09;主题插件给删除了&#xff0c;无…

配置OGG 如何批量修改源端及目标端序列值_满足客户变态需求学会这招你就赚了

欢迎您关注我的公众号【尚雷的驿站】 **************************************************************************** 公众号&#xff1a;尚雷的驿站 CSDN &#xff1a;https://blog.csdn.net/shlei5580 墨天轮&#xff1a;https://www.modb.pro/u/2436 PGFans&#xff1a;ht…

鸿蒙App开发学习 - TypeScript编程语言全面开发教程(下)

现在我们接着上次的内容来学习TypeScript编程语言全面开发教程&#xff08;下半部分&#xff09; 4. 泛型 TypeScript 中的泛型&#xff08;Generics&#xff09;是一种编程模式&#xff0c;用于在编写代码时增强灵活性和可重用性。泛型使得在定义函数、类、接口等数据类型时…

DeformableAttention的原理解读和源码实现

本专栏主要是深度学习/自动驾驶相关的源码实现,获取全套代码请参考 目录 原理第一步看看输入:第二步,准备工作:生成参考点的偏移量生成参考点的权重生成参考点 第三步,工作: 源码 原理 目前流行3D转2DBEV方案的都绕不开的transfomer变体-DeformableAttention. 传统transform…

DataFunSummit 2023因果推断在线峰会:解码数据与因果,引领智能决策新篇章(附大会核心PPT下载)

在数据驱动的时代&#xff0c;因果推断作为数据科学领域的重要分支&#xff0c;正日益受到业界的广泛关注。DataFunSummit 2023年因果推断在线峰会&#xff0c;汇聚了国内外顶尖的因果推断领域专家、学者及业界精英&#xff0c;共同探讨因果推断的最新进展、应用与挑战。本文将…