【开发】MyBatis初识

【开发】MyBatis初识

Maven构建

用Maven构建的其中一个重要因素是可以用坐标轻松地导入依赖:

<dependencies>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.19</version>
</dependency>

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>9.6.0</version>
</dependency>
</dependencies>

另外是Maven封装好了各种开发时需要的操作:编译、打包、调试等

初入门

先是根据官网的入门指引一步步做了个demo

引入依赖之后,写一个mybatis-config.xml,根据模板修改

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/jdbc?useSSL=false&amp;allowPublicKeyRetrieval=true"/>
                <property name="username" value="role"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

其中需要把allowPublicKeyRetrival设置为true(如果useSSL=false)的话

接着UserMapper.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="test">
    <select id="selectStudent" resultType="com.Student">
        SELECT * FROM student
    </select>
</mapper>

id就是函数识别映射语句的字符串,resultType是最终返回的内容

回到Student类

package com;

import java.math.BigDecimal;

public class Student {
    String name;
    String gender;
    int age;
    BigDecimal score;
    String city;

    public Student() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public BigDecimal getScore() {
        return score;
    }

    public void setScore(BigDecimal score) {
        this.score = score;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }


    @Override
    public String toString() {
        return "=====================\n" +
                "名字:" + name + "\n" +
                "性别:" + gender + "\n" +
                "年龄:" + age + "\n" +
                "分数:" + score + "\n" +
                "城市:" + city + "\n" +
                "======================\n";
    }
}

一开始我用的是构造函数,然后运行时报错。看了报错信息发现赋值的顺序似乎并不是确定的,因此可能会有类型错误的问题。所以把构造改成setter,就能保证精准赋值了。

最后是测试代码:

import com.Student;
import org.apache.ibatis.io.*;
import org.apache.ibatis.session.*;
import java.io.*;
import java.util.List;

public class MyBatisDemo {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();

        List<Student> student = sqlSession.selectList("test.selectStudent");

        for (Student stu : student) {
            System.out.println(stu);
        }

        sqlSession.close();
    }
}

Mapper代理开发

事实上官方更加建议把接口封装成一个mapper类,通过mapper去映射指令,这样不仅语义好,而且便于编译器识别拼写问题

明确一下流程:

  • 定义与SQL映射文件同名的Mapper接口,并且将Mapper接口与映射文件放在同一目录下;
  • 设置SQL映射文件的namespace属性为Mapper接口全限定名;
  • 在Mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致;
  • 编码:
    • 通过SqlSession的getMapper方法获取Mapper接口的代理对象;
    • 调用对应方法完成sql的执行。

先是把配置文件和类分别放在resources和java的相同包名下,注意resources处需要用/而不能用.来分割包

<?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.mapper.UserMapper">
    <select id="selectStudent" resultType="com.Student">
        SELECT * FROM student
    </select>
</mapper>
package com.mapper;

import com.Student;
import java.util.List;

public interface UserMapper {
    List<Student> selectStudent();
}

在mybatis-config.xml处也要增加一下mapper路径,有两种形式:

<mapper resource="com/mapper/UserMapper.xml"/>
<mapper class = "com.mapper.UserMapper"/>

第一种是资源文件的导入方式,只能识别路径,而另一种是类文件的导入方式,可以识别类,两者选择一个都能使用

最后把本来selectList("")的部分修改成Mapper的形式即可

import com.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import com.mapper.UserMapper;

public class MyBatisDemoMapper {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<Student> student = mapper.selectStudent();

        for (Student stu : student) {
            System.out.println(stu);
        }

        sqlSession.close();
    }
}

配置文件初步解释

环境配置(environmets)

MyBatis可以配置成适应多种环境,这种机制有助于将SQL映射与多种数据库中。

注意的地方是每个数据库都要对应一个SqlSession实例

<environments default="development">
	<environment id="development">
    	<transactionManager type="JDBC"/>
    	<dataSource type="POOLED">
        	<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        	<property name="url" value="jdbc:mysql://localhost:3306/jdbc?useSSL=false&amp;allowPublicKeyRetrieval=true"/>
        	<property name="username" value="role"/>
        	<property name="password" value="123456"/>
    	</dataSource>
	</environment>
</environments>

别名(typeAlias)

为了降低冗余书写,可以给一些Java类型设置成缩写名字

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

别名是不区分大小写的,因此给内置类型用了比较特殊的命名风格,如果是小写开头就在前面加下划线,如果是大写开头就改成小写,自己创建的类也尽量使用小写别名

映射器(Mappers)

这一块上一部分有细讲,就是告诉MyBatis去什么地方找配置文件

功能-查询

查询所有数据

先前写的就是查询所有数据,不多赘述。

不过在这里有一个小问题,由于命名风格的问题,SQL的字段命名和Java的变量命名可能不一样,比如userName和user_name。这个时候如果不加修饰的话,编译过程中是无法识别对应字段进行赋值的。

有两种解决方法:

  • 给字段起别名
SELECT user_name as UserName FROM my_table;

不过问题是每个查询都要起一个别名,所以可以抽取重复片段

在xml文件中写:

<sql id = "student_column">
    student_name as studentName
</sql>

<mapper namespace="com.mapper.UserMapper">
    <select id="selectStudent" resultType="com.Student">
        SELECT 
        	<include refid = "student_colmn"/>
        FROM student;
    </select>
</mapper>

可是这样依旧是硬编码的,假如需要更灵活地查询,这样操作还是比较麻烦

  • resultMap

这也是个映射的方法,就可以简单地去把字段映射到变量名

<resultMap id = "studentResultMap" type = "Student">
    <result column = "student_name" property = "studentName"/>
    <result column = "student_score" property = "studentScore"/>
    <!-- 如果是主键的映射,则result改成id -->
</resultMap>

这样写的话,原本resultType的区域就得改成:

<select id = "selectStudent" resultMap = "studentResultMap">
    SELECT * FROM student;
</select>

查看详情

有时候会需要一些特定行的详情,在JDBC中用PreparedStatement就是通过setT(id, content)去传入参数的,而在配置文件中也差不多,只不过需要特别的表示方式

<select id="selectById" resultType="com.Student">
	SELECT * FROM student WHERE id = #{id};
</select>

而对应声明的地方可以写成:

Student selectById(int id);

使用的时候就简单了:

import com.Student;
import com.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MyBatisSelectById {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        Student student = mapper.selectById(3);
        System.out.println(student);

        sqlSession.close();
    }
}

其中符号表示有两种:

  • #{}:会被替换成?,避免SQL注入
  • ${}:是sql拼接,容易有SQL注入,不过一般会在表名、列名不固定时使用

查询中其实会有一些特殊符号无法直接使用(小于号等),有两种处理方式:

  • 转义字符:
SELECT * FROM student WHERE id &lt; 3;

这种方式在特殊字符较少的时候使用

  • CDATA区,在CDATA区中就不会被特殊编译了:
<select id="selectById" resultType="com.Student">
    <![CDATA[
        SELECT * FROM student WHERE id = #{id};
    ]]>
</select>

条件查询

条件查询有三种方式,共用同一种接口:

<select id="selectByCondition" resultType="com.Student">
    SELECT * FROM student
    WHERE
    name LIKE #{name}
    AND gender LIKE #{gender}
    AND city LIKE #{city};
</select>
  • 传入对象:

定义:

List<Student> selectByCondition(Student student);

使用:

public class MyBatisSelectByConditionClass {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        Student stu = new Student();
        stu.setName("王五");
        stu.setGender("男");
        stu.setCity("佛山");
        List<Student> student = mapper.selectByCondition(stu);
        System.out.println(student);

        sqlSession.close();
    }
}
  • 散装数据加注解
List<Student> selectByCondition(@Param("name") String name,
                                @Param("gender") String gender,
                                @Param("city") String city);

其中注解里面的字符串需要与xml对应

public class MyBatisSelectByConditionAnnotation {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<Student> student = mapper.selectByCondition("王五", "男", "佛山");
        System.out.println(student);

        sqlSession.close();
    }
}
  • 传入键值对
List<Student> selectByCondition(Map map);

接着就可以分别把字段名和值作为键值对放进去,再查询

public class MyBatisSelectByConditionMap {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        Map stu = new HashMap();
        stu.put("name", "王五");
        stu.put("gender", "男");
        stu.put("city", "佛山");
        List<Student> student = mapper.selectByCondition(stu);
        System.out.println(student);

        sqlSession.close();
    }
}

动态SQL查询

前面的表字段名有点太简单了,我让GPT生成了一张字段名含下划线,比较复杂的表

CREATE TABLE library_borrow_record (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
    reader_name VARCHAR(50) NOT NULL COMMENT '借阅人姓名',
    book_title VARCHAR(100) NOT NULL COMMENT '书名',
    borrow_date DATE NOT NULL COMMENT '借书日期',
    due_date DATE NOT NULL COMMENT '应还日期',
    return_date DATE COMMENT '实际归还日期',
    borrow_status VARCHAR(20) NOT NULL COMMENT '借阅状态',
    fine_amount DECIMAL(8,2) DEFAULT 0.00 COMMENT '罚金金额',
    shelf_code VARCHAR(30) COMMENT '书架编号',
    operator_name VARCHAR(50) COMMENT '办理人姓名',
    renew_count INT DEFAULT 0 COMMENT '续借次数',
    is_damaged TINYINT DEFAULT 0 COMMENT '是否损坏:0否1是',
    remark_text VARCHAR(200) COMMENT '备注信息'
);
INSERT INTO library_borrow_record
(reader_name, book_title, borrow_date, due_date, return_date, borrow_status, fine_amount, shelf_code, operator_name, renew_count, is_damaged, remark_text)
VALUES
('张明', '三体', '2026-03-01', '2026-03-15', '2026-03-14', '已归还', 0.00, 'A_01_03', '李管理员', 1, 0, '按时归还'),
('王雨', '人类简史', '2026-03-05', '2026-03-19', NULL, '借阅中', 0.00, 'B_02_07', '赵管理员', 0, 0, '首次借阅'),
('陈可', '解忧杂货店', '2026-02-20', '2026-03-06', '2026-03-10', '已归还', 4.50, 'C_03_02', '李管理员', 2, 0, '逾期四天'),
('刘晨', '百年孤独', '2026-03-08', '2026-03-22', NULL, '借阅中', 0.00, 'D_01_05', '周管理员', 0, 0, '书籍较新'),
('孙婷', '小王子', '2026-02-25', '2026-03-11', '2026-03-11', '已归还', 0.00, 'A_04_01', '赵管理员', 1, 0, '无'),
('周杰', '活着', '2026-03-02', '2026-03-16', '2026-03-18', '已归还', 3.00, 'B_05_06', '李管理员', 0, 1, '封面轻微破损');

先把对应类写出来:

package com.pojo;

import java.math.BigDecimal;
import java.time.LocalDate;

public class LibraryBorrowRecord {
    private Integer id;
    private String readerName;
    private String bookTitle;
    private LocalDate borrowDate;
    private LocalDate dueDate;
    private LocalDate returnDate;
    private String borrowStatus;
    private BigDecimal fineAmount;
    private String shelfCode;
    private String operatorName;
    private Integer renewCount;
    private Integer isDamaged;
    private String remarkText;

    public void setId(Integer id) { this.id = id; }
    public void setReaderName(String readerName) { this.readerName = readerName; }
    public void setBookTitle(String bookTitle) { this.bookTitle = bookTitle; }
    public void setBorrowDate(LocalDate borrowDate) { this.borrowDate = borrowDate; }
    public void setDueDate(LocalDate dueDate) { this.dueDate = dueDate; }
    public void setReturnDate(LocalDate returnDate) { this.returnDate = returnDate; }
    public void setBorrowStatus(String borrowStatus) { this.borrowStatus = borrowStatus; }
    public void setFineAmount(BigDecimal fineAmount) { this.fineAmount = fineAmount; }
    public void setShelfCode(String shelfCode) { this.shelfCode = shelfCode; }
    public void setOperatorName(String operatorName) { this.operatorName = operatorName; }
    public void setRenewCount(Integer renewCount) { this.renewCount = renewCount; }
    public void setIsDamaged(Integer isDamaged) { this.isDamaged = isDamaged; }
    public void setRemarkText(String remarkText) { this.remarkText = remarkText; }

    public Integer getId() { return id; }
    public String getReaderName() { return readerName; }
    public String getBookTitle() { return bookTitle; }
    public LocalDate getBorrowDate() { return borrowDate; }
    public LocalDate getDueDate() { return dueDate; }
    public LocalDate getReturnDate() { return returnDate; }
    public String getBorrowStatus() { return borrowStatus; }
    public BigDecimal getFineAmount() { return fineAmount; }
    public String getShelfCode() { return shelfCode; }
    public String getOperatorName() { return operatorName; }
    public Integer getRenewCount() { return renewCount; }
    public Integer getIsDamaged() { return isDamaged; }
    public String getRemarkText() { return remarkText; }

    @Override
    public String toString() {
        return "LibraryBorrowRecord{" +
                "id=" + id +
                ", readerName='" + readerName + '\'' +
                ", bookTitle='" + bookTitle + '\'' +
                ", borrowDate=" + borrowDate +
                ", dueDate=" + dueDate +
                ", returnDate=" + returnDate +
                ", borrowStatus='" + borrowStatus + '\'' +
                ", fineAmount=" + fineAmount +
                ", shelfCode='" + shelfCode + '\'' +
                ", operatorName='" + operatorName + '\'' +
                ", renewCount=" + renewCount +
                ", isDamaged=" + isDamaged +
                ", remarkText='" + remarkText + '\'' +
                '}';
    }
}

接着去写配置文件中的映射:

<resultMap id = "RecordMap" type = "com.pojo.LibraryBorrowRecord">
    <id column = "id" property = "id"/>
    <result column = "reader_name" property = "readerName"/>
    <result column = "book_title" property = "bookTitle"/>
    <result column = "borrow_date" property = "borrowDate"/>
    <result column = "due_date" property = "dueDate"/>
    <result column = "return_date" property = "returnDate"/>
    <result column = "borrow_status" property = "borrowStatus"/>
    <result column = "fine_amount" property = "fineAmount"/>
    <result column = "shelf_code" property = "shelfCode"/>
    <result column = "operator_name" property = "operatorName"/>
    <result column = "renew_count" property = "renewCount"/>
    <result column = "is_damaged" property = "isDamaged"/>
    <result column = "remark_text" property = "remarkText"/>
</resultMap>

中途我犯了个错误,type区域需要的是实体类而非对应Mapper类

接下来进行动态SQL的说明

在用户的SQL查询中,包含的条件不一定每一项都存在,这个时候需要根据用户给的条件去动态地生成SQL语句

而MyBatis提供了一些标签便于进行这样的操作:

  • if
  • choose(when, otherwise)
  • trim(where, set)
  • foreach

if标签

首先看if的用法,在test里面写上条件即可

<select id="selectByCondition" resultMap="RecordMap">
    SELECT * FROM library_borrow_record
    WHERE
    <if test="readerName != null">
        reader_name like #{readerName}
    </if>
    <if test="bookTitle != null and bookTitle != '' ">
        and book_title like #{bookTitle}
    </if>
    <if test="operatorName != null and operatorName != '' ">
        and operator_name like #{operatorName}
    </if>
</select>

可是这会有一个问题,假如没有给readerName的话,and打头的条件显然是报错的

因此处理方法是在最开始加上一个恒等式,这样就可以让每个条件都独立存在

<select id="selectByCondition" resultMap="RecordMap">
    SELECT * FROM library_borrow_record
    WHERE 1 = 1
    <if test="readerName != null">
        and reader_name like #{readerName}
    </if>
    <if test="bookTitle != null and bookTitle != '' ">
        and book_title like #{bookTitle}
    </if>
    <if test="operatorName != null and operatorName != '' ">
        and operator_name like #{operatorName}
    </if>
</select>

不过MyBatis其实提供了一个where标签,可以自动检查是否为第一个,进而自己去掉and

<select id="selectByCondition" resultMap="RecordMap">
    SELECT * FROM library_borrow_record
    <where>
        <if test="readerName != null">
            and reader_name like #{readerName}
        </if>
        <if test="bookTitle != null and bookTitle != '' ">
            and book_title like #{bookTitle}
        </if>
        <if test="operatorName != null and operatorName != '' ">
            and operator_name like #{operatorName}
        </if>
    </where>
</select>

choose标签

类似switch语句,只会选择其中一个查询,如果有多个存在,那优先级会取决于配置文件中的顺序

<select id="selectBySingleCondition" resultMap="RecordMap">
    SELECT * FROM library_borrow_record WHERE
    <choose>
        <when test="readerName != null">
            reader_name like #{readerName}
        </when>
        <when test="bookTitle != null and bookTitle != '' ">
            book_title like #{bookTitle}
        </when>
        <when test="operatorName != null and operatorName != '' ">
            operator_name like #{operatorName}
        </when>
        <otherwise>
            1 = 1
        </otherwise>
    </choose>
</select>

不用otherwise标签的话,如果直接这样,在没有任何条件的时候就会报错,这个时候可以将WHERE改成where标签,这样效果就与原先的otherwise一致了

功能-修改与添加

添加行

  • 普通添加

先是添加一个接口函数:

public void addRecord(LibraryBorrowRecord record);

静态的没有什么特别大的问题,主要注意拼写

<insert id="addRecord">
    INSERT INTO library_borrow_record
    ( reader_name, book_title, borrow_date, due_date, return_date, borrow_status, fine_amount,
    shelf_code, operator_name, renew_count, is_damaged, remark_text )
    VALUES
    ( #{readerName}, #{bookTitle}, #{borrowDate}, #{dueDate},
    #{returnDate}, #{borrowStatus}, #{fineAmount},
    #{shelfCode}, #{operatorName}, #{renewCount},
    #{isDamaged}, #{remarkText});
</insert>

然后在sqlSession中,对于这种修改操作,需要自行设置是否自动提交

如果不自动提交,在事务结束之后需要

sqlSession.commit();

如果设置自动提交的话

public class AddColumn {
    public static void main(String[] args) throws IOException {
        String resources = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resources);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //在此处参数填上true代表自动提交,默认不自动
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        LibraryBorrowRecordMapper mapper = sqlSession.getMapper(LibraryBorrowRecordMapper.class);

        LibraryBorrowRecord record = new LibraryBorrowRecord();
        /*
        	此处setter过程省略
        */

        mapper.addRecord(record);
    }
}
  • 主键返回

有时候添加操作之后是需要获取插入的主键的值的,比如购物平台上添加订单的订单项时,订单项中需要获取订单id

操作很简单,在原先添加行的位置修改为:

<insert id="addRecord" useGeneratedKeys="true" keyProperty="id">

这样就能让对象的id被赋值了

对于前面的代码,在最后输出record.setId(),前后对比可以得知主键返回的作用

修改

  • 修改全部字段

这里全都是静态的,因此就不多写了

  • 修改动态字段

这一功能可以涵盖上一个功能,所以重点讲这个

接口函数:

public void updateRecord(LibraryBorrowRecord record);

可以想到类似于动态查询的问题,如果某个字段要被修改并且它是最后一个,那么正常来讲带着逗号的话肯定无法过编译

所以MyBatis提供了set标签解决了这个问题

<update id="updateRecord">
    UPDATE library_borrow_record
    <set>
        <if test="readerName != null and readerName != '' ">
            reader_name = #{readerName},
        </if>
        <if test="bookTitle != null and bookTitle != '' ">
            book_title = #{bookTitle},
        </if>
        <if test="remarkText != null and remarkText != '' ">
            remark_text = #{remarkText},
        </if>
    </set>
    where id = #{id}
</update>
public class UpdateColumn {
    public static void main(String[] args) throws IOException {
        String resources = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resources);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        LibraryBorrowRecordMapper mapper = sqlSession.getMapper(LibraryBorrowRecordMapper.class);

        LibraryBorrowRecord record = new LibraryBorrowRecord();
        record.setId(8);
        record.setReaderName("嘉琪");
        record.setRemarkText("轻微破损痕迹");

        mapper.updateRecord(record);
    }
}

功能-删除

删除一个

这就简单了

public void deleteById(int id);
<delete id="deleteById">
    DELETE FROM library_borrow_record WHERE id = #{id};
</delete>

批量删除

对于“批量”来讲,一组数据的长度是不定的,也就是说不能硬编码删除的数量

所以MyBatis提供了foreach标签去完成变长数据的处理

public void deleteByIds(@Param("ids") int[] ids);
<delete id="deleteByIds">
    DELETE FROM library_borrow_record
    WHERE id in
    <foreach collection="ids" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
</delete>

讲解一下其中的foreach部分,可以看出这其实是生成了一个字符串

collection代表的是标签名,在这个例子中,需要用@Param注解将原先的变量名用ids表示用于识别

MyBatis会将数组参数封装成一个Map集合,默认key是array,而值就是数组,可以通过@Param注解改变默认key的别名

item就是单个元素对应的字段名

seperator和open、close都是用来生成语句的

参数传递

前面提到过可以散装传入参数,但是需要用@Param注解

因为MyBatis会将多个参数封装成Map,它自身会有默认键arg0,arg1,.……和param1,param2,……

因此可以通过使用这两种键去在xml中调用参数

但是这样明显语义不是很好,所以@Param["str"]就可以起名字,这样就会有这样的变成:

map.put("str", value);

就结论而言,POJO类型和Map类型不需要另加注解,而Collection、List、Array和多参数都需要注解,这样才便于开发

注解开发

xml文件配置其实比较复杂,如果不去做比较复杂的事务的话,可以考虑直接用提供的注解,MyBatis给了以下几种注解

  • @Select
  • @Insert
  • @Update
  • @Delete

具体例子如下:

@Select("SELECT * FROM users WHERE id = #{id}")
public User selectById(int id);
posted @ 2026-03-29 14:38  R4y  阅读(5)  评论(0)    收藏  举报