Mybatis 笔记 (一) V- 3.5.16

Mybatis 笔记(3.5.16)

1、基础数据

    -- 创建用户表
CREATE TABLE users
(
    id         INT AUTO_INCREMENT PRIMARY KEY,
    username   VARCHAR(50)  NOT NULL UNIQUE,
    email      VARCHAR(100) NOT NULL UNIQUE,
    password   VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 插入示例数据
INSERT INTO users (username, email, password)
VALUES ('john_doe', 'john.doe@example.com', 'password123'),
       ('jane_smith', 'jane.smith@example.com', 'securepassword'),
       ('alice_jones', 'alice.jones@example.com', 'mypassword'),
       ('bob_brown', 'bob.brown@example.com', 'password456'),
       ('charlie_black', 'charlie.black@example.com', 'pass789');

2、基础依赖

测试数据库: mysql 8.0.26

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>8.0.26</version>
</dependency>

3、魔改点标记

1、事务管理器

我们可以自定义事务管理器来实现自己的额外需求。

2、切换环境

根据 Environment 的id选择

A、试试SqlSessionFactory

对应 :task one

这里主要试试如何创建 SqlSessionFactory 来创建连接以查询对话

基本流程描绘

SqlSessionFactoryBuilder
          |                             done       
          ↓                              ↑       
   XMLConfigBuilder (读取配置内容)  parseConfiguration(核心解析方法)   
          |                              ↑
          ↓                              |
   XPathParser (直接javax的解析)           |
          |                              ↑
          ↓                              |
    configuration.xml               ----> 
                      

1、利用dtd 约束基础数据库 和 mapper 映射文件所在.

2、利用XML解析数据,构建 SqlSessionFactory 来完成整个会话的创建和处理。

如果自己魔改,建议无论如何数据库都留用 pooled 数据库,避免习惯性测试纰漏。

3、这里虽然实现很多,但是最终是三种模式,如图(图A-b-1)
在这里插入图片描述
(图A-1-1)

B、建立连接的三种方式

根据 A-3 可知,原来可以三种模式实现。

1、Reader model

2、InputSteam model

3、Configuration model

1、执行方法

SqlSession session = sqlMapper.openSession();
try {
    User user = (User) session.selectOne("mapper.UserMapper.getUser", 1);
    System.out.println(user.getId() + "," + user.getUsername());
} finally {
    session.close();
}

2、实现方式

除了第三种,前两种除了读取方式不一致,都相当轻松模拟了。

public static SqlSessionFactory ReaderModel(String resource){
    try (Reader reader = Resources.getResourceAsReader(resource)) {
        return new SqlSessionFactoryBuilder().build(reader);
    } catch (IOException e) {
        System.err.println("Error reading file: " + e.getMessage());
        return null;
    }
}

public static  SqlSessionFactory inputSteamModel(String resource){
    try (InputStream inputStream = MybatisHelloWorld.class.getClassLoader().getResourceAsStream(resource)) {
        return new SqlSessionFactoryBuilder().build(inputStream);
    } catch (IOException e) {
        System.err.println("Error reading file: " + e.getMessage());
        return null;
    }
}

C、“复杂”的 Configuration 模式实现

比起前两种实现,第三种相当于自己来折腾了,走起。

1、直接构建Configuration

直接构建尝试

则会在图 (C-1-1) 处触发空指针异常

Configuration configuration = new Configuration();
sqlMapper = new SqlSessionFactoryBuilder().build(configuration);

在这里插入图片描述
(图 C-1-1)

2、补充environment 要素

由于直接构建出现空指针异常,因此需要补充

Environment 及其内部的 datasourcce

Tips: 这里相当于配置文件中配置数据库的地方了。

构建Environment需要三个参数

String id : 这里是环境的唯一id

TransactionFactory transactionFactory :事务工厂

DataSource dataSource : 数据源

2.1、填充id

这里其实就是用来填充环境,选择环境的。

这里,我们目前就用 dedevelopment

2.2、填充TransactionFactory

这里主要分为两个,如图 (C-2-2-1)

1、JdbcTransaction:使用JDBC的事务管理机制

2、ManagedTransaction: 什么都不做的事务管理

为了严谨后续测试,使用 JdbcTransaction

如果留空通常会默认选择后者,但直接构建是必选的。
在这里插入图片描述
(图 C-2-2-1)
在这里插入图片描述
(图 C-2-2-2)

2.3、填充 DataSource

这里主要提供了两种实现方式,如图(C-2-3-1)

1、 PooledDataSource: 线程安全的连接池的数据源

2、UnpooledDataSource: 用完就关的数据源(测试用)

在这里插入图片描述
(图 C-2-3-1)

2.4、整合填充

Environment development = new Environment
            .Builder("development")
            .transactionFactory(new JdbcTransactionFactory())
            .dataSource(new PooledDataSource("com.mysql.jdbc.Driver"
            , "jdbc:mysql://localhost:3306/sys?useUnicode=true"
            , "root"
            , "root"))
            .build();
    Configuration configuration = new Configuration(development);

    sqlMapper = new SqlSessionFactoryBuilder().build(configuration);

    SqlSession session = sqlMapper.openSession();
    try {

        User user = (User) session.selectOne("mapper.UserMapper.getUser", 1);
        System.out.println(user.getId() + "," + user.getUsername());
    } finally {
        session.close();
    }	

2.5、不存在映射语句集合

执行后会发现

Mapped Statements collection does not contain value for mapper.UserMapper.getUser

3、补充映射语句

正常的配置,也是需要配置sql、对象映射才能用,也是正常的。

根据报错得知,我们可以从 DefaultSqlSession#selectList 下发现这个问题。

余下只需要补充即可。

在这里插入图片描述
(图3-1)

3.1、补充 MappedStatement

根据 C.2.5 线索,需要补充 MappedStatement

执行后,会发现

A query was run and no Result Maps were found for the Mapped Statement 'mapper.UserMapper.getUser'. 'resultType' or 'resultMap' must be specified when there is no corresponding method.

查询语句补充

通过构造器,可以知道。

这里对应的id应该与调用的id一致。

故id继续设置:mapper.UserMapper.getUser

Builder的参数

1、Configuration 无需多解释

2、对应MappedStatement的id

3、SqlSource 类型,构建sql用

4、SqlCommandType类型,对应的sql命令类型

StaticSqlSource staticSqlSource = new StaticSqlSource(configuration, "select id,username from users where id = 1");
configuration.addMappedStatement(new MappedStatement
        .Builder(configuration, "mapper.UserMapper.getUser", staticSqlSource, SqlCommandType.SELECT)
        .build());

3.2、补充结果集

构造器中可以发现设置 resultMaps数组

这里跟随同一个id : mapper.UserMapper.getUser

由于设置值未知,先尝试建设一个空数据尝试

 List<ResultMapping> resultMappings = new ArrayList<>();
ResultMap resultMap = new ResultMap.Builder(configuration, "mapper.UserMapper.getUser", User.class, resultMappings).build();

3.3、整合运行

正常出结果

1,john_doe

D、整理信息

这里由 简单的查询语句开始引入记录

select id,username from users where id = ?

1、如何构建sqlSeesion?

1、需要利用配置的 Environment 来生成数据库连接。

2、需要执行特定的sql,故需要构造语句,则这里需要配置 MappedStatement

3、配置了MappedStatement ,要执行,执行要返回结果,故 ResultMap 就要补齐了。

4、如果需要动态参数,那么可以往 MappedStatement 里面加 ParameterMap

2、如何构建Environment 呢?

参考A、C两点,主要是通过这三种方式来构建基础内容

3、如何执行sql?

通过上述构建 sqlSession 的基础构建成 SqlSource, 交由 SimpleExecutor 执行处理。

这里补充一句,其实默认是 CachingExecutor,除非设置了cacheEnabled 为false。

可以从Configuration#newExecutor 看到

在这里插入图片描述
(图D-3-1)

4、怎么构建的sql

4.1、打开会话执行

如果开启会话时不加参数

默认会是 SimpleExecutor ,最终调用的是 DefaultSqlSession

SqlSession session = sqlMapper.openSession()
session.selectOne("mapper.UserMapper.getUser", 1)

4.2、构建SqlSession

根据 4.1 可知其实是由 DefaultSqlSession 去执行的

其实无论是 one 还是 list 其实最终都是list,只不过会给你加一波异常弹出.

在这里插入图片描述
(图D-4-4.2-1)

4.2.1、偏移量处理

这里默认是会给你拉爆的,所以还得是处理好。

!在这里插入图片描述
(图D-4-4.2-2)
在这里插入图片描述

(图D-4-4.2-3)

4.2.2、结果处理器

这里是可以针对结果处理的地方,这里用了默认值,其实就是null。

(图D-4-4.2-4)

4.2.3、获取 MappedStatement

这里结合图 (D-4-4.2-4)和图 (D-4-4.2-5)

实际上这里获取的时候是可以检验或比较严的

校验其实就是就是尝试去构建所有的语句避免get不到

Tips: 这里的id其实就是全局通用的一个id,用来绑定上下文中设定的语句

这里补充下:图 (D-4-4.2-7) 主要图例是 查询语句

构建完后,交回给executor

在这里插入图片描述

(图D-4-4.2-5)
在这里插入图片描述
(图D-4-4.2-6)
在这里插入图片描述
(图D-4-4.2-7)

4.3、补充下构建MappedStatement

下述的方法,都能以全局 id 维系一起。

也就是 getMappedStatementbuildAllStatements内容

参数解析
parsePendingResultMaps(true)解析结果映射(Result Maps)
parsePendingCacheRefs(true)解析缓存应用(Cache Refs)
parsePendingStatements(true)解析SQL 语句
parsePendingMethods(true)解析对应的方法

5、交给执行器处理

入口参考:图(D-4-4.2-4)

由D-3 可知,交由 CachingExecutor 去实现,除非于配置时

configuration.setCacheEnabled(false);

则继续进入图(D-5-1)描述 CachingExecutor

5.1、CachingExecutor

这里通常也是所描绘的二级缓存,专门用一个执行器处理。

但是是需要开启的,在 MappedStatment

# 由上至下
1、判断 `MappedStatement` 内是否存在缓存。

2、判断是否需要刷新缓存,默认是false的。

3、校验 `MappedStatement` 缓存标记且确保单纯的缓存不存在后续的结果处理,保证一定的幂等性。

4、校验是否存在调用函数的一些问题,如果有就会出异常,也是保护的一种方式
  参考图图(D-5-2)

3、校验 `TransactionalCacheManager` 内是否存在缓存若有返回,无则继续委托查询,而委托通常就是 `D-3`中的`SimpleExecutor`

在这里插入图片描述
(图D-5-5.1-1)

ensureNoOutParams

这里主要是防止一些调用函数出现在缓存中,避免执行到一些 超乎意料 的内容发生,通常是函数和存储语句

在这里插入图片描述
(图D-5-5.1-2)

5.2、SimpleExecutor

这里通常是满足如下其一

1、非缓存开启

2、缓存无法满足后继续委托

Tips:

1、值得注意的是,这里由于SimpleExecutor 并未实现query方法,还是回归到 BaseExecutor

2、通常执行器内都是依赖于sqlsession,也称之为 一级缓存

# 由上至下
## 
1、检测执行器是否被关闭

2、校验是否为顶层查询 并且配置了刷新缓存要求。(这里默认回是false)

3、校验是否存在结果处理器
 3.1、如果存在,则继续调用数据库。
 3.2、否则校验是否存在调用函数`handleLocallyCachedOutputParameters`
    尝试将参数防止到 执行器缓存内

4、然后尝试从数据库获取内容`queryFromDatabase`

在这里插入图片描述

(图D-5-5.2-1)

在这里插入图片描述

(图D-5-5.2-2)

6、执行器开始查数据

这里很简洁。

为缓存设置一个设定的key 作为执行标识。

1、针对本地缓存。

若执行完美,则为结果值;否则为空

2、针对回调出参类型,则会相应填入回调出参的缓存内。

在这里插入图片描述
(图D-6-1)

6.1、doQuery(可插入处理器的地方)

这里开始构建statement处理器。

Tips: 通常这个部分就是我们可以对statement魔改的位置

在这里插入图片描述

(图D-6-6.1-1)

6.1.1、RoutingStatementHandler

虽然有诸多实现类,但是最后也是通过 RoutingStatementHandler 来继续构建。

如图(D-6-6.1-3) 这里没有设置的话,其实就是 StatementType.PREPARED ,可以参考 MappedStatement#Builder

在这里插入图片描述
(图D-6-6.1-2)
在这里插入图片描述
(图D-6-6.1-3)

6.1.2、PreparedStatementHandler

1、通过驱动器获取连接内容

2、构建 Statement

在这里插入图片描述
(图D-6-6.2-1)

prepare

1、构建连接实例
这里由于设置是 JdbcTransactionFactory 且设置了PooledDataSource
因此这里会最终由
JdbcTransaction -> PooledDataSource -> 反射代理构建链接

2、这里承接 6.1.1 会是 PreparedStatementHandler 处理,由于并未实现,所以依旧是BaseStatementHandler 处理 跟随图(D-6-6.1-4)

2.1、为保错误能被记录的上下文设置。
2.2、instantiateStatement

  • 移步(图D-6-6.1-5)
    2.2.1、这里就是首先判断是否设置了自增长主键,如果设置了就进入。
    (1)设置了keycolumn则返回对应的主键
    (2)没有设置则会返回主键的值(如果非自增,这里会返回影响的行数)
    这里主要三个实现类:
    -1、NoKeyGenerator 没有主键,非插入语句& useGeneratedKeys 默认配置
    -2、Jdbc3KeyGenerator 可自增长的主键配置
    -3、SelectKeyGenerator 需要协助处理的并非db能自增长的配置

2.2.2、这里默认其实都是第一个,传入默认的语句即可。
具体是关于ResultSetType 结果集类型
2.3、设置 setStatementTimeout
设置超时时间
2.4、设置setFetchSize
设置获取的行数。

在这里插入图片描述
(图D-6-6.1-4)

在这里插入图片描述
(图D-6-6.1-5)

parameterize

Statement 进行参数化配置

由于 PreparedStatementHandler 并未自己来处理,所以都由 BaseStatementHandler 实现

1、获取全局的配置参数映射

这里可以看到开始配置的parameterMappings

2、这里排除非入参的参数,获取 此次sql所需的参数

这里取参数优先级 从上到下

2.1、 如果本来就有参数就直接采用 ,这里是提前赋值的参数

​ 这里可以为类比为 @param 注解对应的参数

2.2、对比是否为存在的typeHandlerRegistry 处理赋值。

2.3、如果是其他类型,则用其类型来获取值

3、设置值到类型sql对应的参数上。

Tips: 这里值得注意的是,若是没有?,最终会再交付到mysql 时触发异常

 Parameter index out of range (1 > number of parameters, which is 0).

在这里插入图片描述
(图D-6-6.1-6)

7、整理结果

回归到 图 (D-6-6.1-1),这里实际上还是继续调用 PreparedStatementHandler

1、ps.execute(); 其实已经交由mysql去执行,直接下一步

2、结果处理器,这里默认构建的时候都会创建一个DefaultResultSetHandler ,参考 BaseStatementHandler 构造器,是调用 Configuration#newResultSetHandler ,对应图(D7-2)

在这里插入图片描述
(图D-7-1)
在这里插入图片描述
(图D-7-2)

DefaultResultSetHandler

这里其实主要是两个方面

1、 mappedStatement.getResultMaps() 部分

主要是对结果集直接解析到对象中

2、 mappedStatement.getResultSets(); 部分

主要是处理嵌套结果集的,比如我们mybatis中,的结果套结果的部分

在这里插入图片描述
(图D-7-3)

7.1、getFirstResultSet

1、尝试获取结果集

getResultSet 这里也提示了 the current result as a ResultSet object or null if the result is an update count or there are no more results 可能存在没有结果集的情况

2、如果为空则尝试获取全部可能的结果集

尝试继续获取结果集

3、获取到结果集后对其赋值解析

参考图 (图D-7.1-2)

根据 columnNamesjdbcTypesclassNames,

按照数据的模式填入ResultSetWrapper 中暂存

在这里插入图片描述

(图D-7.1-1)

在这里插入图片描述

(图D-7.1-2)

7.2、validateResultMapsCount

继续从mappedStatement.getResultMaps() 中获取之前设置的结果映射

会从 数据库的结果 和 结果集映射中简单判断下是否设定了内容。

在这里插入图片描述

(图D-7.2-1)

7.3、handleResultSet

这里通常没有设置的情况下,会构建一个默认的 DefaultResultHandler ,里面的参数正是构建configuration的默认 DefaultObjectFactory

这里主要是构建一个 List 对象,将对象解析放入。

最后释放资源

在这里插入图片描述

(图D-7.3-1)

handleRowValues

简单直白,如果有嵌套结果的话继续判断下面

ensureNoRowBounds 确保没有行越界,也有有值,方便后续处理

checkResultHandler 确保有正确的结果处理器

否则直接

handleRowValuesForSimpleResultMap

数据库查询返回的行数据映射到一个简单的结果映射对象上,也就是构建的User 对象中

最后存储到处理器的 multipleResults 中存放起来

在这里插入图片描述

(图D-7.3-2)

7.4、cleanUpAfterHandlingResultSet

清空一些嵌套的结果集,主要是不影响后续的内容处理。

8、回到开始查询的位置

回到 图 (图D-6-1)

首先删除缓存内容,再将结果存放到 一级缓存。

最终递归返回到执行的开始。

在这里插入图片描述
(图D-8-1)

  • 26
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
将SSM框架中的MyBatis升级到MyBatis-Plus是可行的,可以实现共存。SSM框架由Spring、Spring MVC和MyBatis组成,而MyBatis-Plus是对MyBatis的增强扩展。下面将介绍如何将它们共存。 首先,需要将MyBatis升级到MyBatis-Plus。可以将MyBatis-Plus的依赖项添加到项目的pom.xml文件中,替换原有的MyBatis依赖。然后,需要对原有的MyBatis配置文件进行修改。MyBatis-Plus提供了一些方便的功能和特性,如自动填充、逻辑删除等,可以根据项目需求选择开启或关闭。 在SSM框架中,MyBatis-Plus可以与原有的Spring框架和Spring MVC框架完美共存。Spring框架负责管理和配置各种Bean,MyBatis-Plus可以与Spring框架一起使用,将其作为DAO层的组件进行管理。在Spring的配置文件中,可以将MyBatis-Plus的配置文件加入到配置中。 在Spring MVC框架中,可以继续使用原有的控制器、服务和视图解析器等组件。MyBatis-Plus可以与Spring MVC框架无缝集成,通过Spring MVC接收请求,然后调用MyBatis-Plus进行数据访问和处理。 在具体开发过程中,可以利用MyBatis-Plus提供的一些特性简化开发工作。例如,可以使用MyBatis-Plus的代码生成器来自动生成DAO、实体类和Mapper等代码,减少手动编写的工作量。 总结来说,将SSM框架中的MyBatis升级到MyBatis-Plus是完全可以实现的,它们可以共存并完美集成。通过使用MyBatis-Plus,我们可以更加便捷地开发和管理数据库操作,提高开发效率和代码质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值