MyBatis进阶

(一)Mybatis映射器

在JPA中,可以只写接口,不写实现,同样在Mybatis中也可以只写接口,不写实现,可以直接通过映射器,让Mybatis来管理实现接口。

  1. 准备

在JPA中,dao层被命名为resptory,这是规范,同样在Mybatis中,也要求dao层命名为mapper。(注:使用dao命名也不影响功能的实现)

① mapper层:
1)新建接口(不写实现的接口,并在接口内添加方法,注意接口名可以不加I,但需要根据公司具体需求),例如:UserMapper.java

每一个mapper都会对应一个映射文件

public interface UserMapper {
    List<User> findAll();
}

2)新建映射文件 UserMapper.xml。
注意:xml文件内配置的命名空间使用接口的全限定名;

<mapper namespace="cn.lzj.mapper.RoleMapper">
 <!--查询全部数据-->
    <select id="findAll" resultType="User">
      select * from user
    </select>
</mapper>

3)在MyBatis的主配置文件内,引入mapper的映射文件

<mappers>
        <mapper resource="cn/lzj/mapper/userMapper.xml"/>
        <mapper resource="cn/lzj/mapper/roleMapper.xml"/>
</mappers>

4)测试:通过MyBatis的映射器,获取DB中的数据,能成功获取

public void testFindAll() throws Exception{
        SqlSession session = MyBatisUtil.openSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        mapper.findAll().forEach(e-> System.out.println(e));
}

(二)使用Mybatis映射器,对数据进行增删改查

  1. UserMapper.java接口
public interface UserMapper {
    User findOne(Long id); //查询一条
    List<User> findAll();  //查询所有
    void save(User user);  //添加
    void delete(Long id);  //删除
    void update(User user); //动态修改(避免未修改的值变为null)
    void batchSave(List<User> list); //批量添加
    void batchDelete(List<Long> id); //批量删除
    List<User> selectByLike(UserQuery query); //高级查询
    Long countSelect(UserQuery query); //计数-->和高级查询共同使用一组sql,抽取公共的sql
}
  1. UserMapper.映射文件
<?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">
<!--namespace:命名空间  使用映射,修改为接口的全限定名-->
<mapper namespace="cn.lzj.mapper.UserMapper">
    <!--查询一条-->
    <select id="findOne" parameterType="long" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>
    <!--查询全部-->
    <select id="findAll" resultType="User">
        SELECT * FROM user
    </select>
    <!--添加-->
    <insert id="save" parameterType="User">
        INSERT INTO user(name,age)
        VALUES (#{name},#{age})
    </insert>
    <!--删除-->
    <delete id="delete" parameterType="long">
        DELETE FROM user WHERE id = #{id}
    </delete>
    <!--修改-->
    <update id="update" parameterType="User">
         UPDATE user
              <set>
                  <if test="name!=null and name!=''">
                      name = #{name},
                  </if>
                  <if test="age!=null">
                      age =#{age},
                  </if>
              </set>
          WHERE id = #{id}
    </update>
    <!--
    批量添加:
        foreach标签:实现遍历
                 collection:遍历的集合    item:集合的每一个元素   separator:分隔符
    -->
    <insert id="batchSave" parameterType="list">
        INSERT INTO user(name,age) VALUES
        <foreach collection="list" item="user" separator=",">
            (#{user.name},#{user.age})
        </foreach>
    </insert>
    <!--
    批量删除
         open="(":开始  close=")":结束 index:索引
         参数推荐使用集合,因为数组无法进行变化
    -->
    <delete id="batchDelete" parameterType="list">
        DELETE FROM user WHERE id IN
        <foreach collection="list" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>
    
  <--  传数组的方法
    <delete id="batchDelete" parameterType="long[]">
        delete from employee where id in
        <foreach collection="array" item="v" open="(" close=")" separator=",">
            #{v}
        </foreach>
</delete>
  -->

    <!--
        高级查询:
            使用Query对象,封装接收前台用户所传入的查询的参数,因为用户所传入的参数是动态变化的
    -->
    <select id="selectByLike" parameterType="cn.lzj.domain.UserQuery" resultType="user">
        SELECT * FROM user <include refid="whereSql"/>
    </select>

    <select id="countSelect" parameterType="cn.lzj.domain.UserQuery" resultType="long">
        SELECT COUNT (id)  FROM user <include refid="whereSql"/>
    </select>
    <!--
        SQL标签:用于抽取公共的sql语句
        对于特殊符号,如小于"<",可以进行转义:&lt;:小于  &gt;:小于     也可以使用CDATA区
        where标签:用于将第一个and转换为where
    -->
    <sql id="whereSql">
        <where>
            <if test="name != null and name !=''">
                name LIKE CONCAT("%",#{name},"%")
            </if>
            <if test="minAge != null and maxAge !=''">
                AND age >= #{minAge}
            </if>
            <if test="maxAge != null and maxAge !=''">
                AND <![CDATA[age <= #{maxAge}]]> <!--可以转义,也可以使用CDATA区,不解析内部的字符串,即不会解析到"<"-->
            </if>
        </where>
    </sql>
</mapper>
  1. 注意点
    ①sql语句的id名,必须和接口中的方法名对应一致
    在这里插入图片描述分析:
    在这里插入图片描述②批量添加时,value值的name和age需要指明是哪一个domain对象的属性
    在这里插入图片描述③注意批量删除数据的时候,属性不要写错了,如:open不要写成了index
    在这里插入图片描述④高级查询时,使用了where标签,就不要再写where了:
    细节:
    a .准备一个Query对象(封装所有条件)
    b. 模糊查询 concat("%",#{name},"%")
    c. 遇到特殊符号 1.转义 < 2.CDATA段 <![CDATA[...]]>
    d. 使用where标签(第一个and变成where)
    e. if中有多个条件使用 and/or 进行关联
    f. 如果出现相就的代码,可以单独抽取sql标签,引用include即可

在这里插入图片描述⑤动态修改数据时,不要忘记添加",",否则只能修改一个属性,若修改多个属性就会报错
在这里插入图片描述

(三)通过Mybatis映射器配置多对一关系

(1) 准备

domain之间的关系,在JPA直接可以通过加注解,JPA就可以自动为我们实现,但是使用MyBatis,需要我们自己写SQL进行关系的配置

1.准备
① 新建一个role表
②在user表中添加外键字段
注意:
在此处我们只添加外键字段,一般不在表中添加外键,使用代码进行关系的维护。这样可以避免每次根据外键查询,降低性能,但是不添加外键易出现无效数据和有问题的代码,但是性能提升了,所以还需要根据公司的需求,选择是否添加外键。
③user类中配置外键

public class User {
    private Integer id;
    private String name;
    private Integer age;
    private Role role;    //外键   底层会自动配置role_id
    //省略getter、setter和toString方法
}

④ role类

public class Role {
    private Long id;
    private String name;
    //省略getter、setter和toString方法
}

(2) 使用嵌套结果(1条sql)方式数据的查询

注意:

①查询的sql要关连多张表(一定要取别名,不然有些名称会产生冲突)
②当我们使用了association 后默认的映射失败,需要自己手动完成映射

  1. UserMapper.xml映射文件
<mapper namespace="cn.lzj.mapper.UserMapper">
    <resultMap id="userMap" type="User">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <!--嵌套结果配置多对一关系
            association: 多对一,一对多都是它(代表要关连一个对象)
                    当你使用了这个标签,自动映射就失效
                    property="department" 属性名称 javaType:属性的类型
        -->
        <association property="role" javaType="Role">
            <id property="id" column="rid"/>
            <result property="name" column="rname"/>
        </association>
    </resultMap>
    <!--
        查询全部数据
            当有字段名称或者类型对应不上了,就需要我们写手动映射
            如果在查询中出现相同名称的字段,为了区分,我们需要为它取别名
    -->
    <select id="findAll" resultMap="userMap">
      SELECT u.* , r.id rid , r.name rname FROM user u LEFT JOIN role r ON r.id = u.role_id
    </select>
</mapper>

(3) 使用嵌套查询(n+1条sql)方式数据的查询

注意:

① 会产生n+1条sql
② 需要去找到对应的那条sql并且执行
③ 保证MyBatis能找到这两个xml

  1. roleMapper.xml映射文件
<mapper namespace="cn.lzj.mapper.RoleMapper">
     <select id="findOne" resultMap="roleMap">
      select * from role where id=#{id}
    </select>
</mapper>
  1. userMapper.xml映射文件
<mapper namespace="cn.lzj.mapper.UserMapper">
    <!--
        通过代码配置关系映射
            依然需要做结果映射
             column="department_id" :列名(待会执行sql需要传入的数据)
             select:去获取相应的SQL
    -->
    <resultMap id="userMap" type="User">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <!--嵌套查询,配置多对一的关系-->
        <association property="role" javaType="Role" column="role_id" select="cn.lzj.mapper.RoleMapper.findOne">
        </association>
    </resultMap>
    <!--查询全部数据-->
    <select id="findAll" resultMap="userMap">
      select u.*,r.id rid,r.name rname from user u LEFT JOIN role r on r.id = u.role_id
    </select>
</mapper>

(4) 野方法

注意:此方法只是能实现功能,获取每一个user及其所对应的role,但可能会出现一定的问题,推荐使用嵌套查询或嵌套结果进行数据的获取

<mapper namespace="cn.lzj.mapper.UserMapper">
    <resultMap id="userMap" type="User">
        <result property="role.id" column="rid"/>
        <result property="role.name" column="rname"/>
    </resultMap>
    <!--查询全部数据-->
    <select id="findAll" resultMap="userMap">
      select u.*,r.id rid,r.name rname from user u LEFT JOIN role r on r.id = u.role_id
    </select>
</mapper>

(三)通过Mybatis映射器配置一对多关系

(1) 准备

  1. 一方,即role内配置外键
public class Role {
    private Long id;
    private String name;
    private List<User> user = new ArrayList<>();
     //省略getter、setter和toString方法
}

(2) 使用嵌套结果方式数据的查询

roleMapper.xml映射文件

<mapper namespace="cn.lzj.mapper.RoleMapper">
    <resultMap id="roleMap" type="Role">
        <id property="id" column="rid"/>
        <result property="name" column="rname"/>
        <!--一对多-->
        <collection property="user" ofType="User">
            <id property="id" column="id"/>
            <result property="name" column="name"/>
            <result property="age" column="age"/>
            <result property="role" column="role_id"/>
        </collection>
    </resultMap>
    <!--注意:此处只能使用左外联查询,因为是以一方为主要查询对象-->
    <select id="findAll" resultMap="roleMap">
         select u.*,r.id rid,r.name rname from role r LEFT JOIN user u on r.id = u.role_id
    </select>
</mapper>

(3) 使用嵌套查询方式数据的查询

userMapper.xml映射文件

<mapper namespace="cn.lzj.mapper.UserMapper">
      <select id="findByRoleId" parameterType="long" resultType="User">
      select * from user u where role_id = #{id}
    </select>
</mapper>

roleMapper.xml映射文件

<mapper namespace="cn.lzj.mapper.RoleMapper">
    <resultMap id="roleMap" type="Role">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <!--
         一对多 : 使用集合进行配置
        -->
        <collection property="user" ofType="User" column="id" select="cn.lzj.mapper.UserMapper.findByRoleId">
        </collection>
    </resultMap>
    <!--注意:此处只能使用左外联查询,因为是以一方为主要查询对象-->
    <select id="findAll" resultMap="roleMap">
         select * from role
    </select>
</mapper>

(4)级联保存

  • 准备两个Mapper
  • 保存部门后需要马上拿到它的id
  • 保存员工传的是Map{List,deptId}

departmentMapper.xml

<!--保存后需要拿到id-->
<insert id="save" parameterType="department"
        useGeneratedKeys="true" keyProperty="id" keyColumn="id">
    insert into department (name) values (#{name})
</insert>

employeeMapper.xml

<insert id="batchSave" parameterType="map">
    insert into employee (name,age,sex,dept_id) values
    <foreach collection="list" item="emp" separator=",">
        (#{emp.name},#{emp.age},#{emp.sex},#{deptId})
    </foreach>
</insert>

(四)缓存

  • 自带一级级联
  • 二级缓存需要加上标签
  • 二级缓存的对象必需是序列化对象 … implements Serializable
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值