MyBatis 是一个持久层框架,用于简化 Java 应用程序与数据库之间的交互过程。具体而言,它提供了一种将数据库操作映射到 Java 方法的方式,通过 XML 文件或注解配置 SQL 语句与 Java 方法的映射关系。使用 MyBatis,开发人员可以通过简单的配置文件或注解来管理 SQL 语句,而不需要直接编写 JDBC 代码。
MyBatis的执行流程
流程图如下:
- 读取MyBatis配置文件,如mybatis-config.xml(该文件包含了 MyBatis 的全局配置信息,如数据库连接信息、缓存配置、映射文件的路径等)加载运行环境;映射文件,如XxxMapper,xml(该文件包含了 SQL 语句与 Java 方法的映射关系,以及结果映射等配置)。
- 构造会话工厂SqlSessionFactory, 它是 MyBatis 的核心接口之一,它负责创建 SqlSession 对象,SqlSession 是 MyBatis 中执行 SQL 的主要接口。
- 会话工厂创建SqlSession对象(包含了执行SQL语句的所有方法),它SqlSession 提供了执行 SQL 语句、获取映射器(Mapper)等功能。
- 操作数据库的接口,Executor执行器,同时负责查询缓存的维护,通过 SqlSession 执行 SQL 语句,可以是预定义的 SQL 语句,也可以是动态生成的 SQL 语句。
- Executor接口的执行方法中有一个MappedStatement类型的参数,封装了映射信息,因此,MyBatis 会根据 SQL 语句的配置,进行参数绑定、结果映射等操作。
- 关闭 SqlSession,在完成数据库操作后,应用程序需要关闭 SqlSession,释放数据库连接等资源。通常情况下,可以通过 try-with-resources 或手动调用 close() 方法来关闭 SqlSession。
MyBatis的延迟加载
延迟加载(Lazy Loading) 是一种软件设计模式,用于推迟对象或数据的加载直到真正需要使用它们的时候。在软件开发中,延迟加载通常用于优化性能和资源利用,避免不必要的开销和提高用户体验。
在数据库访问中,延迟加载通常指的是将某些数据的加载推迟到需要访问这些数据的时候才进行,而不是在查询时立即加载所有数据。这样做可以减少数据库查询次数,降低系统负载,提高性能。延迟加载通常应用于复杂对象关系的情况,其中某些关联数据不一定需要在每次查询中都被加载,而是根据需要进行加载。
例如:
Mybatis支持延迟记载,但默认是关闭的,可以在MyBartis的配置文件中,通过lazyLoadingEnabled=true|false进行设置。
MyBatis 支持两种类型的延迟加载:
-
关联对象延迟加载:
- 当一个对象关联了其他对象(一对一、一对多等关系),并且配置为延迟加载时,MyBatis 会在访问该关联对象时才真正执行相关的查询操作,将关联对象加载到内存中。
-
集合延迟加载:
- 当一个对象关联了一个集合对象(一对多关系),并且配置为延迟加载时,MyBatis 会在访问该集合对象时才真正执行相关的查询操作,将集合加载到内存中。
延迟加载的实现原理通常是使用代理对象,在对象的属性被访问时触发数据库查询。它的配置通常在 MyBatis 映射文件(XxxMapper.xml)中进行,可以通过以下方式实现延迟加载:
<association property="associationProperty" select="selectStatement" fetchType="lazy" />
<collection property="collectionProperty" select="selectStatement" fetchType="lazy" />
其中,association
和 collection
标签用于指定延迟加载的对象或集合,select
属性指定了延迟加载时执行的查询语句,fetchType="lazy"
则指定了延迟加载的方式为懒加载。
需要注意的是,延迟加载必须依赖于外部事务或连接,因此在延迟加载期间,相关的数据库连接必须保持打开状态。在使用延迟加载时,开发人员需要谨慎处理事务的管理,以避免可能的资源泄露或数据不一致。
延迟加载的原理:
- 使用CGLIB创建目标对象的代理对象
- 当调用目标方法user.getOrderList()时,进入拦截器invoke方法,发现user.getOrderList()是null值,执行sql查询order列表
- 获取到数据后,然后调用user.setOrderList(List orderList) ,接着完成user.getOrderList()方法的调用
MyBatis的缓存
MyBatis 提供了一级缓存和二级缓存两种缓存机制,用于提高数据库访问性能。这些缓存可以在适当的情况下存储已经查询过的数据,以避免重复查询数据库,提高系统性能。
一级缓存是 MyBatis 默认开启的,它是基于 SqlSession 对象的缓存,也就是说,只有在同一个 SqlSession 内执行的查询可以共享一级缓存。一级缓存存储在 SqlSession 的生命周期内,当调用 commit()
、close()
、clearCache()
等方法时会清空一级缓存。
一级缓存的特点包括:
- 作用范围:只在当前 SqlSession 内有效,不同的 SqlSession 之间不共享缓存。
- 生命周期:与 SqlSession 的生命周期一致,当 SqlSession 关闭时缓存也会失效。
- 自动管理:MyBatis 会自动管理一级缓存,无需手动配置。
二级缓存是一个跨 SqlSession 的缓存,它可以被多个 SqlSession 共享,即它的作用域是namespace和mapper的作用域,不依赖于session。当多个 SqlSession 查询相同的数据时,如果开启了二级缓存,则第一个查询结果会被缓存起来,后续的查询可以直接从缓存中获取数据,而不需要再次查询数据库。
二级缓存的特点包括:
- 作用范围:跨 SqlSession,可以被多个 SqlSession 共享。
- 生命周期:缓存在整个应用程序的生命周期内有效,需要手动配置并在需要时开启。
- 需要序列化支持:由于二级缓存需要跨 SqlSession 进程共享数据,因此缓存的数据需要支持序列化。
配置二级缓存:
-
在 MyBatis 的配置文件中开启二级缓存:
<settings> <setting name="cacheEnabled" value="true"/> <\settings>
-
在映射文件中配置需要使用二级缓存的语句:
<mapper namespace="com.example.mapper.UserMapper"> <cache/> <!-- 或者在具体的 select 语句上添加 <cache/> 标签 --> </mapper>
MyBatis 的缓存机制能够有效地提高数据库访问性能,一级缓存适用于单个 SqlSession 内的缓存需求,而二级缓存适用于多个 SqlSession 之间共享数据的缓存需求。
注意:
- 对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了新增、修改、删除操作后,默认该作用域下所有 select 中的缓存将被 clear
- 二级缓存需要缓存的数据实现Serializable接口
- 只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中