SSM框架,MyBatis框架的学习(下)

77 阅读8分钟

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 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="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 tor
        JOIN t_customer tur
        ON tor.customer_id = tur.customer_id
        WHERE 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_idorder_namecustomer_idcustomer_id(1)customer_name
1o111c01

测试:

@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_emp
        WHERE <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_emp
    where
    <choose>
        <when test="empName != null">emp_name=#{empName}</when>
        <when test="empSalary &lt; 3000">emp_salary &lt; 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_emp
    WHERE 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>

要求:

  1. Mapper.xml文件和mapper接口的命名必须相同
  2. 最终打包后的位置要一致,都是指定的包地址下

为了让打包后的位置一致,可以将resources下的存放mapper.xml文件的目录命名为和接口所在的包一样的文件夹结构命名(若mapper接口在java的com.ergou.mapper包下,mapper.xml应该在resource的com.ergou.mapper包下

注:在创建resources目录下的多层结构时,不是使用.分割,而是使用/分割