MyBatis(下篇)
1.多表映射
**前言:**MyBatis 思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序查询需求,那就太好了,而 ResultMap 就是 MyBatis 就是完美答案。
**对一实体类设计思路:**在对一关系下,类中只要包含单个对方对象类型属性即可
**对多实体类设计思路:**在对多关系下,类中只要包含对方类型集合属性即可
只有真实发生多表查询时,才需要设计和修改实体类,否则不提前设计和修改实体类!
无论多少张表联查,实体类设计都是两两考虑!
在查询映射的时候,只需要关注本次查询相关的属性!例如:查询订单和对应的客户,就不要关注客户中的订单集合!
1.1对一查询
在resultMap中给对象属性赋值时,使用association标签进行赋值
property:对象属性的属性名
column:数据表中的列名
javaType:对象属性的类型的全限定符或全限定符的别名
association标签:对一关系映射标签
id标签:声明主键列的映射关系
result标签:声明非主键列的映射关系
sql
========================数据库sql语句准备===================================
CREATE TABLE `t_customer` (`customer_id` INT NOT NULL AUTO_INCREMENT, `customer_name` CHAR(100), PRIMARY KEY (`customer_id`) );
CREATE TABLE `t_order` ( `order_id` INT NOT NULL AUTO_INCREMENT, `order_name` CHAR(100), `customer_id` INT, PRIMARY KEY (`order_id`) );
INSERT INTO `t_customer` (`customer_name`) VALUES ('c01');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o1', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o2', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o3', '1');
java
=======================对应多表查询实体类设计================================
@Data
public class Customer {
private Integer customerId;
private String customerName;
private List<Order> orderList;// 体现的是对多的关系
}
@Data
public class Order {
private Integer orderId;
private String orderName;
private Customer customer;// 体现的是对一的关系
}
java
=======================Mapper接口方法的声明=================================
public interface OrderMapper {
Order selectOrderWithCustomer(Integer orderId);
}
XML
=====================Mapper.xml文件编写====================================
<?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.ergou.mapper.OrderMapper">
<!-- 创建resultMap实现"对一"关联关系映射 -->
<!-- id属性:通常设置为这个resultMap所服务的那条SQL语句的id加上"ResultMap" -->
<!-- type属性:要设置为这个resultMap所服务的那条SQL语句最终要返回的类型 -->
<resultMap id="selectOrderWithCustomerResultMap" type="order">
<!-- 先设置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>
<!-- Order selectOrderWithCustomer(Integer orderId); -->
<select id="selectOrderWithCustomer" resultMap="selectOrderWithCustomerResultMap">
SELECT order_id,order_name,c.customer_id,customer_name
FROM t_order o
LEFT JOIN t_customer c
ON o.customer_id=c.customer_id
WHERE o.order_id=#{orderId}
</select>
</mapper>
1.2对多映射
1.一对多使用collection子标签进行关联多方Order
<collection property="指定 Java 对象中集合属性的名称" javaType="指定集合本身的类型(如 `List`, `Set` 等)" ofType="指定集合中元素的类型" autoMapping="true"> </collection> 2.属性:
1)property="orders" 这里的orders表示User类的成员变量orders
2)javaType="List" 表示User类的成员变量orders存储的Order对象使用的类型,这里是List 一般不书写,通常,MyBatis 能够根据 ofType 自动推断出合适的 javaType
- ofType="Order" 表示List集合中存储数据的类型 Order 3.一定要记住这里给user表的id起别名是uid,order表的id起别名是oid.在resultMap标签的id子标签中的column属性值书写对应的uid和oid
XML
<!-- 配置resultMap实现从Customer到OrderList的"对多"关联关系 -->
<resultMap id="selectCustomerWithOrderListResultMap"
type="customer">
<!-- 映射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>
<!-- Customer selectCustomerWithOrderList(Integer customerId); -->
<select id="selectCustomerWithOrderList" resultMap="selectCustomerWithOrderListResultMap">
SELECT c.customer_id,c.customer_name,o.order_id,o.order_name
FROM t_customer c
LEFT JOIN t_order o
ON c.customer_id=o.customer_id
WHERE c.customer_id=#{customerId}
</select>
多表映射配置优化
setting属性 | 属性含义 | 可选值 | 默认值 |
---|---|---|---|
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
关联关系 | 配置项关键词 | 所在配置文件和具体位置 |
---|---|---|
对一 | ①association标签:对一关系映射标签②javaType属性:对象属性的类型③column属性:数据表中的列名④property属性:映射的属性名 | Mapper配置文件中的resultMap标签内 |
对多 | ①collection标签:对多关系映射标签②ofType属性:集合中存储的数据类型③column属性:数据表中的列名④property属性:映射的属性名 | Mapper配置文件的resultMap标签内 |
有用的mybatis配置设置
①logImpl:开启控制台的日志输出功能,可以看到后台执行的sql语句
②mapUnderscoreToCamelCase:自动开启驼峰式映射(不适合嵌套)
③autoMappingBehavior:自动映射字段和属性(适用多层嵌套)
④typeAliases:自动类型别名转换
XML
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="autoMappingBehavior" value="FULL"/>
</settings>
<typeAliases>
<package name="com.atguigu.pojo"/>
</typeAliases>
<typeAliases>
<typeAlias type="com.atguigu.pojo.Employee" alias="huermosi"/>
</typeAliases>
2.动态语句
前言 :动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
if和where标签
①if标签
可以让我们有选择的加入Sql语句的片段。
在if标签的test属性里面,可以访问实体类的属性,不可以访问数据库表的字段
若test标签里面内容为true则添加if标签里面的语句
②where标签
可以帮助我们自动去掉"标签里面多余的and/or",自动连接where关键字,防止报错
XML
<select id="queryByNameAndSalary" resultType="com.ergou.pojo.Employee">
SELECT * FROM t_emp
<where>
<if test="name != null">
emp_name = #{name}
</if>
<if test="salary != null and salary > 100">
AND emp_salary = #{salary}
</if>
</where>
</select>
set标签
自动去掉多余的逗号,自动连接set关键字
XML
<update id="update">
UPDATE t_emp
<set>
<if test="empName != null">
emp_name = #{empName},
</if>
<if test="empSalary != null">
emp_salary = #{empSalary}
</if>
</set>
WHERE emp_id = #{empId}
</update>
trim标签
prefix属性:指定要动态添加的前缀
suffix属性:指定要动态添加的后缀
prefixOverrides属性:指定要动态去掉的前缀,使用"|"分隔有可能的多个值
suffixOverrides属性:指定要动态去掉的后缀,使用"|"分隔有可能的多个值
XML
<select id="querytrim" resultType="employee">
<trim prefix="where" prefixOverrides="and | or">
<if test="name!=null">
emp_name=#{name}
</if>
<if test="salary!=null and salary > 100">
and emp_salary=#{salary}
</if>
</trim>
</select>
==========================================================================
<update id="updatatrim" >
updata t_emp
<trim prefix="set" suffixOverrides=",">
<if test="empName!=null">
emp_name=#{empName},
</if>
<if test="empSalary!=null">
emp_salary=#{empSalary}
</if>
</trim>
where emp_id=#{empId}
</update>
choose/when/otherwise标签
从上到下依次执行条件判断
遇到的第一个满足条件的分支会被采纳
被采纳分支后面的分支都将不被考虑
如果所有的when分支都不满足,那么就执行otherwise分支
XML
<!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) -->
<select id="selectEmployeeByConditionByChoose" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_salary from t_emp
where
<choose>
<when test="empName != null">emp_name=#{empName}</when>
<when test="empSalary < 3000">emp_salary < 3000</when>
<otherwise>1=1</otherwise>
</choose>
</select>
foreach标签
①collection属性 :指定要遍历的集合
②item属性: 遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象
③separator属性 :指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符
④open属性: 指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串
⑤close属性: 指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串
XML
<select id="querybatch" resultType="employee">
select * from t_emp
where emp_id in
<foreach collection="ids" open="(" separator="," close=")" item="id">
#{id}
</foreach>
</select>
<delete id="deletebatch" >
delete from t_emp
where emp_id in
<foreach collection="ids" open="(" separator="," close=")" item="id">
#{id}
</foreach>
</delete>
<insert id="insertbatch" >
insert into t_emp(emp_name,emp_salary)
values
<foreach collection="list" separator="," item="employee">
(#{employee.empName},#{employee.empSalary})
</foreach>
</insert>
<!-- 如果一个标签涉及到多条执行sql语句,一定要在mybatis的配置中打开多语句执行权限-->
<update id="updatebatch" >
<foreach collection="list" item="emp">
update t_emp set emp_name=#{emp.empName},emp_salary=#{emp.empSalary}
where emp_id=#{emp.empId}
</foreach>
</update>
XML
在mybatis中开启多语句执行权限
atguigu.dev.url=jdbc:mysql:///mybatis-example?allowMultiQueries=true
sql片段
作用: 提取出重复的sql语句,方便以后调用,简化代码量
标签:<sql></sql>
XML
<!-- 使用sql标签抽取重复出现的SQL片段 -->
<sql id="mySelectSql">
select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp
</sql>
<!-- 使用include标签引用声明的SQL片段 -->
<include refid="mySelectSql"/>
3.高级扩展
3.1批量扫描mapper接口
要求:
1.要求Mapperxml文件和mapper接口的命名必须相同
2.最终打包的位置要一致,都是在指定的包地址下面
①将xml文件放到mapper接口所在的包下
②在resource文件夹下创建与mapper接口相同的文件夹结构
XML
<mappers>
<package name="com.atguigu.mapper"/>
</mappers>
3.2插件
插件可以在用于语句执行过程中进行拦截,并允许通过自定义处理程序来拦截和修改 SQL 语句、映射语句的结果等。
MyBatis 的插件机制包括以下三个组件:
①Interceptor(拦截器):定义一个拦截方法 intercept,该方法在执行 SQL 语句、执行查询、查询结果的映射时会被调用。
②Invocation(调用):实际上是对被拦截的方法的封装,封装了 Object target、Method method 和 Object[] args 这三个字段。
③InterceptorChain(拦截器链):对所有的拦截器进行管理,包括将所有的 Interceptor 链接成一条链,并在执行 SQL 语句时按顺序调用。
分页插件(PageHelper):
导入依赖(pom.xml):
XML
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
配置分页插件(MyBatis-config.xml):
1com.github.pagehelper.PageInterceptor:插件名称
2Dialect:指定数据库类型
XML
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
插件的使用:
java
@Test
public void testTeacherRelationshipToMulti() {
TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class);
PageHelper.startPage(1,2);
// 查询Customer对象同时将关联的Order集合查询出来
List<Teacher> allTeachers = teacherMapper.findAllTeachers();
//
PageInfo<Teacher> pageInfo = new PageInfo<>(allTeachers);
System.out.println("pageInfo = " + pageInfo);
long total = pageInfo.getTotal(); // 获取总记录数
System.out.println("total = " + total);
int pages = pageInfo.getPages(); // 获取总页数
System.out.println("pages = " + pages);
int pageNum = pageInfo.getPageNum(); // 获取当前页码
System.out.println("pageNum = " + pageNum);
int pageSize = pageInfo.getPageSize(); // 获取每页显示记录数
System.out.println("pageSize = " + pageSize);
List<Teacher> teachers = pageInfo.getList(); //获取查询页的数据集合
System.out.println("teachers = " + teachers);
teachers.forEach(System.out::println);
}
3.3逆向工程
MyBatis 的逆向工程是一种自动化生成持久层代码和映射文件的工具,它可以根据数据库表结构和设置的参数生成对应的实体类、Mapper.xml 文件、Mapper 接口等代码文件,简化了开发者手动生成的过程。逆向工程使开发者可以快速地构建起 DAO 层,并快速上手进行业务开发。 MyBatis 的逆向工程有两种方式:通过 MyBatis Generator 插件实现和通过 Maven 插件实现。无论是哪种方式,逆向工程一般需要指定一些配置参数,例如数据库连接 URL、用户名、密码、要生成的表名、生成的文件路径等等。 总的来说,MyBatis 的逆向工程为程序员提供了一种方便快捷的方式,能够快速地生成持久层代码和映射文件,是半自动 ORM 思维像全自动发展的过程,提高程序员的开发效率。
注意:逆向工程只能生成单表crud的操作,多表查询依然需要我们自己编写!
逆向工程插件MyBatisX使用
MyBatisX 是一个 MyBatis 的代码生成插件,可以通过简单的配置和操作快速生成 MyBatis Mapper、pojo 类和 Mapper.xml 文件。下面是使用 MyBatisX 插件实现逆向工程的步骤:
-
安装插件:在Intellij IDEA中打开插件市场,搜索MyBatisX并进行安装
-
使用Intellij IDEA连接数据库
-
填写信息
-
展示数据库
-
逆向工程使用
-
效果展示