配置文件加载与解析
- 加载顺序:
- MyBatis 首先会加载核心配置文件(mybatis - config.xml)。这个文件是整个 MyBatis 配置的基础,它包含了数据库连接相关的全局配置、插件配置等重要信息。因为这些全局配置信息对于后续的操作(如创建数据源、配置事务管理器等)是必需的,所以它最先被加载。
- 在解析核心配置文件过程中,当遇到
标签时,MyBatis 会根据其中的配置来加载映射文件(mapper.xml)。这些映射文件用于定义具体的 SQL 语句以及 SQL 执行结果与 Java 对象之间的映射关系,它们的加载是基于核心配置文件的指引进行的。 - 核心配置文件(mybatis - config.xml):MyBatis 工作的第一步是加载核心配置文件。这个文件犹如整个框架的蓝图,涵盖数据库连接信息、事务管理配置、插件配置等关键部分。例如,以下是核心配置文件片段:
其中,
- 映射文件(mapper.xml):MyBatis 会根据核心配置文件的指引加载映射文件。映射文件的主要功能是定义 SQL 语句,并建立 SQL 执行结果与 Java 对象的映射,或者将 Java 对象属性作为 SQL 参数。以简单的UserMapper.xml为例:
这里定义了selectUserById查询语句,从users表按id查询用户信息。
构建 SqlSessionFactory
- 加载解析配置文件的关键角色:SqlSessionFactory 在 MyBatis 工作流程中扮演着至关重要的角色,它会完成全局配置文件和映射文件的加载解析操作。这一过程是整个框架能够正常运行的基础。
- 解析配置信息:在加载配置文件后,MyBatis 通过解析这些信息构建 SqlSessionFactory。这是 MyBatis 的核心对象,线程安全,用于创建 SqlSession。构建过程中,会解析数据库连接信息、映射文件中的 SQL 和映射关系等,封装成内部数据结构,方便后续操作。
解析全局配置文件(mybatis - config.xml):
- 数据库连接环境配置:在全局配置文件中,SqlSessionFactory会解析
标签下的内容,包括事务管理器类型(如JDBC或MANAGED)和数据源类型(如POOLED、UNPOOLED或JNDI)等。以数据源配置为例,它会读取 标签中的属性,如数据库驱动名称(driver)、连接 URL(url)、用户名(username)和密码(password)等信息,这些信息将用于后续建立与数据库的连接。 - 插件配置:如果全局配置文件中配置了插件(
标签),SqlSessionFactory也会解析这些插件相关的信息。插件可以用于拦截 MyBatis 的执行过程,实现如分页、缓存等功能扩展。例如,对于一个分页插件,它会解析插件的类名、参数等信息,以便在合适的时候加载并应用插件功能。 - 缓存配置:在全局配置文件中还可以配置缓存相关信息。通过
标签可以开启二级缓存,例如 ,这里配置了缓存的淘汰策略(LRU,即最近最少使用)、刷新间隔(60000 毫秒)、缓存大小(512 个对象)以及是否只读(true表示只读)。这些配置参数会影响 MyBatis 的缓存行为,SqlSessionFactory在解析时会将这些缓存配置信息保存起来,用于后续创建缓存对象和管理缓存数据。 - 其他全局配置:还包括像设置全局的映射器启用或者禁用自动映射(
标签中的相关配置)等内容,SqlSessionFactory都会一一进行解析并保存这些配置信息,用于后续的数据库操作过程。
解析映射文件(mapper.xml):
- SQL 语句解析:对于每个被配置的映射文件,SqlSessionFactory会解析其中的 SQL 语句。这些 SQL 语句通过
- 结果映射解析(resultMap):resultMap是 MyBatis 中非常重要的一个概念,用于将查询结果的列与 Java 对象的属性进行映射。SqlSessionFactory会解析resultMap标签中的内容,包括
标签(用于标识主键映射)和 标签(用于普通列的映射)。例如,在一个resultMap定义中, 表示将查询结果中的user_id列映射到 Java 对象的id属性上,SqlSessionFactory会解析并记录这种映射关系,以便在执行查询操作后能够正确地构建 Java 对象并填充属性值。
构建内部数据结构保存配置信息:在完成全局配置文件和映射文件的加载解析后,SqlSessionFactory会将这些信息构建成内部的数据结构进行保存。这样在后续创建 SqlSession 对象并执行数据库操作时,就可以快速地获取所需的配置信息,如 SQL 语句内容、参数映射规则、结果映射规则以及数据库连接信息等,从而高效地执行数据库操作。例如,它可能会将解析后的 SQL 语句和映射关系存储在一个类似于映射表的数据结构中,其中key是 SQL 语句的id,value是包含 SQL 语句内容、参数映射和结果映射等信息的对象。这种方式使得在执行SqlSession的各种数据库操作方法时,可以快速地根据 SQL 语句id找到对应的完整配置信息并执行操作。
创建实例:利用 SqlSessionFactoryBuilder 来创建 SqlSessionFactory。通常读取配置文件流后,使用build方法构建。例如:
String resource = "mybatis - config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
先获取核心配置文件输入流,再构建 SqlSessionFactory。
MyBatis 缓存机制
一级缓存:
- 原理与范围:一级缓存是 SqlSession 级别的缓存。当 MyBatis 执行查询操作时,会先在一级缓存中查找是否已经存在相同查询的结果。如果存在,就直接返回缓存中的结果,而不会再次向数据库发送查询请求。这个缓存是基于PerpetualCache实现的,它是一个简单的基于HashMap的缓存。例如,在同一个SqlSession中执行两次相同的查询sqlSession.selectOne("com.example.mapper.UserMapper.selectUserById", 1);,第一次查询会从数据库获取数据,第二次查询就会从一级缓存中获取数据。
- 缓存失效情况:一级缓存会在以下几种情况下失效。一是当执行插入、更新或删除操作后,缓存会被清空,因为这些操作可能会改变数据库中的数据,导致之前缓存的查询结果不再准确。二是当SqlSession被关闭后,一级缓存也会被清除。
二级缓存:
- 原理与范围:二级缓存是 Mapper 级别的缓存,它的作用范围比一级缓存更广。它是基于Cache接口实现的,可以通过配置文件进行配置。当开启二级缓存后,MyBatis 会在不同的SqlSession之间共享缓存数据。例如,在一个多线程或者多个SqlSession操作的场景下,如果一个SqlSession执行了查询并将结果存入二级缓存,其他SqlSession执行相同的查询时就可以从二级缓存中获取数据。配置二级缓存通常在mapper.xml文件中添加
标签或者在MyBatis的全局配置文件中进行配置。 - 缓存配置参数:在配置二级缓存时,可以设置多种参数来控制缓存的行为。如前面提到的在全局配置文件中的
标签配置,eviction属性用于指定缓存的淘汰策略,常见的有LRU(最近最少使用)、FIFO(先进先出)等;flushInterval属性用于设置缓存的刷新间隔时间,单位是毫秒;size属性用于指定缓存的大小,即最多可以缓存多少个对象;readOnly属性用于设置缓存是否为只读。如果设置为true,则缓存中的对象是只读的,可以提高性能,因为 MyBatis 不需要对缓存对象进行序列化和反序列化操作,但如果缓存对象被修改,可能会导致数据不一致的问题。如果设置为false,则缓存对象是可读写的,MyBatis 会在必要时对缓存对象进行序列化和反序列化操作,以保证数据的一致性。 - 缓存数据的存储与读取:二级缓存的数据存储和读取是基于Cache接口的实现类完成的。当执行查询操作并且二级缓存开启时,MyBatis 会首先检查二级缓存中是否存在相应的数据。如果存在,就直接返回缓存数据;如果不存在,则执行查询操作从数据库获取数据,然后将数据存入二级缓存中,以便后续查询使用。在存储数据时,会根据缓存的配置参数(如淘汰策略、缓存大小等)来管理缓存数据。
缓存使用的注意事项:
- 缓存一致性问题:在使用缓存时,需要注意缓存数据与数据库数据的一致性。由于缓存的数据可能不是最新的,特别是在多用户并发操作或者数据库数据频繁更新的场景下。例如,如果一个用户更新了数据库中的数据,而缓存没有及时刷新,其他用户可能会获取到旧的缓存数据。为了避免这种情况,可以在执行更新操作后手动刷新缓存,或者根据缓存的配置参数(如设置合理的刷新间隔)来保证缓存数据的及时性。
- 缓存穿透、缓存雪崩等问题:缓存穿透是指查询一个不存在的数据,导致每次查询都直接穿透缓存访问数据库。缓存雪崩是指大量缓存同时失效,导致大量请求直接访问数据库,可能会造成数据库压力过大。为了避免这些问题,可以采用一些策略,如设置缓存空对象来解决缓存穿透问题(当查询不存在的数据时,在缓存中存储一个空对象,下次查询相同数据时直接返回空对象),以及合理设置缓存过期时间和采用分布式缓存来缓解缓存雪崩问题。
创建 SqlSession 与 StatementHandler 的关联
- 创建 SqlSession:SqlSession 是执行 SQL 操作的主要接口,通过 SqlSessionFactory 的openSession方法创建。例如:
SqlSession sqlSession = sqlSessionFactory.openSession();
注意,SqlSession 对象非线程安全,用于一次数据库事务或一系列操作,完成后应及时关闭释放资源。
- StatementHandler 的作用:StatementHandler 是 MyBatis 的四大核心处理器之一,它负责处理 SQL 语句的预编译和执行。在 SqlSession 执行 SQL 操作时,会创建一个 StatementHandler 对象来处理具体的 SQL 执行细节。它主要的职责包括对 SQL 语句进行语法解析、参数设置、结果集映射等操作。例如,在执行查询操作时,StatementHandler 会将 SQL 语句发送给数据库,接收返回的结果集,并根据配置的结果映射规则(如resultMap)将结果集映射为 Java 对象。
- 工作流程中的关联:当调用 SqlSession 的方法(如selectOne、selectList、insert、update或delete)执行 SQL 操作时,SqlSession 会根据配置和操作类型创建相应的 StatementHandler。具体来说,它会根据 SQL 语句是查询还是更新操作来选择不同的 StatementHandler 实现类。对于查询操作,通常会使用PreparedStatementHandler,它会将 SQL 语句进行预编译,提高执行效率;对于更新操作,可能会使用SimpleStatementHandler或者CallableStatementHandler,这取决于 SQL 语句的类型(是普通的更新语句还是存储过程调用)。在创建 StatementHandler 之后,SqlSession 会将相关的参数(如 SQL 语句的id、参数对象等)传递给 StatementHandler,然后由 StatementHandler 完成后续的 SQL 执行工作。
执行 SQL 操作与 StatementHandler 的具体操作
- SQL 执行过程与 StatementHandler:调用 SqlSession 方法执行 SQL 时,MyBatis 根据 SQL 语句id在映射文件中查找对应的 SQL。例如执行sqlSession.selectOne("com.example.mapper.UserMapper.selectUserById", 1);,会找到UserMapper.xml中id为selectUserById的查询语句,将参数1(假设是用户id)设到#{id}占位符。在这个过程中,StatementHandler 起到关键作用。它会获取这个 SQL 语句,将其进行预编译(如果是PreparedStatementHandler),并将参数1正确地设置到预编译后的 SQL 语句中,然后通过数据库驱动执行查询。
- 参数与结果映射中的 StatementHandler 职责:在执行 SQL 操作时,MyBatis 进行参数和结果映射。对于参数映射,StatementHandler 会按照规则(如#{}占位符)将 Java 对象属性值传递给 SQL 参数。例如,它会解析 SQL 语句中的参数占位符,根据参数的类型和值,将其正确地设置到预编译的 SQL 语句中。对于结果映射,如resultMap定义,StatementHandler 会接收查询结果集,将查询结果列值映射到 Java 对象属性。它会根据配置的resultMap信息,识别列名和 Java 对象属性的对应关系,然后将结果集中的数据填充到 Java 对象的相应属性中。
- StatementHandler 的可扩展性和插件机制:StatementHandler 的设计具有良好的可扩展性,这使得 MyBatis 可以通过插件机制来对 SQL 执行过程进行拦截和扩展。例如,可以编写一个插件来实现分页功能。插件可以在 StatementHandler 执行 SQL 之前,修改 SQL 语句(如添加LIMIT子句用于分页),或者在执行之后对结果集进行处理(如对返回的列表数据进行二次筛选)。这种插件机制基于 MyBatis 的拦截器(Interceptor)接口,通过实现拦截器并配置到 MyBatis 中,可以在 StatementHandler 的不同执行阶段(如prepare、parameterize、batch、update、query等阶段)进行拦截操作,从而实现各种功能扩展。
事务管理
- 开启与提交 / 回滚:如果openSession方法没传入false参数(如sqlSessionFactory.openSession(true)),默认开启自动提交事务的 SqlSession。如需手动控制事务,可用beginTransaction开启,一系列操作完成后用commit提交或rollback回滚。例如:
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
// 执行SQL操作
sqlSession.insert("com.example.mapper.UserMapper.insertUser", new User("newUser", "password"));
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
} finally {
sqlSession.close();
}
先开启 SqlSession,插入新用户,有异常则回滚,最后关闭。
资源清理
- 关闭 SqlSession:完成 SQL 操作和事务处理后,必须关闭 SqlSession 释放数据库连接等资源,通过调用close方法实现。不及时关闭可能导致数据库连接泄漏等问题。例如:
sqlSession.close();
本文暂时没有评论,来添加一个吧(●'◡'●)