MyBatis多表映射
多表结果实体类设计技巧:
对一,属性中包含对方对象
对多,属性中包含对方对象集合
对一查询
对一查询在设计接收多表查询的结果的实体类时,设计一个对方类型的属性即可
查询的结果映射时,如果查询的列名映射到对象的属性,此属性是另一个类的对象,使用resultType方式映射便不会成功映射到此属性中,便要使用resultMap的方式处理此属性的对象
在resultMap中给对象属性赋值时,使用association标签进行赋值
association标签的属性:
- property:对象属性的属性名
- javaType:对象属性的类型的全限定符或全限定符的别名
在association标签内继续使用id标签和result标签对该对象属性的属性赋值
例:
Order类:
@Data
public class Order {
private Integer orderId;
private String orderName;
private Integer customerId;
private Customer customer;
}
OrderMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""<https://mybatis.org/dtd/mybatis-3-mapper.dtd>">
<mapper namespace="com.ergou.mapper.OrderMapper"><resultMap id="orderMap" type="com.ergou.pojo.Order">
<!-- order的主键 id标签--><id column="order_id" property="orderId"/><result column="order_name" property="orderName"/><result column="customer_id" property="customerId"/><association property="customerName" javaType="com.ergou.pojo.Customer"><id column="customer_id" property="customerId"/><result column="customer_name" property="customerName"/></association></resultMap><select id="queryOrderById" resultMap="">SELECT * FROM t_order torJOIN t_customer turON tor.customer_id = tur.customer_idWHERE tor.order_id = #{id}</select>
</mapper>
select标签中的sql语句如下
SELECT * FROM t_order tor
JOIN t_customer tur
ON tor.customer_id = tur.customer_id
WHERE tor.order_id = 1
查询结果如下:
order_id | order_name | customer_id | customer_id(1) | customer_name |
---|---|---|---|---|
1 | o1 | 1 | 1 | c01 |
测试:
@org.junit.jupiter.api.Test
public void test1() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sessionFactory.openSession();OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);Order order = mapper.queryOrderById(1);System.out.println(order);
}
对多查询
对多查询在设计接收多表查询的结果的实体类时,设计一个包含对方类型的集合属性即可
为了给集合属性赋值,也是要使用resultMap方式来映射
在resultMap标签中给集合属性赋值时,使用collection标签给集合属性赋值
collection标签的属性:
- property:集合的属性名
- ofType:集合的泛型类型
在collection标签中继续使用id标签和result标签完成集合内元素的映射即可
result标签自动映射
可以将autoMappingBehavior设置为full,进行多表resultMap映射的时候,可以省略符合列和属性命名映射规则(列名=属性名,或者开启驼峰映射也可以自定映射)的result标签
在mybatis-config.xml文件中的settings标签中设置:
将autoMappingBehavior设置的value值为FULL
<!--开启resultMap自动映射 -->
<setting name="autoMappingBehavior" value="FULL"/>
默认情况下,resultMap会自动映射一层result标签,想要多层自动映射(association标签和collection标签中),就要使用autoMappingBehavior的设置
MyBatis动态语句
if标签和where标签
判断test属性中的表达式,表达式结果为true就是sql语句中添加if标签中的sql语句,结果为false就是sql语句中不添加if标签中的sql语句
if标签的test属性的语法:(test中只能访问实体类的属性,不能访问数据库中的字段)
变量名 比较符号 数据值/变量名
如果要连接多个表达式,and表示且,or表示或。
<select id="queryByNameAndSalary" resultType="com.ergou.pojo.Employee">SELECT * FROM t_empWHERE <if test="name != null">emp_name = #{name}</if><if test="name != null and salary != null">AND</if><if test="salary != null and">emp_salary = #{salary}</if></select>
以上例子一旦出现emp_name和emp_salary都是空的时候,WHERE关键字就失去作用,导致报错
WHERE语句的控制要让mybatis操作的话,可以使用where标签
将sql语句中的WHERE关键字替换成where标签,可以让mybatis自动控制WHERE和AND或OR的有无
where标签的作用:
- 自动添加WHERE关键字,WHERE内任何一个if满足,都会自动添加where关键字,不满足就不会有WHERE关键字
- 若if中的sql语句中因为动态变化出现有多余的AND或OR,就会自动去除其多余的AND或OR
以上代码用where标签改进后:
<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标签的作用类似于where标签,控制动态的update标签的sql语句
set标签的作用:
- 自动去掉多余的逗号
- 自动添加SET关键字,当set标签中的if标签都是false的结果时,就不会添加SET关键字
<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标签
使用trim标签控制条件部分两端是否包含某些字符
- prefix属性:指定要动态添加的前缀
- suffix属性:指定要动态添加的后缀
- prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
- suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值
例:
<where><if test="name != null">emp_name = #{name}</if><if test="salary != null and salary > 100">AND emp_salary = #{salary}</if></where>
等同于:
<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>
也等同于:
<trim prefix="WHERE" suffixOverrides="AND|OR"><if test="name != null">emp_name = #{name} AND</if><if test="salary != null and salary > 100">emp_salary = #{salary}</if>
</trim>
prefix=”WHERE” 意思是当WHERE关键字缺少的时候自动加上WHERE关键字(在if中的开头处加上WHERE关键字)
prefixOverrides="AND|OR" 意思是AND关键字或OR关键字多余的时候自动去掉多余的AND和OR关键字(去掉if中的开头的AND或OR)
suffix属性和suffixOverrides属性同理,只不过操作的是结尾后缀
同理也可以替代set标签
choose/when/otherwise标签
在多个分支条件中,仅执行一个。有点类似于Java中的switch语句
- 从上到下依次执行条件判断
- 遇到的第一个满足条件的分支会被采纳
- 被采纳分支后面的分支都将不被考虑
- 如果所有的when分支都不满足,那么就执行otherwise分支
<!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) -->
<select id="selectEmployeeByConditionByChoose" resultType="com.atguigu.mybatis.entity.Employee">select emp_id,emp_name,emp_salary from t_empwhere<choose><when test="empName != null">emp_name=#{empName}</when><when test="empSalary < 3000">emp_salary < 3000</when><otherwise>1=1</otherwise></choose><!--第一种情况:第一个when满足条件 where emp_name=?第二种情况:第二个when满足条件 where emp_salary < 3000第三种情况:两个when都不满足 where 1=1 执行了otherwise-->
</select>
foreach标签
foreach标签的属性值
- collection属性:指定要遍历的数据
- item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象
- separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符
- open属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串
- close属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串
- index属性:这里起一个名字,便于后面引用
- 遍历List集合,这里能够得到List集合的索引值
- 遍历Map集合,这里能够得到Map集合的key
例:以id批量查询员工
<select id="queryBatch" resultType="com.ergou.pojo.Employee">SELECT * FROM t_empWHERE emp_id IN<foreach collection="ids" open="(" separator="," close=")" item="id" index="index">#{id}</foreach>
</select>
若ids是一个里面的数据为1和2和3三个Integer类型的变量的List集合,此处的foreach标签的结果为:
(1,2,3)
例:批量插入员工
<insert id="insertBatch">INSERT INTO t_emp(emp_name,emp_salary)VALUES <foreach collection="employees" separator="," item="employee" index="index">(#{employee.empName},#{employee.empSalary})</foreach>
</insert>
例:批量更新员工
<!-- int updateEmployeeBatch(@Param("empList") List<Employee> empList) -->
<update id="updateEmployeeBatch"><foreach collection="empList" item="emp" separator=";">update t_emp set emp_name=#{emp.empName} where emp_id=#{emp.empId}</foreach>
</update>
如果向批量更新员工这种在一个标签中出现有多个语句要运行,要设置允许运行多语句:
在数据库连接配置中找到url,在其后加上参数allowMultiQueries=true
jdbc:mysql:///mybatis-example?allowMultiQueries=true
sql片段
sql标签用于抽取重复出现的sql片段以方便重复调用
抽取重复的SQL片段
<!-- 使用sql标签抽取重复出现的SQL片段 -->
<sql id="mySelectSql">select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp
</sql>
引用已抽取的SQL片段,使用include标签,refid属性指定sql片段的id值
<!-- 使用include标签引用声明的SQL片段 -->
<include refid="mySelectSql"/>
MyBatis拓展
Mapper按包批量扫描
在mybatis-config.xml文件中的mappers标签中,可以直接指定mapper包下的所有的接口都完成mapper文件的映射的配置:
<mappers>
<package name="com.ergou.mapper"/>
</mappers>
要求:
- Mapper.xml文件和mapper接口的命名必须相同
- 最终打包后的位置要一致,都是指定的包地址下
为了让打包后的位置一致,可以将resources下的存放mapper.xml文件的目录命名为和接口所在的包一样的文件夹结构命名(若mapper接口在java的com.ergou.mapper包下,mapper.xml应该在resource的com.ergou.mapper包下
注:在创建resources目录下的多层结构时,不是使用.分割,而是使用/分割