5 mybatis单表增删改查

5 单表

值参数(如 id=1):用 #{ }(安全,防注入)。

表名 / 字段名 / sql语句(SQL 语法部分):用 ${ }(直接拼接,不加单引号)。

在MyBatis中的.xml中写sql语句时,只要为表定义了别名,就从头到尾都使用这个别名来引用它的字段。因为,MyBatis 对表名直接访问字段和通过别名访问字段的大小写、解析逻辑存在差异,用别名能避免因系统(如 MySQL 在不同环境下的大小写处理、MyBatis 自身解析规则)对表名、字段名的误处理,确保 SQL 中字段引用稳定且符合预期,从而避免 “未知列” 等因命名解析引发的错误。

注:下面给的代码是两个项目里的,两个项目里的user类字段不一样,主要看写法。

5.1 查询

5.1.1 普通查询

UserMapper.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.lsl.dao.UserDao">
    <!-- id:方法名称 -->
    <!-- resultType:数据返回的类型 -->
    <select id="findAll" resultType="com.lsl.entity.User">
        select * from user;
    </select>
    
    <!-- 
        通过id查询 
        SQL语句使用#{占位符的名称,名称可以任意},仅限于基本数据类型和String类型
    -->
    <select id="findById" resultType="com.lsl.entity.User" parameterType="java.lang.Integer">
    select * from user where id = #{id}
    </select>
    
    <select id="findByName" resultType="com.lsl.entity.User" parameterType="java.lang.String">
        select * from user where username = #{username}
    </select>
    
    <!-- 多个参数可以直接使用user对象 -->
    <select id="findByUser1" resultType="com.lsl.entity.User" parameterType="com.lsl.entity.User">
        select * from user where username = #{username} and password = #{password}
    </select>
    
    <!-- 分页查询 -->
    <select id="findAllUser" parameterType="java.lang.Integer" resultType="com.lsl.entity.User">
        select * from user limit #{param1} offset #{param2}
    </select>
    
    <select id="findAllUserByUsername" parameterType="com.lsl.entity.User" resultType="com.lsl.entity.User">
        select * from user where username = #{username} limit #{pageSize} offset #{pageStart}
    </select>
    
    <!-- 具体函数的查询 -->
    <select id="findByCount" resultType="int">
        select count(*) from user
    </select>

</mapper>

UserDao

package com.lsl.dao;

import com.lsl.entity.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserDao {
    public abstract List<User> findAll();
    User findById(Integer id);
    List<User> findByName(String username);
    List<User> findByUser1(User user);
    List<User> findAllUser(Integer pagesize,Integer pageStart);
    List<User> findAllUserByUsername(User user);
    public Integer findByCount();
}

对于实体类User,最好实现Serializable这个接口,如果用到MyBatis的二级缓存,就必须要实现这个接口。

public class UserTest {
    private InputStream in = null;
    private SqlSession session = null;
    private UserDao userDao = null;

    @Before  //前置通知, 在方法执行之前执行
    public void init() throws IOException {
        //加载主配置文件,目的是为了构建SqlSessionFactory对象
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //通过SqlSessionFactory工厂对象创建SqlSesssion对象
        session = factory.openSession();
        //通过Session创建UserDao接口代理对象
        userDao = session.getMapper(UserDao.class);
    }

    @After  //@After: 后置通知, 在方法执行之后执行 。
    public void destory() throws IOException {
        //释放资源
        if (session != null) {  // 增加null检查
            session.close();
        }
        in.close();
    }
    
    @Test
    public void aaa(){
        List<User>  users = userDao.findAll();
        for (User user: users
        ) {
            System.out.println(user.toString());
        }
    }
    
    @Test
    public void findByName(){
        List<User> users = userDao.findByName("熊大");
        for(User user:users){
            System.out.println(user.toString());
        }
    }
    
    @Test
    public void  findByUser1(){
        User user = new User();
        user.setUsername("熊大");
        user.setPassword("123");
        List<User> users = userDao.findByUser1(user);
        for(User user1:users){
            System.out.println(user1.toString());
        }
    
    }
    
    @Test
    public void findAllUser(){
        Integer pageSize = 5;
        Integer pageIndex = 1;
        Integer pageStart = pageSize * (pageIndex-1);
        List<User> users = userDao.findAllUser(pageSize,pageStart);
        for(User user1:users){
            System.out.println(user1.toString());
        }
    }
    
    @Test
    public void findAllUserByUsername(){
        User user = new User();
        user.setUsername("小李");
        user.setPageSize(5);
        user.setPageStart(0);
        List<User> users = userDao.findAllUserByUsername(user);
        for(User user1:users){
            System.out.println(user1.toString());
        }
    }
    
    @Test
    public void testFindByCount() throws Exception {
        Integer count = userDao.findByCount();
        System.out.println("总记录数:"+count);
    }
}

5.1.2 模糊查询

<!-- 模糊查询 -->
<select id="findByName" resultType="com.qcby.domain.User" parameterType="string">
    <!-- 第一种方式的SQL语句-->
    select * from user where username  like #{username}
</select>

<select id="findByName1" resultType="com.qcby.domain.User" parameterType="string">
    <!-- 第二种SQL语句的编写 强调:'%${value}%'不能修改,固定写法(不推荐使用)  -->
    select * from user where username  like '%${value}%'
</select>
public List<User> findByName(String username);

public List<User> findByName1(String username);
// 第一种
@Test
public void testFindByName() throws Exception {
    List<User> list = mapper.findByName("%王%");
    for (User user : list) {
        System.out.println(user);
    }
}
// 第二种
@Test
public void testFindByName1() throws Exception {
    List<User> list = mapper.findByName1("王");
    for (User user : list) {
        System.out.println(user);
    }
}
  • 使用#{}时:需在参数中包含通配符%,例如where name like #{name},传入的参数应为"%张三%"
  • 使用${}时:可在 SQL 中直接拼接通配符,例如where name like '%${value}%',传入的参数为"张三"
(注:模糊查询推荐使用#{}以保证安全性,若必须使用${}需自行处理注入风险)
  • 值参数(如 id=1):用 #{ }(安全,防注入)。
  • 表名 / 字段名 / sql语句(SQL 语法部分):用 ${ }(直接拼接,不加单引号)。

5.1.3 动态sql

单条件判断,满足条件则拼接标签内的 SQL 片段;
<select id="findByWhere" parameterType="com.qcby.domain.User" resultType="com.qcby.domain.User">
    select * from user where 1 = 1
    <if test="username != null and username != ''">
        and username like #{username}
    </if>
    <if test="sex != null and sex != ''">
        and sex = #{sex}
    </if>
</select>
// 条件查询
public List<User> findByWhere(User user);
@Test
public void testFindByWhere(){
    User user = new User();
    user.setUsername("%熊%");
    user.setSex("女");
    List<User> users = mapper.findByWhere(user);
    System.out.println(users);
}
  • 智能处理 SQL 中的 WHERE 关键字,自动剔除标签内拼接 SQL 开头的多余 AND/OR,避免语法错误;
  • where标签目的就是为了去掉 where 1=1的拼接的。
  • where标签使用在if标签的外面。
<!-- 动态sql-->
<!-- where if 传入了那些参数就查那些-->
<select id="findUser" resultType="com.lsl.entity.User" parameterType="com.lsl.entity.User">
    select * from user
    <where>
        <if test="username!=null and username!=''">
            username = #{username}
        </if>
        <if test="birthday!=null">
            and birthday=#{birthday}
        </if>
        <if test="address!=null and address!=''">
            and address = #{address}
        </if>
        <if test="password!=null and password!=''">
            and password = #{password}
        </if>
    </where>
</select>

<!--使用where关键字-->
<select id="findByWhere1" parameterType="com.qcby.domain.User" resultType="com.qcby.domain.User">
    select * from user
    <where>
        <if test="username != null and username != ''">
            and username like #{username}
        </if>
        <if test="sex != null and sex != ''">
            and sex = #{sex}
        </if>
    </where>
</select>
List<User> findUser(User user);
public List<User> findByWhere1(User user);
@Test
public void findUser(){
    User user = new User();
    user.setUsername("熊大");
    user.setAddress("上海");
    user.setPassword("123");
    List<User> users = userDao.findUser(user);
    for(User user1:users){
        System.out.println(user1.toString());
    }
}
@Test
public void testFindByWhere1(){
    User user = new User();
    user.setSex("女");
    List<User> users = mapper.findByWhere1(user);
    System.out.println(users);
}

3.<choose>+<when>+<otherwise>
多条件分支判断(类似 Java 的 if-else if-else),仅执行第一个满足条件的 <when> 片段,都不满足则执行 <otherwise>;

<!-- 相当于if else-if else -->
<select id="selectUserByChoose" resultType="com.lsl.entity.User" parameterType="com.lsl.entity.User">
    select * from user
    <where>
        <choose>
            <when test="username!=null and username!=''">
                username = #{username}
            </when>
            <when test="birthday!=null">
                and birthday=#{birthday},
            </when>
            <otherwise>
                and id=#{id}
            </otherwise>
        </choose>
    </where>
</select>

4.<trim>
自定义 SQL 片段的前后缀处理,通过 prefix(前缀)、suffix(后缀)、prefixOverrides(剔除前缀多余内容)、suffixOverrides(剔除后缀多余内容)灵活控制拼接;

<select id="selectUserByUsernameAndSex" parameterType="com.lsl.entity.User"
        resultType="com.lsl.entity.User">
    select * from user
    <trim prefix="where" prefixOverrides="and | or">
        <if test="username!=null and username!=''">
            username = #{username}
        </if>
        <if test="birthday != null">
            and  birthday = #{birthday}
        </if>
        <if test="address != null and address!=''">
            and address = #{address}
        </if>
        <if test="password !=null and password!=''">
            and password = #{password}
        </if>
    </trim>
</select>
@Test
public void selectUserByUsernameAndSex(){
    User user = new User();
    user.setUsername("熊大");
    // user.setAddress("上海");
    user.setPassword("123");
    List<User> users = userDao.selectUserByUsernameAndSex(user);
    for (User user1: users) {
        System.out.println(user1.toString());
    }
}

5.<foreach>
遍历集合(如 List、Array 等),常用于批量操作(如 IN 条件、批量插入 / 删除),可指定遍历的元素分隔符、索引、元素变量名等;

<!-- collection:当前要循环的数组或者集合   -->
<!--  item: 我们指定要循环的数组的每一个元素  -->
<!-- separator:每一个元素应该用什么来做分割   -->
<!-- open:当前循环是以什么开始   -->
<!-- close:当前循环是以什么结束   -->

<!--foreach标签 select * from user where id = 1 or id = 2 or id = 3 -->
<select id="findByIds" parameterType="com.qcby.domain.User" resultType="com.qcby.domain.User">
    select * from user
    <where>
        <foreach collection="ids" open="id = " separator="or id = " item="i">
            #{i}
        </foreach>
    </where>
</select>

<!--foreach标签 select * from user where id in (1,2,3)-->
<select id="findByIds1" parameterType="com.qcby.domain.User" resultType="com.qcby.domain.User">
    select * from user
    <where>
    <!--这个ids是user类里面的字段,private List<Integer> ids;-->
        <foreach collection="ids" open="id in ( " separator="," close=")" item="i">
            #{i}
        </foreach>
    </where>
</select>
@Test
public void testFindByIds(){
    User user = new User();
    List<Integer> ids = new ArrayList<>();
    ids.add(1);
    ids.add(2);
    ids.add(3);
    user.setIds(ids);
    List<User> list = mapper.findByIds(user);
    System.out.println(list);

}

@Test
public void testFindByIds1(){
    User user = new User();
    List<Integer> ids = new ArrayList<>();
    ids.add(1);
    ids.add(2);
    user.setIds(ids);
    List<User> list = mapper.findByIds1(user);
    System.out.println(list);

}

6.<sql>+<include>
SQL 片段复用,将通用 SQL 片段(如字段列表、条件)用 <sql> 定义,再通过 <include> 引入,减少重复代码。

<!--提取公共的SQL-->
<sql id="findAllSql">
    select * from user
</sql>

<!--编写sql语句(重点练习的) com.qcbyjy.mapper.UserMapper.findAll -->
<select id="findAll2" resultType="com.qcby.domain.User">
    <include refid="findAllSql" />
    /*select * from user*/
</select>

<!--使用where关键字-->
<select id="findByWhere2" parameterType="com.qcby.domain.User" resultType="com.qcby.domain.User">
    <include refid="findAllSql" />
    /*select * from user*/
    <where>
        <if test="username != null and username != ''">
            and username like #{username}
        </if>
        <if test="sex != null and sex != ''">
            and sex = #{sex}
        </if>
    </where>
</select>

<!--foreach标签 select * from user where id in (1,2,3)-->
<select id="findByIds2" parameterType="com.qcby.domain.User" resultType="com.qcby.domain.User">
    <include refid="findAllSql" />
    /*select * from user*/
    <where>
        <foreach collection="ids" open="id in ( " separator="," close=")" item="i">
            #{i}
        </foreach>
    </where>
</select>
@Test
public void testFindAll2(){
    List<User> users = mapper.findAll2();
    System.out.println(users);
}

@Test
public void testFindByWhere2(){
    User user = new User();
    user.setSex("女");
    List<User> users = mapper.findByWhere2(user);
    System.out.println(users);
}

@Test
public void testFindByIds2(){
    User user = new User();
    List<Integer> ids = new ArrayList<>();
    ids.add(1);
    ids.add(2);
    ids.add(3);
    user.setIds(ids);
    List<User> list = mapper.findByIds2(user);
    System.out.println(list);

}

5.2 修改

5.2.1 普通修改

<!-- 修改 -->
<update id="update" parameterType="com.qcby.domain.User">
    update user set username = #{username},birthday = #{birthday},sex = #{sex},address=#{address} where id = #{id}
</update>
public void update(User user);
@Test
public void testUpdate() throws Exception {
    User user = mapper.findById(5);
    user.setUsername("小凤");
    mapper.update(user);
    session.commit();
}

5.2.2 动态sql

只有 是仅适用于 UPDATE 操作的标签,其他标签(、++、、、、+)并非 “所有操作都完全适用”,而是存在主要适用场景和次要 / 极少适用场景,核心区别在于 “是否为该操作的常规需求”。(笔记里的动态sql只是举例一部分例子,没有全部举)
比如:
  • 主要用于 SELECT(拼接查询条件),虽理论可用于复杂 DELETE,但并非 DELETE 操作的常规用法;
  • 系列主要用于 SELECT 的多分支条件筛选,在增、删、改中极少用到;
  • 虽可用于批量查、增、删,但单条增 / 删 / 查中基本用不到。
  • 、、+是通用标签(所有 CRUD 操作均可能用到)
用于 UPDATE 语句,自动处理标签内 SQL 结尾的多余逗号,同时生成 SET 关键字;
<update id="update" parameterType="com.lsl.entity.User">
    update user
    <set>
        <if test="username!=null and username!=''">
            username = #{username},
        </if>
        <if test="birthday!=null">
            birthday=#{birthday},
        </if>
        <if test="address!=null and address!=''">
            address = #{address},
        </if>
        <if test="password!=null and password!=''">
            password = #{password},
        </if>
    </set>
        where id=#{id}
</update>
int update(User user);
@Test
public void update(){
    User user = new User();
    user.setUsername("sssssss");
    user.setId(1);
    userDao.update(user);
    session.commit();
}

2.<trim>

<update id="trimUpdate" parameterType="com.lsl.entity.User">
    update user
    <trim prefix="set" suffixOverrides=",">
        <if test="username!=null and username!=''">
            username = #{username},
        </if>
        <if test="birthday != null">
            birthday = #{birthday},
        </if>
        <if test="address != null and address!=''">
            address = #{address},
        </if>
        <if test="password !=null and password!=''">
            password = #{password},
        </if>
    </trim>
    where id = #{id}
</update>
@Test
public void trimUpdate(){
    User user = new User();
    user.setUsername("老王");
    user.setId(1);
    userDao.trimUpdate(user);
    session.commit();
}

5.3 新增

5.3.1 普通新增

<insert id="insert" parameterType="com.lsl.entity.User">
    insert into user (username ,birthday,sex,address,password) value(#{username},#{birthday},#{sex},#{address},#{password})
</insert>

<!-- 返回主键,我们主键需要设置为自动递增 注册 -->
<insert id="insert1" parameterType="com.qcby.domain.User">
    /*
    <selectKey> 是 MyBatis 中用于获取插入操作后自动生成的主键值的标签
    keyProperty表示要返回的属性名称,是直接将查询到的主键值赋值给这个传入的 User 对象的 id 属性。
    order取值AFTER表示插入数据后的行为
    resultType表示返回值的类型,此处为 Integer,与 User 的 id 类型匹配
    */
    <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
        select last_insert_id();
    </selectKey>
    insert into user (username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})
</insert>

标签通过 keyProperty="id" 指定的 “id”,是直接将查询到的主键值赋值给这个传入的 User 对象的 id 属性。

执行完成后,你无需额外获取新对象,直接通过原来的 user.getId() 就能拿到自动生成的主键值。

Integer insert(User user);
public void insert1(User user);
@Test
public void insert(){
    User user = new User();
    user.setSex("女");
    user.setAddress("保定");
    user.setUsername("sss");
    userDao.insert(user);
    session.commit();
}

@Test
public void testInsert() throws Exception {
    User user = new User();
    user.setUsername("美美");
    user.setBirthday(new Date());
    user.setSex("男");
    user.setAddress("顺义");
    mapper.insert(user);
    session.commit();
    System.out.println(user.getId());
}

5.3.2 动态sql

1.<foreach>

<!-- collection:当前要循环的数组或者集合   -->
<!--  item: 我们指定要循环的数组的每一个元素  -->
<!-- separator:每一个元素应该用什么来做分割   -->
<!-- open:当前循环是以什么开始   -->
<!-- close:当前循环是以什么结束   -->

<!--insert into 表名 (字段) values (值),(值)-->
<insert id="insertMoreByList" >
    insert into user(username,birthday,sex,address) values
    <foreach collection="users" item="user" separator=",">
        (#{user.username},#{user.birthday},#{user.sex},#{user.address})
    </foreach>
</insert>
    //@Param的核心作用是给参数指定名称,方便在 XML 中通过${名称}或#{名称}引用
    int insertMoreByList(@Param("users") List<User> users);
    @Test
    public void insertMoreByList(){
        User user1 = new User("小赵",new Date(),"男","保定");
        User user2 = new User("小张",new Date(),"男","保定");
        User user3 = new User("小李",new Date(),"男","保定");
        List<User> users = Arrays.asList(user1,user2,user3);
        userDao.insertMoreByList(users);
        session.commit();
    }
    注:
    • 不写parameterType完全可以传参,MyBatis 会自动推断类型;
    • @Param是显式指定参数名的方式,更清晰且避免歧义,上面代码中不写也可以
    • 使用时只需导入org.apache.ibatis.annotations.Param即可,前提是项目已引入 MyBatis 依赖。

    5.4 删除

    5.4.1 普通删除

    <delete id="delete" parameterType="java.lang.Integer">
        delete from user where id = #{id}
    </delete>
    @Test
    public void delete(){
        userDao.delete(5);
        session.commit();//事务提交
    }

    5.4.2 动态sql

    1.<foreach>

    <!-- collection:当前要循环的数组或者集合   -->
    <!--  item: 我们指定要循环的数组的每一个元素  -->
    <!-- separator:每一个元素应该用什么来做分割   -->
    <!-- open:当前循环是以什么开始   -->
    <!-- close:当前循环是以什么结束   -->
    <!--delete from user where id in (1,2,3,4,5); -->
    <delete id="deleteMoreByArray">
        delete from user where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>
    int deleteMoreByArray(@Param("ids") Integer[] ids);//强制入参
    @Test
    public void deleteMoreByArray(){
        Integer[] integer = new Integer[]{6,7,8};
        userDao.deleteMoreByArray(integer);
        session.commit();
    }

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值