MyBatis
文章目录
1. 简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
2. 使用流程
- 导入依赖
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
</dependencies>
- 定义 mapper类及对应 xml 文件
public interface StuMapper {
Stu selectOne(Integer id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.mapper.StuMapper">
<select id="selectOne" resultType="com.mybatis.pojo.Stu">
select * from stu where id = #{args0}
</select>
</mapper>
- 配置 MyBatis 配置文件
<?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="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://localhost:3306/niuke"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/StuMapper.xml"/>
</mappers>
</configuration>
- 运行和测试
@Test
public void tryTest() throws IOException {
String mybatisConfigFilePath = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(mybatisConfigFilePath);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sessionFactory.openSession();
StuMapper stuMapper = session.getMapper(StuMapper.class);
Stu stu = stuMapper.selectOne(1);
System.out.println("stu = " + stu);
session.commit(); //提交事务 [DQL不需要,其他需要]
session.close(); //关闭会话
}
3. 基本使用
3.1 配置文件
MyBatis配置文件设计标签和顶层结构如下:配置 MyBatis 配置文件时需按如下顺序及层级配置
- configuration(配置)
- properties(属性):用于配置属性,一般指定
resource
路径直接读取文件 - settings(设置):配置 MyBatis 缓存,主键,日志
logImpl
,驼峰命名mapUnderscoreToCamelCase
- typeAliases(类型别名):一般指定包路径,则会包下类都会以首字母小写的非全限定类名作为别名,部分常用类已经定义了别名,如 byte、long 等
- typeHandlers(类型处理器):java类型与数据库数据类型转换使用,一般使用默认的即可
- objectFactory(对象工厂):默认即可。每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。
- plugins(插件):较常使用的即
pageHelper
分页插件 - environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器):一般用 “JDBC”
- dataSource(数据源):一般类型用 “POOLED”
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器):指定 xml 文件位置,一般采取
package
的方式
- properties(属性):用于配置属性,一般指定
上面各字段配置具体可参考官网,很详细 https://mybatis.net.cn/configuration.html
3.2 SQL语句传参
#{}
形式传参:Mybatis会将SQL语句中的#{}
转换为问号占位符,一般用该形式,可以防止SQL注入${}
形式传参:底层Mybatis做的是字符串拼接操作,当上面格式无法使用时,才使用该格式,如表名、列名或关键字
3.3 数据输入
这里数据输入具体是指上层方法(例如Service方法)调用Mapper接口时,数据传入的形式。
- 简单类型:基础数据类型及其包装类,还有 String
- 复杂类型:除简单数据类型以为的类,如实体类,集合,数组,复合类型
3.3.1 单个简单类型参数
单个简单类型参数,在 #{}
中可以随意命名,但是没有必要。通常还是使用和接口方法参数同名。
Stu selectOne(Integer id);
<select id="selectOne" resultType="com.mybatis.pojo.Stu">
select * from stu where id = #{id}
</select>
3.3.2 多个简单类型数据
多个简单类型参数,如果没有特殊处理,那么Mybatis无法识别自定义名称:故一般需使用 @Param
注解,指定名称
List<Stu> selectList(@Param("name") String name, @Param("age") Integer age);
<select id="selectList" resultType="com.mybatis.pojo.Stu">
select * from stu where name = #{name} and age = #{age}
</select>
当然,也可使用 arg0, arg1 ... argn
或 param1, param2 ... paramn
表示第 1 , 2 ... n
个参数
3.3.3 实体类类型参数
Mybatis会根据 #{}
中传入的数据,加工成 getXxx()
方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到 #{}
解析后的问号占位符这个位置。
int insert(Stu stu);
<insert id="insert">
insert into stu(name, age) values(#{name}, #{age})
</insert>
3.3.4 Map类型参数
#{}
中写Map中的key 即可。
int update(Map<String, Object> map);
<update id="update">
update stu set name = #{name} where id = #{id}
</update>
3.4 数据输出
数据输出总体上有两种形式:
- 增删改操作返回的受影响行数:直接使用
int
或long
类型接收即可 - 查询操作的查询结果
3.4.1 resultType指定输出类型
使用 resultType
指定查询返回值类型即可,resultType="全限定符 | 别名 | 集合类型写泛型类型"
int selectCount();
<select id="selectCount" resultType="int">
select count(*) from stu
</select>
关于别名:https://mybatis.net.cn/configuration.html#typeAliases ,可在配置文件中配置别名包扫描,同时MyBatis也为常见的Java类型内建了一些类型别名。
3.4.2 返回实体类对象注意点
- 如果 Java 类型属性值与数据库字段名称不符,可通过 sql 语句的 as 别名,将名字改为一致;也可使用
resultMap
属性,配置对应resultMap
- 通过配置
mapUnderscoreToCamelCase=true
将自动映射数据库带下划线名称的字段,映射上 Java 实体中首字母小写的驼峰式命名 - 关于实体类属性的约定:
getXxx()
方法、setXxx()
方法把方法名中的get
或set
去掉,首字母小写
resultMap定义及使用:
<resultMap id="stuMap" type="com.mybatis.pojo.Stu">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="age" column="age" />
</resultMap>
<select id="selectOne" resultMap="stuMap">
select * from stu where id = #{id}
</select>
3.4.3 返回主键值
- 返回自增类型主键 :使用
useGeneratedKeys="true" keyProperty="id"
属性
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into stu(name, age) values(#{name}, #{age})
</insert>
- 返回非自增长类型主键 :使用
<selectKey>
标签
<insert id="insertUser" parameterType="User">
<selectKey keyProperty="id" resultType="java.lang.String" order="BEFORE">
SELECT UUID() as id
</selectKey>
INSERT INTO stu (id, username, age) VALUES ( #{id}, #{username}, #{age} )
</insert>
3.5 mapper.XML标签总结
select 元素允许配置很多属性来配置每条语句的行为细节
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
resultType | 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
resultMap | 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType | 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
insert, update 和 delete标签:
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType | 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys | (仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。 |
keyProperty | (仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset )。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn | (仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
4. 多表映射
关联关系 | 配置项关键词 | 所在配置文件和具体位置 |
---|---|---|
对一 | association标签/javaType属性/property属性 | Mapper配置文件中的resultMap标签内 |
对多 | collection标签/ofType属性/property属性 | Mapper配置文件中的resultMap标签内 |
对一映射:使用 <association>
标签,property
属性指定属性名,javaType
指定对应类型
@Data
public class Order {
private Integer orderId;
private String orderName;
private Customer customer;// 对一的关系
}
<resultMap id="selectOrderWithCustomerResultMap" type="order">
<id column="order_id" property="orderId"/>
<result column="order_name" property="orderName"/>
<!-- 使用association标签配置“一对一”关联关系 -->
<!-- property属性:在Order类中对一的一端进行引用时使用的属性名 -->
<!-- javaType属性:一的一端类的全类名 -->
<association property="customer" javaType="customer">
<!-- 配置Customer类的属性和字段名之间的对应关系 -->
<id column="customer_id" property="customerId"/>
<result column="customer_name" property="customerName"/>
</association>
</resultMap>
对多映射:使用 <collection>
标签,property
属性指定属性名,ofType
指定泛型类型
@Data
public class Customer {
private Integer customerId;
private String customerName;
private List<Order> orderList;// 对多的关系
}
<resultMap id="selectCustomerWithOrderListResultMap"type="customer">
<id column="customer_id" property="customerId"/>
<result column="customer_name" property="customerName"/>
<!-- collection标签:映射“对多”的关联关系 -->
<!-- property属性:在Customer类中,关联“多”的一端的属性名 -->
<!-- ofType属性:集合属性中元素的类型 -->
<collection property="orderList" ofType="order">
<!-- 映射Order的属性 -->
<id column="order_id" property="orderId"/>
<result column="order_name" property="orderName"/>
</collection>
</resultMap>
多表映射优化:设置 autoMappingBehavior
属性为 FULL
setting属性 | 属性含义 | 可选值 | 默认值 |
---|---|---|---|
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
将 autoMappingBehavior
设置为 full
,进行多表 resultMap
映射的时候,可以省略符合列和属性命名映射规则(列名=属性名,或者开启驼峰映射也可以自定映射)的 result
标签
修改 mybati-sconfig.xml
:
<!--开启resultMap自动映射 -->
<setting name="autoMappingBehavior" value="FULL"/>
<resultMap id="selectCustomerWithOrderListResultMap"type="customer">
<id column="customer_id" property="customerId"/>
<!-- 开启自动映射,并且开启驼峰式支持,可以省略 result -->
<!-- <result column="customer_name" property="customerName"/> -->
<collection property="orderList" ofType="order">
<id column="order_id" property="orderId"/>
<!-- <result column="order_name" property="orderName"/> -->
</collection>
</resultMap>
5. 动态语句
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
where
标签:where
标签结合if
标签使用会自动去掉“标签体内前面多余的and/or
“if
标签:有选择的加入SQL语句,当判断结果为 true 时才执行该标签内语句set
标签:使用set标签动态管理set子句,标签内会自动去掉两端多余的逗号choose/when/otherwise
标签:类似于 Java中的switch
语句,用choose
包裹逻辑判断代码,根据条件从上到下判断,执行第一个满足条件when
的分支,没有则执行otherwise
foreach
标签:遍历集合,批量插入或根据一些条件查询经常用到,批量更新需要注意在jdbc.url
添加allowMultiQueries=true
参数collection
属性:要遍历的集合item
属性:遍历集合的过程中的每一个具体对象separator
属性:指定当foreach
标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符open
属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串close
属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串index
属性:这里起一个名字,便于后面引用。遍历List集合,这里能够得到List集合的索引值;遍历Map集合,这里能够得到Map集合的key
sql
和include
抽取重复 sql 片段:用sql
标签定义 sql 片段,include
标签引用 sql 片段
6. 配置文件批量映射优化
在 3.1 中配置 mappers 映射器时采用 package
方法,这个包下所有 Mapper 配置文件将被自动加载、注册。
<mappers>
<package name="com.mybatis.mapper"/>
</mappers>
要求 Mapper 接口和 Mapper 配置文件名称一致,且 Mapper 接口与 Mapper 配置文件编译后的文件都在指定包下
7. 插件机制及分页插件PageHelper
7.1 插件机制
MyBatis 对插件进行了标准化的设计,并提供了一套可扩展的插件机制。插件可以在用于语句执行过程中进行拦截,并允许通过自定义处理程序来拦截和修改 SQL 语句、映射语句的结果等。
具体来说,MyBatis 的插件机制包括以下三个组件:
Interceptor
(拦截器):定义一个拦截方法intercept
,该方法在执行 SQL 语句、执行查询、查询结果的映射时会被调用。Invocation
(调用):实际上是对被拦截的方法的封装,封装了Object target
、Method method
和Object[] args
这三个字段。InterceptorChain
(拦截器链):对所有的拦截器进行管理,包括将所有的 Interceptor 链接成一条链,并在执行 SQL 语句时按顺序调用。
插件的开发非常简单,只需要实现 Interceptor 接口,并使用注解 @Intercepts
来标注需要拦截的对象和方法,然后在 MyBatis 的配置文件中添加插件即可。
7.2 分页插件PageHelper
详细文档可看:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
这里提供基本使用参考:
- 导入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
- MyBatis配置文件中配置分页插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
- 分页插件的使用
PageHelper.startPage(2, 4);
PageInfo<Stu> pageInfo = new PageInfo<>(stuMapper.selectPage()); // List<Stu> selectPage();
System.out.println(pageInfo);
8. 逆向工程
MyBatis 的逆向工程是一种自动化生成持久层代码和映射文件的工具,它可以根据数据库表结构和设置的参数生成对应的实体类、Mapper.xml 文件、Mapper 接口等代码文件,简化了开发者手动生成的过程。逆向工程使开发者可以快速地构建起 DAO 层,并快速上手进行业务开发。
MyBatis 的逆向工程有两种方式:通过 MyBatis Generator 插件实现和通过 Maven 插件实现。无论是哪种方式,逆向工程一般需要指定一些配置参数,例如数据库连接 URL、用户名、密码、要生成的表名、生成的文件路径等等。
注意:逆向工程只能生成单表crud的操作,多表查询依然需要我们自己编写
这里提供一种使用插件实现逆向工程的方法:逆向工程插件MyBatisX
- 首先在 idea 中下载并安装插件,注意要 apply 才能生效
- 配置 idea database,在idea的右侧
- 选中要逆向的表进行逆向,可选多个
- 按需求填写并最后生成即可