Mybatis基础操作

kitten 发布于 2024-05-14 231 次阅读


准备操作

  1. 创建员工表emp
  2. 创建新springboot工程 , 选择引入依赖(mybatis , mysql驱动 , lombok)
  3. application.properties中引入数据库连接信息
  4. 创建对应实体类Emp
  5. 准备接口mapper

1.删除

Mapper/EmpUser


package com.kitten.mybatis_usage.mapper;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EmpMapper {
//    根据Id删除数据
    @Delete("delete from emp where id = #{id}")
    public void deleteById(Integer id);
}

需要注意 :

  • 如何传参 , 通过#{} , 这种是通过预编译的方式 , 能提高效率 , 也能防止sql注入(通过占位符而不是直接传递参数)
  • 配置日志输出项 `application.properties`
    
    #配置mybatis日志输出项
    mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    
    
  • ${} , 这种是通过拼接的方式 将参数拼接到 SQL 语句中 , 存在SQL注入问题 , 一般多用于对表名或者列表进行动态设置时 使用

2.新增

可以自己尝试写一下 , 并不难

新建pojo/emp 实体类


package com.kitten.mybatis_usage.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
    private Integer id;
    private String username;
    private String password;
    private String name;
    private short gender;
    private String image;
    private short job;
    private LocalDate entrydate;
    private Integer deptId;
//    localdataetime 既包含年月日 又包含 十分秒
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

mapper/Empmapper


package com.kitten.mybatis_usage.mapper;
import com.kitten.mybatis_usage.pojo.Emp;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EmpMapper {
//    根据Id删除数据
    @Delete("delete from emp where id = #{id}")
    public void deleteById(Integer id);
//    新增数据
    @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
    public void insertEmp(Emp emp);
}

test单元测试类


...
@Test
    public void testAdd(){
        Emp emp = new Emp();
        emp.setUsername("Tony");
        emp.setName("kitten");
        emp.setGender((short)1);
        emp.setImage("1.jpg");
        emp.setJob((short)1);
        emp.setEntrydate(LocalDate.of(2000,1,1));
        emp.setDeptId(1);
        emp.setCreateTime(LocalDateTime.now());
        emp.setUpdateTime(LocalDateTime.now());
        empMapper.insertEmp(emp);
    }

2.1新增-主键返回

数据添加成功后,需要获取插入数据的主键


@Options(keyProperty="id",useGeneratedKeys=true)
@Insert(...)
...

其中useGeneratedKeys=true 表明要获取到返回的主键值

3.更新

mapper/Empmapper


//    更新数据
@Update("update emp set username=#{username},name=#{name},gender=#{gender},image=#{image},"+
"job=#{job},entrydate=#{entrydate},dept_id=#{deptId},update_time=#{updateTime} where id = #{id}")
public void updateById(Emp emp);

test


...
@Test
    public void testUpdate(){
        Emp emp = new Emp();
        emp.setId(18);
        emp.setUsername("TheKitten");
        emp.setName("kitten");
        emp.setGender((short)1);
        emp.setImage("2.jpg");
        emp.setJob((short)4);
        emp.setEntrydate(LocalDate.of(2000,1,1));
        emp.setDeptId(1);
        emp.setUpdateTime(LocalDateTime.now());
//        执行更新员工操作
        empMapper.updateById(emp);
    }

一般我们实现更新时 , 都要进行回显展示

4.查询

首先要查询

mapper/Empmapper


@Select("select * from emp where id = #{id}")
public emp getById(Integer id);

test


@Test
public void testGetById(){
    Emp emp = empMapper.getById(18);
    sout-> emp;
}

这里输出的时候 , 由于 数据库中设置的字段名 有 dept_id , create_time 和 update_time , 但是我们定义的类的 属性名称分别是 deptId , createTime 和 updateTime, 所以 打印出来的结果为 null

对于前面几个属性 , id , username …… 这些 实体类属性名 和 数据库表查询返回的字段名 一致,mybatis会自动进行封装

解决方案:


//    解决方案一 : 起别名
@Select("select id, username, password, name, gender, image, job, entrydate,"+
        " dept_id deptId, create_time createTime, update_time updateTime from emp where id = #{id}")
public Emp getById(Integer id);
//    解决方案二 : @results 手动映射封装
@Results({
       @Result(column = "dept_id", property = "deptId"),
       @Result(column = "create_time", property = "createTime"),
       @Result(column = "update_time", property = "updateTime")
})
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);

解决方案三 : 开启mybatis驼峰命名自动转换(常用)

application.properties


mybatis.configuration.map-underscore-to-camel-case=true

4.1条件查询

根据前端输入的员工信息 , 员工性别 , 入职时间 搜索满足条件的员工信息

mapper/EmpMapper


//    条件查询 注意like后面接的 pattern , 不用#{} 而是用 ${}
    @Select("select * from emp where name like '%${name}%' and gender= #{gender} and "+
            "entrydate between #{begin} and #{end} order by update_time desc")
    public List list(String name, Short gender, LocalDate begin , LocalDate end);

test


//条件查询
...
    @Test
    public void testList(){
        List list = empMapper.list("张", (short) 1, LocalDate.of(2010, 1, 1), LocalDate.of(2020, 1, 1));
        System.out.println(list);
    }

注意 这里的like 运算符后面 还是跟了 ${} , 这样就会导致下面几个问题 :

  1. 性能低 , 通过拼接字符串的方式 , 不会预编译
  2. 不安全 , 存在SQL注入问题

如何解决? 通过mysql 提供的 concat() 拼接函数


@Select("select * from emp where name like concat('%',#{name},'%') and gender= #{gender} and "+
       "entrydate between #{begin} and #{end} order by update_time desc")
public List list(String name, Short gender, LocalDate begin , LocalDate end);

在springboot1.x版本中 , 需要在参数前 加上 @param 这个注解

XML映射文件

  1. 映射文件的名称与 Mapper 接口 名称一致 , 并且将XML文件 和Mapper几口放置在同包下
  2. XML的namespace 属性为Mapper接口全限定名一致
  3. XML映射文件中sql语句的id 和 Mapper 接口中的方法名一致,并与返回类型一致

例: java/com.kitten 下放了一个 mapper层 , 里面有一个EmpMapper接口 , 那么我要在resources 文件夹下建一个 com.kitten 包 , 里面放EmpMapper.XML文件

对于上面的 list方法 , 我们可以把 这一整段拆分成两部分 :

resources/com/kitten/mapper/EmpMapper.xml

<?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">
<mapper namespace="com.kitten.mapper.EmpMapper">
<!--    id 方法名      resultType 结果的单个类型 ArrayList里放的是emp , 那么类型就是emp-->
    <select id="list" resultType="com.kitten.pojo.Emp">
        select * from emp where name like concat('%',#{name},'%') and gender= #{gender} and
        entrydate between #{begin} and #{end} order by update_time desc
    </select>
</mapper>

mapper/EmpMapper

@Mapper
public List list(String name, Short gender, LocalDate begin , LocalDate end);

在mybatis官方文章中 , 说明了我们可以在什么时候使用XML映射 , 当我们写一些逻辑很简单的SQL语句时 , 可以直接在方法上面写注解 , 但是在写一些相对复杂的SQL语句时 , 最好还是用XML映射

动态SQL

随着外部条件变化 而变化的语句叫动态sql

1.动态select

主要要明白的有<if> , <where>
resources/com/kitten/mapper/EmpMapper.xml中的 <mapper>标签中的select语句改一下, 将语句改为 :

<select id="list" resultType="com.kitten.pojo.Emp">
        select *
        from mybatis.emp
        <where>
            <if test="name != null">
                name like concat('%', #{name}, '%')
            </if>
            <if test="gender != null">
                gender = #{gender}
            </if>
            <if test="begin != null and end != null">
                entrydate between #{begin} and #{end}
            </if>
        </where>
        order by update_time desc;
    </select>

<if>标签能进行判断从而来拼接sql , 如果我们第一个参数是空,后面参数不为空时 , 拼接出的select语句中就有 where and ... ; 这样的话就会报SQLSyntaxError错误 , 所以我们可以使用<where>标签 , 它能动态拼接语句里的 and , or 运算符 , 而且 <if> 中的条件都不成立时 , 它会自动省略 where 子句

2.动态update

主要要明白的有: <set>

java/.../mapper/EmpMapper

...
//    动态更新
    public void updateById(Emp emp);

resources/.../mapper/EmpMapper.xml

<update id="updateById">
# 动态更新员工操作
        update mybatis.emp
        <set>
            <if test="username != null">username = #{username},</if>
            <if test="name != null">name = #{name},</if>
            <if test="gender != null">gender = #{gender},</if>
            <if test="image != null">image = #{image},</if>
            <if test="job != 0">job = #{job},</if>
            <if test="entrydate != null">entrydate = #{entrydate},</if>
            <if test="deptId != null">dept_id = #{deptId},</if>
            <if test="updateTime != null">update_time = #{updateTime},</if>
        </set>
        where id = #{id}
    </update>

test

@Test
    public void testUpdate(){
        Emp emp = new Emp();
        emp.setId(18);
        emp.setUsername("Thekitten");
        emp.setName("kitten10");
        emp.setGender((short) 1);
        emp.setImage("3.png");
        emp.setUpdateTime(LocalDateTime.now());
        empMapper.updateById(emp);
    }

<set>标签是用来对<if> 中的 , 进行动态删除 , 和<where>类似

3.动态delete

主要是<foreach> 标签

resources/.../mapper/EmpMapper.xml

<delete id="deleteByIds">
# delete from mybatis.emp where id in (16,17,18)
        delete from mybatis.emp where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>

java/.../mapper/EmpMapper


//    动态删除
    public void deleteByIds(List ids);

test


...
//    delete 遍历集合
    @Test
    public void testDelete(){
        List ids = Arrays.asList(13, 14, 15);
        empMapper.deleteByIds(ids);
    }

4.重复语句

我们想再写一个select语句时 :

<select id="list_repeat" resultType="com.kitten.pojo.Emp">
        select * from mybatis.emp
        where id = #{id}
    </select>

//    重复sql语句
    public Emp list_repeat(Integer id);

可以发现这个sql 语句中的 select * from emp 与我们之前写的第一个select中的相似,这些想同的地方我们可以抽离除来,简化代码 :

resources/.../mapper/EmpMapper.xml

<mapper>
    <sql id="commonSelect">
        select *
        from mybatis.emp
    </sql>
    ...
    <select id="list_repeat" resultType="com.kitten.pojo.Emp">
        <include id="commonSelect" />
        where id = #{id}
    </select>
</mapper>

test里自行写测试方法

总结

mybatis的基本用法到这里就基本结束了 , 下面来整理一下做demo的整个流程 :

  1. 创建springBoot工程 : 勾选maven , mybatis framework 框架和 mysql 驱动 (之后还会勾选web)
  2. 连接数据库 : 配置resources/application.properties下连接数据库信息(四个:驱动类名,数据库url,username和password)
  3. 配置开发工具:
    • maven下装lombok,自动为实体类添加getter/setter,全/无参构造
    • application.properties下配置mybatis日志输出项和自动转换驼峰命名
  4. 创建工程文件:
    • pojo/数据库实体类对应数据库中表中信息
    • java/.../mapper/实体类Mapper接口定义操作数据库方法名以及参数
    • resources/.../mapper/实体类Mapper.xml文件名称要和接口文件名称一致,xml文件中配置namespace
  5. 写sql语句 : xml文件一般是用来写复杂sql语句的,对于简单sql语句,我们直接写在test中就行 , 写动态sql语句时 , 注意各个标签的使用方式

搞明白流程后,我们回到springBoot文档中做一个综合案例