2023-05-24
MyBatisx
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
关键对象
SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。 因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
in short 类似 Hibernate的SessionFactory / JPA的EntityManagerFactory 重量级对象, 应该在整个应用作用域, 保持同一个实例
SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。
in short 类似 Hibernate 的 Session / JPA 的 EntityManager, 但是JPA是java规范, mybatis 不是, 这SqlSession可不能作为类属性作用域
映射器实例 XxxxMapper
映射器是一些绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。虽然从技术层面上来讲,任何映射器实例的最大作用域与请求它们的 SqlSession 相同。但方法作用域才是映射器实例的最合适的作用域。
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// 你的应用逻辑代码
}in short 类似JPA的Repository + Mapper 处理逻辑的对象; 它这里需要定义sql语句, 接受对象不是强关联, 总的来说 MyBatis 是’封装SQL语句的框架更为贴切’
Get started
Maven 依赖
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
构建 SqlSessionFactory
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。 而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先 配置的 Configuration 实例 来构建出 SqlSessionFactory 实例。
全局配置 for xml
全局配置文件 mybatis-config.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属性】设置成不同的环境-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED"><!-- 数据源配置 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- mappers(映射器) -->
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);全局配置 for java code
参考 org.mybatis.spring.boot 的自动配置源码 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
以 java 代码的形式
PooledDataSource dataSource = new PooledDataSource();
dataSource.setDriver("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword ("123456");
dataSource.setUrl("jdbc:mysql://localhost:3306/inventory");
dataSource.setDefeultAutoCommit(false);
// 采用 MyBatis 的 JDBC 事务方式
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment ("development", transactionFactory, dataSource);
// //配置 参考xml
Configuration configuration = new Configuration(environment);
//一个java 接口, 使用注解定义, 见下
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
return SqlSessionFactory;
注意该例中,configuration 添加了一个映射器类(mapper class)。映射器类是 Java 类,它们包含 SQL 映射注解从而避免依赖 XML 文件
不过,由于 Java 注解的一些限制以及某些 MyBatis 映射的复杂性,要使用大多数高级映射(比如:嵌套联合映射),仍然需要使用 XML 配置 有鉴于此,如果存在一个同名 XML 配置文件,MyBatis 会自动查找并加载它(在这个例子中,基于类路径和 BlogMapper.class 的类名,会加载 BlogMapper.xml)
定义 Mapper
for xml
MyBatis 的真正强大在于它的语句映射, 参考 MyBatis之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="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>在命名空间 “org.mybatis.example.BlogMapper” 中定义了一个名为 “selectBlog” 的映射语句,这样你就可以用全限定名 “org.mybatis.example.BlogMapper.selectBlog” 来调用映射语句了,就像上面例子中那样:
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
//类似的还有
// org.apache.ibatis.session.SqlSession#selectList
// org.apache.ibatis.session.SqlSession#selectMap
// org.apache.ibatis.session.SqlSession#selectCursor / 还可以拿游标for java 注解
一个映射器类就是一个仅需声明与 SqlSession 方法相匹配方法的接口。
public interface BlogMapper {
@Select("select * where id = #{id}")
Map selectBlog(long id);
@Select("select * from user where id = #{id}")
User findById(@Param("id") long id);
@Select("select * from user where name = #{name}")
User findByName(@Param("name") String name);
@Select("select * from user where email = #{email}")
User findByEmail(@Param("email") String email);
}扫描 “Mapper” 的方式
https://mybatis.org/spring/zh/mappers.html#scan
定义扫描 Mappr 的方式 不需要一个个地注册你的所有映射器。你可以让 MyBatis-Spring 对类路径进行扫描来发现它们。
有几种办法来发现映射器:
使用 <mybatis:scan/> 元素
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
<mybatis:scan base-package="org.mybatis.spring.sample.mapper" />
<!-- ... -->
</beans>使用 @MapperScan 注解
当你正在使用 Spring 的基于 Java 的配置时(也就是 @Configuration),相比于使用 <mybatis:scan/>,你会更喜欢用
@MapperScan 注解的使用方法如下:
@Configuration
@MapperScan("org.yang.mybits.mapper")
public class ...<mybatis:scan/> 和 @MapperScan 都在 MyBatis-Spring 1.2.0 中被引入。@MapperScan 需要你使用 Spring 3.1+。
在 Spring中 MapperScannerConfigurer 定义
使用经典 Spring XML 配置文件中注册一个 MapperScannerConfigurer
MapperScannerConfigurer 是一个 BeanDefinitionRegistryPostProcessor,这样就可以作为一个 bean,包含在经典的 XML 应用上下文中。为了配置 MapperScannerConfigurer,使用下面的 Spring 配置:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.mybatis.spring.sample.mapper" />
</bean>MapperScannerConfigurer 这个对象可以设置搜索指定 basePackage (包)的 指定 annotationClass(注解) 注册为Mapper
basePackage:指定要扫描的包的基础包路径,通常是 Mapper 接口所在的包。annotationClass:指定要用于标识 Mapper 接口的注解,通常是@Mapper。markerInterface:指定一个标记接口,只有继承该接口的接口才会被扫描并注册为 Mapper 接口。sqlSessionFactoryBeanName:指定要使用的 SqlSessionFactory 的 Bean 名称,用于配置多个 SqlSessionFactory 的情况。sqlSessionTemplateBeanName:指定要使用的 SqlSessionTemplate 的 Bean 名称,用于配置多个 SqlSessionTemplate 的情况。
踩坑指南
<!> 使用 @MapperScan 注解, 会与默认的扫描@Mapper注册方式互斥!
Springboot会从Spring.factories这个配置文件中预加载一些类并缓存起来。在扫描的时候会将包下的符合条件的bean扫描并注册到容器中,然后将预加载的bean也注册到容器中。
这里面涉及一个先后问题,如果在bean上标记了@MapperScan那么这个类肯定是排在预加载之前,因为在处理的过程中首先是从包扫描中发现的类进行处理,然后再是spring.factories中的类。(这边知道就好了)那么在处理完@MapperScan后 会将引入这个类的类加入缓存中,用来甄别后面是否有重复的,那么就是在这将后面@Mapper处理的时候就被过滤掉了
使用 SqlSession
SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
更简洁的方式——使用和指定语句的参数和返回值相匹配的接口(比如 BlogMapper.class),现在你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
}在之前版本的 MyBatis 中,命名空间(Namespaces)的作用并不大,是可选的。 但现在,随着命名空间越发重要,你必须指定命名空间。
全局配置
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
configuration(配置)
properties(属性)
settings(设置)// MyBatis 中极为重要的调整设置, settings标签用来设置全局参数,大多数情况下,这些参数使用它们的默认值即可
typeAliases(类型别名)//类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
typeHandlers(类型处理器)//MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。
objectFactory(对象工厂)//每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。
plugins(插件)//MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。
environments(环境配置)//MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中 //每个 SqlSessionFactory 实例只能选择一种环境。
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)//MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。
mappers(映射器)//定义 SQL 映射语句, 告诉 MyBatis 到哪里去找到这些语句配置 settings(设置)
配置 typeAliases(类型别名)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>Java 类型别名
常见的 Java 类型内建的类型别名。它们都是不区分大小写的
| 别名 | 映射的类型 |
|---|---|
| _byte | byte |
| _long | long |
| _short | short |
| _int | int |
| _integer | int |
| _double | double |
| _float | float |
| _boolean | boolean |
| string | String |
| byte | Byte |
| long | Long |
| short | Short |
| int | Integer |
| integer | Integer |
| double | Double |
| float | Float |
| boolean | Boolean |
| date | Date |
| decimal | BigDecimal |
| bigdecimal | BigDecimal |
| object | Object |
| map | Map |
| hashmap | HashMap |
| list | List |
| arraylist | ArrayList |
| collection | Collection |
| iterator | Iterator |
配置 typeHandlers(类型处理器)
配置 plugins(插件)
配置 environments(环境配置)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。
为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。可以接受环境配置的两个方法签名是:
String environment = '指定环境ID'
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>注意一些关键点:
- 默认使用的环境 ID(比如:default=“development”)。
- 每个 environment 元素定义的环境 ID(比如:id=“development”)。
- 事务管理器的配置(比如:type=“JDBC”)。
- 数据源的配置(比如:type=“POOLED”)。 默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。
缓存机制
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。
一级缓存 是针对 SqlSession,可以理解为是一个连接(同一个数据库连接下执行的查询共用一块缓存) 二级缓存 是针对namespace,也就是一个Mapper.xml文件,一个文件内的查询共用一块缓存,多个namespace缓存之间相互不受影响
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存 (一级缓存)。 要启用全局的二级缓存
基本上就是这样:
- 映射语句文件中的所有 select 语句的结果将会被缓存
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改
可以通过 cache 元素的属性来修改。比如:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>缓存只作用于 cache 标签所在的映射文件中的语句。 如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用
@CacheNamespaceRef注解指定缓存作用域。
清除策略
可用的清除策略有:
- LRU – 最近最少使用:移除最长时间不被使用的对象(默认)。
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
- WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
日志
Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
- SLF4J
- Apache Commons Logging
- Log4j 2
- Log4j
- JDK logging
MyBatis 内置日志工厂会基于运行时检测信息选择日志委托实现。它会(按上面罗列的顺序)使用第一个查找到的实现。当没有找到这些实现时,将会禁用日志功能。
so.. 常用的日志框架都适配了
手动指定
<configuration>
<settings>
...
<setting name="logImpl" value="LOG4J"/>
...
</settings>
</configuration>可选的值有:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING,或者是实现了 org.apache.ibatis.logging.Log 接口,且构造方法以字符串为参数的类完全限定名。
通用 Mapper 插件 (tk.mybatis.mapper)
使用Mybatis的开发者,大多数都会遇到一个问题,就是要写大量的SQL在xml文件中,除了特殊的业务逻辑SQL之外,还有大量结构类似的增删改查SQL。 而且,当数据库表结构改动时,对应的所有SQL以及实体类都需要更改。 这时,通用 Mapper 便应运而生……
配置
<?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>
<!-- 配置插件 -->
<plugins>
<!-- 自定义插件 -->
<plugin interceptor="com.lagou.plugin.MyPlugin">
<property name="name" value="123"/>
</plugin>
<!--分页插件:如果有分页插件,要排在通用mapper之前-->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 设置分页方言 -->
<property name="dialect" value="mysql"/>
</plugin>
<!-- 引入通用 mapper -->
<plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
<!-- 通用Mapper接口,多个通用接口用逗号隔开 -->
<property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
</plugin>
</plugins>
...
TODO
iBatis, Mybatis 和 Mybatis-Plus 傻傻分不清?
iBatis
iBATIS 一词来源于 “internet” 和 “abatis” 的组合,是一个基于 Java 的持久层框架。
- iBatis 最早由Apache创建,后来成为Apache Software Foundation的一个项目。它是一个数据映射框架,用于将Java对象映射到关系型数据库表。
- iBatis的核心思想是通过XML映射文件来定义SQL查询和映射规则,从而实现数据库操作。这种方式使得数据库查询和映射规则与Java代码分离,便于维护。
MyBatis
MyBatis 实际上是iBatis的继任者。它最初称为iBatis SQL Maps,但在2010年更名为MyBatis。
- MyBatis在iBatis的基础上进行了改进和扩展,以提供更多功能和性能优化。它仍然使用XML映射文件来定义SQL查询和映射规则,但也支持注解方式。
- MyBatis具有很强的可配置性,使得开发者可以轻松定制SQL查询和映射规则,同时提供了很好的性能。 https://mybatis.net.cn/
MyBatis-Plus
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
- MyBatis-Plus 是对MyBatis的一个增强扩展,旨在简化数据库操作和提供更多便利功能。
- MyBatis-Plus提供了一组实用的API,可以轻松执行诸如分页查询、条件查询、批量插入和更新等常见数据库操作。这些功能通过简单的方法调用而不是复杂的手动编写SQL来实现。
- MyBatis-Plus还提供了一种代码生成器,可以自动生成实体类、Mapper接口以及XML映射文件,加速开发过程。
https://www.baomidou.com/pages/24112f/
in short iBatis是最早的数据映射框架,MyBatis是其继承者,而MyBatis-Plus是对MyBatis的增强扩展