Mybatis教程

快速入门

核心配置文件

  • 必要配置:
  • 配置数据源,配置映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!--配置日志输出,可以查看底层的sql-->
    <settings>
        <!--配置mybatis自带的标注日志输出-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!--配置别名,一定要配置在前面-->
    <typeAliases>
        <typeAlias type="com.yangyi.entity.Monster" alias="Monster"/>
    </typeAliases>

    <!--配置环境-->
    <environments default="development">
        <environment id="development">
            <!--配置事务管理器-->
            <transactionManager type="JDBC"/>
            <!--配置数据源-->
            <dataSource type="POOLED">
                <!--配置驱动-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!--配置连接mysql-url
                老韩解读:
                1. jdbc:mysql 协议
                2. 127.0.0.1:3306 : 指定连接mysql的ip+port
                3. mybatis: 连接的DB
                4. useSSL=true 表示使用安全连接
                5. &amp; 表示 & 防止解析错误
                6. useUnicode=true : 使用unicode 作用是防止编码错误
                7. characterEncoding=UTF-8 指定使用utf-8, 防止中文乱码
                8. 老韩温馨提示:不要背,直接使用即可
                -->
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="hsp"/>
            </dataSource>
        </environment>
    </environments>
    <!--配置映射文件-->
    <mappers>
        <!--配置映射文件路径-->
        <mapper resource="com/yangyi/mapper/MonsterMapper.xml"/>
    </mappers>
</configuration>

配置接口映射文件

  • mapper标签的namespace属性设置为接口的全类名
  • SQL语句标签的id属性配置接口的具体方法,parameterType属性配置方法的形参,resultType属性配置方法的返回值类型,都要与接口中的形参和返回值类型保持一致
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTP Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--mapper映射文件,该文件指定实现接口的方法-->
<!--namespace属性指定该xml文件与接口对应-->
<mapper namespace="com.yangyi.mapper.MonsterMapper">
    <!--insert标签表示实现方法的方式,标签内写SQL语句-->
    <!--id属性指定实现接口中的方法名-->
    <!--parameterType属性表示接口形参的类型-->
    <!--resultType属性表示接口的返回类型-->
    <!--useGeneratedKeys="true"与 keyProperty="id"配合使用可以获取自增长的id-->
    <insert id="addMonster" parameterType="com.yangyi.entity.Monster" useGeneratedKeys="true" keyProperty="id">
        insert into monster (age, birthday, email, gender, name, salary) value
            (#{age},#{birthday},#{email},#{gender},#{name},#{salary})
    </insert>

    <delete id="deleteMonster" parameterType="java.lang.Integer">
        delete from monster where id = #{id}
    </delete>

    <!--查询一个monster的接口-->
    <select id="selectMonster" parameterType="java.lang.Integer" resultType="com.yangyi.entity.Monster">
    select * from monster where id = #{id}
    </select>

    <!--查询所有monster的接口,返回类型仍然是Monster类型,而不是List类型-->
    <select id="selectAllMonster" resultType="Monster">
        select * from monster
    </select>

    <update id="updateMonster" parameterType="Monster">
        update monster set age = #{age},birthday = #{birthday},email = #{email},gender = #{gender},name = #{name},salary = #{salary} where id = #{id}
    </update>
</mapper>

Monster接口

package com.yangyi.mapper;

import com.yangyi.entity.Monster;

import java.util.List;

/**
 * @Projectname: mybatis
 * @Filename: MonsterMapper
 * @Author: 杨逸
 * @Data:2023/9/14 9:52
 * @Description: 操作monster表的接口
 */
public interface MonsterMapper {
    //添加monster
    void addMonster(Monster monster);
    //删除
    void deleteMonster(Integer id);
    //查询一个monster
    Monster selectMonster(Integer id);
    //查询所有monster
    List<Monster> selectAllMonster();
    //更新
    void updateMonster(Monster monster);
}

使用Mybatis的程序

  • mybatis工具类
  • 通过Resource对象获取核心配置文件的输入流
  • 通过SqlSessionFactory对象获取与数据库通信的session对象
  • 通过Session对象获取mapper映射借口的代理对象
  • 通过mapper映射代理对象进行对数据库的操作
package com.yangyi.util;

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;

/**
 * @Projectname: mybatis
 * @Filename: MybatisUtils
 * @Author: 杨逸
 * @Data:2023/9/14 10:34
 * @Description: 工具类
 */
public class MybatisUtils {
    public static  SqlSessionFactory sqlSessionFactory;
    //静态代码块初始化
    static {
        String resource = "mybatis-config.xml";
        try {
            InputStream resourceAsStream = Resources.getResourceAsStream(resource);
            //获取会话工厂
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

  • 测试
package com.yangyi.mapper;

import com.yangyi.entity.Monster;
import com.yangyi.util.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;

import java.util.Date;
import java.util.List;

import static org.junit.Assert.*;

/**
 * @Projectname: mybatis
 * @Filename: MonsterMapperTest
 * @Author: 杨逸
 * @Data:2023/9/14 10:41
 * @Description: 测试方法
 */
public class MonsterMapperTest {

    private SqlSession session = null;
    private MonsterMapper mapper = null;

    /**
     * 初始化
     */
    @Before
    public void init(){
        session = MybatisUtils.getSqlSession();
        System.out.println("会话对象初始化");
        //获取映射对象
        mapper = session.getMapper(MonsterMapper.class);
    }

    /**
     * 插入数据
     */
    @Test
    public void addMonster() {
        Monster monster = new Monster(null,888,"kk1","1231@163.com",new Date(),9899,1);
        //插入一条数据
        mapper.addMonster(monster);
        System.out.println("插入一条数据");
        //获取插入数据的子增长id
        System.out.println("monster.getId() = " + monster.getId());
        //提交事务,默认是不提交的
        session.commit();
        session.close();
    }

    /**
     * 删除
     */
    @Test
    public void deleteMonster(){
        System.out.println("删除monster");
        mapper.deleteMonster(1);
        session.commit();
        session.close();
    }

    /**
     * 查询一个monster
     */
    @Test
    public void selectMonster(){
        Monster monster = mapper.selectMonster(3);
        System.out.println("查询");
        System.out.println("monster = " + monster);
        session.close();
    }

    /**
     * 查询所有monster
     */
    @Test
    public void selectAllMonster(){
        List<Monster> monsters = mapper.selectAllMonster();
        System.out.println("查询所有monster");
        for (Monster monster : monsters) {
            System.out.println(monster);
        }
    }

    /**
     * 更新
     */
    @Test
    public void updateMonster(){
        Monster monster = new Monster(3,988,"jjj","123123@163.com",new Date(),98939,0);
        System.out.println("数据更新");
        mapper.updateMonster(monster);
        session.commit();
        session.close();

    }
}

启用mybatis的日志

  • 注意需要配置在前面
 <!--配置日志输出,可以查看底层的sql-->
    <settings>
        <!--配置mybatis自带的标注日志输出-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

通过SqlSession操作数据库,调用Mybatis原生API

  • 调用时,第一个参数是接口方法的完整名称,第二参数是接口方法的形参列表
package com.yangyi.NavicatAPI;

import com.yangyi.entity.Monster;
import com.yangyi.util.MybatisUtils;
import org.apache.ibatis.session.SqlSession;

import java.util.Date;
import java.util.List;

/**
 * @Projectname: mybatis
 * @Filename: NavicatAPI
 * @Author: 杨逸
 * @Data:2023/9/19 14:33
 * @Description: 演示Mybatis原生curd API的使用
 */
public class NavicatAPI {
    private SqlSession session = MybatisUtils.getSqlSession();


    public void insert(){
        //准备数据
        Monster monster = new Monster(null,88,"yy","1231@163.com",new Date(),90899,1);

        //第一个参数是映射接口的完整方法名,第二个参数是方法的形参列表
        session.insert("com.yangyi.mapper.MonsterMapper.addMonster",monster);
        session.commit();

        System.out.println("使用Mybatis原生API插入数据成功");
    }

    public void delete(){
        int id = 3;
        //第一个参数是映射接口的完整方法名,第二参数是接口方法的形参列表
        session.delete("com.yangyi.mapper.MonsterMapper.deleteMonster",id);
        session.commit();

        System.out.println("使用Mybatis原生API删除数据成功");
    }

    public void update(){
        Monster monster = new Monster(5,88,"yy","1231@163.com",new Date(),90899,1);
        session.delete("com.yangyi.mapper.MonsterMapper.updateMonster",monster);
        session.commit();

        System.out.println("使用Mybatis原生API更新数据成功");
    }

    public void select(){
        List<Object> objects = session.selectList("com.yangyi.mapper.MonsterMapper.selectAllMonster");
        for (Object object : objects) {
            System.out.println("object = " + object);
        }
        System.out.println("使用Mybatis原生API查询数据成功");
    }
}

Mybatis-config.xml配置文件的详解

properties标签
  • 引入外部的配置文件
setting标签
  • 设置全局的参数,比如:日志输出
TypeAliases标签
  • 设置类的别名
  • 注意要配置前面
<typeAliases>
        <!--只设置一个别名-->
        <typeAlias type="com.yangyi.entity.Monster" alias="Monster"/>
        <!--为整个包的类设置别名-->
        <package name="com.yangyi.entity"/>
    </typeAliases>
typeHandler类型处理器
  • 用于将数据库数据的数据类型转换为java中的数据类型
  • 涉及的不多,Mybatis默认的基本够用
environments环境

SQL映射文件详解

  • parameterType:形参的类型,如果形参类型是Map类型,则直接写map即可,不需要写全类名
  • resultType:返回值的类型,如果返回的是集合,则设置的集合内元素的类型
  • 模糊查询需要使用${}进行取出实体中的属性
  • resultMap:可以处理实体属性和表字段不一致的问题,在配置文件中直接给表字段设置别名
  • 案例
  • 映射接口文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTP Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--mapper映射文件,该文件指定实现接口的方法-->
<!--namespace属性指定该xml文件与接口对应-->
<mapper namespace="com.yangyi.mapper.MonsterMapper">

    <select id="findMonsterByIdOrByName" parameterType="Monster" resultType="Monster">
        select * from monster where id = #{id} or  name = #{name}
    </select>

    <!--模糊查询,查询名字包含指定参数的数据,使用模糊查询的时候,取值需要使用${}-->
    <select id="findMonsterByName" resultType="Monster" parameterType="java.lang.String">
        select * from monster where name like '%${name}%';
    </select>

    <!--形参是Map类型,parameterType直接写map即可-->
    <select id="findMonsterByIdOrByName_map" parameterType="map" resultType="Monster">
        select * from monster where id = #{id} or name = #{name }
    </select>

    <!--形参和返回类型是Map类型,直接写map即可-->
    <select id="findMonster_paramMap_resultMap" parameterType="map" resultType="map">
        select * from monster where id = #{id} or name = #{name }
    </select>
</mapper>
  • 测试
package com.yangyi.mapper;

import com.yangyi.entity.Monster;
import com.yangyi.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.*;

/**
 * @Projectname: mybatis
 * @Filename: MonsterMapperTest
 * @Author: 杨逸
 * @Data:2023/9/19 16:59
 * @Description: 测试类
 */
public class MonsterMapperTest {

    private static SqlSession session = null;
    private static MonsterMapper mapper = null;
    @BeforeClass
    public static void init(){
        session = MybatisUtils.getSqlSession();
        mapper = session.getMapper(MonsterMapper.class);
    }

    @Test
    public void findMonsterByIdOrByName() {
        Monster monster = new Monster();
        monster.setId(5);
        monster.setName("kk");

        List<Monster> monsters = mapper.findMonsterByIdOrByName(monster);
        for (Monster monster1 : monsters) {
            System.out.println("monster1 = " + monster1);
        }

        System.out.println("按照id或者名称查询数据成功");
    }

    @Test
    public void findMonsterByName() {
        List<Monster> k = mapper.findMonsterByName("k");
        for (Monster monster : k) {
            System.out.println("monster = " + monster);
        }

        System.out.println("按名称模糊查询成功");
    }

    @Test
    public void findMonsterByIdOrByName_map(){
        Map<String,Object> map = new HashMap<>();
        map.put("id",5);
        map.put("name","kk");

        List<Monster> monsters = mapper.findMonsterByIdOrByName_map(map);
        for (Monster monster : monsters) {
            System.out.println("monster = " + monster);
        }

        System.out.println("按map查询成功");

    }

    @Test
    public void findMonster_paramMap_resultMap(){
        Map<String,Object> map = new HashMap<>();
        map.put("id",5);
        map.put("name","kk");

        List<Map<String, Object>> map1 = mapper.findMonster_paramMap_resultMap(map);

        for (Map<String, Object> objectMap : map1) {
            for (String s : objectMap.keySet()) {
                System.out.println(s + "-->" + objectMap.get(s));
            }
        }

        System.out.println("查询成功");
    }
}
  • 案例二
  • 映射接口配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTP Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--mapper映射文件,该文件指定实现接口的方法-->
<!--namespace属性指定该xml文件与接口对应-->
<mapper namespace="com.yangyi.mapper.UserMapper">
    <!--插入数据时,实体的属性名与表的字段名不一致时,只需要注意引用时使用实体类的属性名即可-->
    <insert id="insertUser" parameterType="com.yangyi.entity.User">
        insert into users (user_name,user_email) value (#{userName},#{userEmail});
    </insert>

    <!--配置返回字段的别名与实体类的属性名一致-->
    <resultMap id="selectMap" type="com.yangyi.entity.User">
        <result column="user_name" property="userName"></result>
        <result column="user_email" property="userEmail"></result>
    </resultMap>
    <!--引用配置的表字段别名-->
    <select id="selectAllUser" resultMap="selectMap">
        select * from users
    </select>

</mapper>
  • 实体
package com.yangyi.entity;

/**
 * @Projectname: mybatis
 * @Filename: User
 * @Author: 杨逸
 * @Data:2023/9/20 10:00
 * @Description: 实体类-演示实体类与表的字段名不一致的问题
 */
public class User {
    private Integer userId;
    private String userName;
    private String userEmail;

    public User() {
    }

    public User(Integer userId, String userName, String userEmail) {
        this.userId = userId;
        this.userName = userName;
        this.userEmail = userEmail;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserEmail() {
        return userEmail;
    }

    public void setUserEmail(String userEmail) {
        this.userEmail = userEmail;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", userEmail='" + userEmail + '\'' +
                '}';
    }
}

  • 测试
package com.yangyi.mapper;

import com.yangyi.entity.User;
import com.yangyi.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.List;

import static org.junit.Assert.*;

/**
 * @Projectname: mybatis
 * @Filename: UserMapperTest
 * @Author: 杨逸
 * @Data:2023/9/20 10:18
 * @Description: 测试
 */
public class UserMapperTest {

    private static SqlSession session = null;
    private static UserMapper mapper = null;
    @BeforeClass
    public static void init(){
        session = MybatisUtils.getSqlSession();
        mapper = session.getMapper(UserMapper.class);
    }

    @AfterClass
    public static void destroy(){
        session.close();
    }

    @Test
    public void insertUser() {
        User user = new User();
        user.setUserName("kk");
        user.setUserEmail("kk@163.com");
        mapper.insertUser(user);
        session.commit();
        System.out.println("插入数据成功");
    }

    @Test
    public void selectAllUser() {
        List<User> users = mapper.selectAllUser();
        for (User user : users) {
            System.out.println("user = " + user);
        }
        System.out.println("实体属性名与表字段名不一致处理,查询成功");
    }
}

动态SQL

  • 映射接口
package com.yangyi.mapper;


import com.yangyi.entity.Monster;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

/**
 * @Projectname: mybatis
 * @Filename: MonsterMapper
 * @Author: 杨逸
 * @Data:2023/9/14 9:52
 * @Description: 操作monster表的接口
 */
public interface MonsterMapper {
    //演示if标签的使用,类似单分支
    //查询年龄大于age的数据,如果age为负数,则查询所有数据
    //注解@Param的value属性的设置要与映射文件里test里的值保持一致,动态SQL的if标签的test才能取值
    List<Monster> selectMonsterByAge(@Param(value = "age") Integer age);

    //演示where标签的使用
    //形参是对象时,不需要使用@Param注解
    List<Monster> selectMonsterByIdAndByName(Monster monster);

    //演示choose标签的使用,类似多分支
    //如果name不空,就使用name进行查询
    //如果id>0,就使用id进行查询
    //否则使用salary>1000进行查询
    List<Monster> selectMonsterByIdOrName(Map<String,Object> map);

    //演示foreach标签的使用
    List<Monster> selectMonsterById_foreach(Map<String,Object> map);

    //演示trim标签的使用
    List<Monster> selectMonsterByIdAndName_trim(Map<String,Object> map);

    //演示set标签的使用
    void updateMonster_set(Map<String,Object> map);
}

  • 映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTP Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--mapper映射文件,该文件指定实现接口的方法-->
<!--namespace属性指定该xml文件与接口对应-->
<mapper namespace="com.yangyi.mapper.MonsterMapper">

    <!--if标签的使用-->
    <select id="selectMonsterByAge" parameterType="java.lang.Integer" resultType="Monster">
        select * from monster where 1=1 <if test="age >= 10"> and age > #{age}</if>
    </select>

    <!--where标签的使用-->
    <!--where标签会自动带上where关键字,不需要我们手动添加,标签内if标签的and关键字一定要加,最后拼接SQL的时候Mybatis会将多余的and关键字去掉-->
    <select id="selectMonsterByIdAndByName" resultType="Monster" parameterType="Monster">
        select * from monster
        <where>
            <if test="id > 4">AND id > #{id}</if>
            <if test="name != null and name != ''">AND name = #{name}</if>
        </where>
    </select>

    <!--choose标签的使用,配置when标签和otherwise标签使用-->
    <select id="selectMonsterByIdOrName" parameterType="map" resultType="Monster">
        select * from monster
        <choose>
            <when test="name != null and name != ''">WHERE name = #{name}</when>
            <when test="id > 0">WHERE  id = #{id}</when>
            <otherwise>WHERE salary > 1000</otherwise>
        </choose>
    </select>

    <!--演示foreach标签的使用-->
    <!--collection表示集合的名称,item表示遍历是元素的名称,open表示拼接开始字符,close表示拼接结束的字符,separator表示拼接的分割字符-->
    <select id="selectMonsterById_foreach" parameterType="map" resultType="Monster">
        select * from monster
        <if test="ids != null and ids != '' ">
            <where>
                id IN <foreach collection="ids" open="(" close=")" separator="," item="id">#{id}</foreach>
            </where>
        </if>
    </select>

    <!--演示trim标签的使用,使用trim可以实现定制where标签,用的比较少-->
    <select id="selectMonsterByIdAndName_trim" resultType="Monster" parameterType="map">
        select * from monster
        <trim prefix="WHERE" prefixOverrides="and|or|hsp">
        <!--前缀替换,当前缀是and或者or或者hsp时,替换为WHERE,也可以使用后缀替换.与前缀替换类似-->
        <!--如果该标签内的SQL的拼接后,第一个关键字是and或者or或者hsp时,会将第一个关键字替换为WHERE-->
            <if test="id > 4">AND id > #{id}</if>
            <if test="name != null and name != ''">AND name = #{name}</if>
        </trim>
    </select>

    <!--演示set标签的使用,注意最后要带上逗号分割-->
    <update id="updateMonster_set" parameterType="map">
        update monster
        <set>
            <if test="age != null and age != '' ">
                age = #{age},
            </if>
            <if test="birthday != null and birthday != '' ">
                birthday = #{birthday},
            </if>
            <if test="email != null and email != '' ">
                email = #{email},
            </if>
            <if test="gender != null and gender != '' ">
                gender = #{gender},
            </if>
            <if test="name != null and name != '' ">
                name = #{name},
            </if>
            <if test="salary != null and salary != '' ">
                salary = #{salary},
            </if>
        </set>
        WHERE id = #{id}
    </update>
</mapper>
  • 测试代码
package com.yangyi.mapper;

import com.yangyi.entity.Monster;
import com.yangyi.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.Date;
import java.util.HashMap;
import java.util.List;

import static org.junit.Assert.*;

/**
 * @Projectname: mybatis
 * @Filename: MonsterMapperTest
 * @Author: 杨逸
 * @Data:2023/9/20 10:55
 * @Description: 测试
 */
public class MonsterMapperTest {
    private static SqlSession session = null;
    private static MonsterMapper mapper = null;
    @BeforeClass
    public static void init(){
        session = MybatisUtils.getSqlSession();
        mapper = session.getMapper(MonsterMapper.class);
    }

    @Test
    public void selectMonsterByAge() {
        List<Monster> monsters = mapper.selectMonsterByAge(10);
        for (Monster monster : monsters) {
            System.out.println("monster = " + monster);
        }
        System.out.println("使用动态SQL查询成功");
    }

    @Test
    public void selectMonsterByIdAndByName(){
        Monster monster = new Monster();
        monster.setId(5);
        monster.setName("杨逸");
        List<Monster> monsters = mapper.selectMonsterByIdAndByName(monster);
        for (Monster m : monsters) {
            System.out.println("m = " + m);
        }
        System.out.println("演示where标签查询数据成功");
    }

    @Test
    public void selectMonsterByIdOrName(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","杨逸");
        List<Monster> monsters = mapper.selectMonsterByIdOrName(map);
        for (Monster monster : monsters) {
            System.out.println("monster = " + monster);
        }
        System.out.println("使用choose标签查询成功");
    }

    @Test
    public void selectMonsterById_foreach(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("ids",new int[]{4,7});
        List<Monster> monsters = mapper.selectMonsterById_foreach(map);
        for (Monster monster : monsters) {
            System.out.println("monster = " + monster);
        }
        System.out.println("使用foreach标签查询成功");
    }

    @Test
    public void selectMonsterByIdAndName_trim(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","杨逸");
        map.put("id",4);
        List<Monster> monsters = mapper.selectMonsterByIdAndName_trim(map);
        for (Monster monster : monsters) {
            System.out.println("monster = " + monster);
        }
        System.out.println("使用trim标签查询成功");
    }

    @Test
    public void updateMonster_set(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("id",8);
        map.put("birthday","2022-09-11");
        mapper.updateMonster_set(map);

        session.commit();
        System.out.println("使用set标签更新数据成功");
    }
}

映射关系

一对一映射关系

  • 实体类Person
package com.yangyi.entity;

/**
 * @Projectname: mybatis
 * @Filename: Person
 * @Author: 杨逸
 * @Data:2023/9/21 9:44
 * @Description: 实体类
 */
public class Person {
    private Integer id;
    //对应的card实体,后面通过映射实现赋值
    private IdentityCard card;
    private String name;

    public Person() {
    }

    public Person(Integer id, IdentityCard card, String name) {
        this.id = id;
        this.card = card;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public IdentityCard getCard() {
        return card;
    }

    public void setCard(IdentityCard card) {
        this.card = card;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", card=" + card +
                ", name='" + name + '\'' +
                '}';
    }
}

  • 实体类IdentityCard
package com.yangyi.entity;

/**
 * @Projectname: mybatis
 * @Filename: IdentityCard
 * @Author: 杨逸
 * @Data:2023/9/21 9:43
 * @Description: 实体类
 */
public class IdentityCard {
    private Integer id;
    private String card_sn;

    public IdentityCard() {
    }

    public IdentityCard(Integer id, String card_sn) {
        this.id = id;
        this.card_sn = card_sn;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getCard_sn() {
        return card_sn;
    }

    public void setCard_sn(String card_sn) {
        this.card_sn = card_sn;
    }

    @Override
    public String toString() {
        return "IdentityCard{" +
                "id=" + id +
                ", card_sn='" + card_sn + '\'' +
                '}';
    }
}

  • 映射接口
public interface IdentityCardMapper {
    IdentityCard getIdentityCardById(Integer id);
}
public interface PersonMapper {
    //第一种配置级联查询
    Person selectPersonById1(Integer id);
    //第二种配置级联查询
    Person selectPersonById2(Integer id);
}
  • 使用注解的接口
package com.yangyi.mapper;

import com.yangyi.entity.Person;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

/**
 * @Projectname: mybatis
 * @Filename: PersonMapperAnnotation
 * @Author: 杨逸
 * @Data:2023/9/21 11:16
 * @Description: 通过注解的形式实现级联查询
 */
public interface PersonMapperAnnotation {
    //注解本质就是对映射xml文件的体现
    @Select(value = "select * from person where id = #{id}")
    //通过@Results配置resultMap
    @Results(value = {
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "name",column = "name"),
            //one指定返回一个数据,select指定查询数据的映射接口
            @Result(property = "card",column = "card_id",one = @One(select = "com.yangyi.mapper.IdentityCardMapper.getIdentityCardById"))
    })
    Person selectPersonById(Integer id);
}

  • 映射配置文件
  • IdentityCradMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTP Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--mapper映射文件,该文件指定实现接口的方法-->
<!--namespace属性指定该xml文件与接口对应-->
<mapper namespace="com.yangyi.mapper.IdentityCardMapper">

    <select id="getIdentityCardById" parameterType="java.lang.Integer" resultType="IdentityCard">
        select * from identity_card where id = #{id}
    </select>


</mapper>
  • PersonMapper.xml
  • 通过标签association配置级联属性的查询映射
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTP Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yangyi.mapper.PersonMapper">

    <!--级联查询的第一种配置方式-->
    <resultMap id="PersonMap1" type="Person">
        <!--id标签一般使用在映射主键上-->
        <id property="id" column="id"></id>
        <!--result用在一般字段上-->
        <result property="name" column="name"></result>
        <!--association标签用在映射级联属性,property指定级联属性的名称,javaType指定级联属性的类型-->
        <association property="card" javaType="IdentityCard">
            <result property="id" column="id"></result>
            <result property="card_sn" column="card_sn"></result>
        </association>
    </resultMap>
    <!--级联属性查询,封装-->
    <select id="selectPersonById1" parameterType="java.lang.Integer" resultMap="PersonMap1">
        select * from person,identity_card where card_id = identity_card.id  and person.id = #{id};
    </select>


    <!--第二种配置级联查询,将多表查询,拆解为单表查询,提高复用性-->
    <resultMap id="PersonMap2" type="Person">
        <id property="id" column="id"></id>
        <result property="name" column="name"></result>
        <!--将联机属性通过另一个映射接口查询出来,column指定将查询出的字段作为参数传入另一个查询接口,select指定将要调用的映射接口的完整名称-->
        <association property="card" column="card_id" select="com.yangyi.mapper.IdentityCardMapper.getIdentityCardById"></association>
    </resultMap>
    <select id="selectPersonById2" parameterType="java.lang.Integer" resultMap="PersonMap2">
        select * from person where id = #{id}
    </select>

</mapper>
  • 测试
  • IdentityCardMapper接口的测试
package com.yangyi.mapper;

import com.yangyi.entity.IdentityCard;
import com.yangyi.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import static org.junit.Assert.*;

/**
 * @Projectname: mybatis
 * @Filename: IdentityCardMapperTest
 * @Author: 杨逸
 * @Data:2023/9/21 10:48
 * @Description: 测试
 */
public class IdentityCardMapperTest {
    private static SqlSession session = null;
    private static IdentityCardMapper mapper = null;
    @BeforeClass
    public static void init(){
        session = MybatisUtils.getSqlSession();
        mapper = session.getMapper(IdentityCardMapper.class);
    }

    @AfterClass
    public static void destroy(){
        session.close();
    }

    @Test
    public void getIdentityCardById() {
        int id = 1;
        IdentityCard identityCardById = mapper.getIdentityCardById(id);
        System.out.println("identityCardById = " + identityCardById);
        System.out.println("查询成功");
    }
}
  • PersonMapper接口的测试
package com.yangyi.mapper;

import com.yangyi.entity.Person;
import com.yangyi.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import static org.junit.Assert.*;

/**
 * @Projectname: mybatis
 * @Filename: PersonMapperTest
 * @Author: 杨逸
 * @Data:2023/9/21 11:08
 * @Description: 测试
 */
public class PersonMapperTest {

    private static SqlSession session = null;
    private static PersonMapper mapper = null;
    @BeforeClass
    public static void init(){
        session = MybatisUtils.getSqlSession();
        mapper = session.getMapper(PersonMapper.class);
    }

    @AfterClass
    public static void destroy(){
        session.close();
    }

    @Test
    public void selectPersonById1() {
        int id = 1;
        Person person = mapper.selectPersonById1(id);
        System.out.println("person = " + person);
        System.out.println("第一种配置级联查询成功");
    }

    @Test
    public void selectPersonById2() {
        int id = 1;
        Person person = mapper.selectPersonById2(id);
        System.out.println("person = " + person);
        System.out.println("第二种级联查询成功");
    }
}
  • PersonMapperAnnotation使用注解的接口测试
package com.yangyi.mapper;

import com.yangyi.entity.Person;
import com.yangyi.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import static org.junit.Assert.*;

/**
 * @Projectname: mybatis
 * @Filename: PersonMapperAnnotationTest
 * @Author: 杨逸
 * @Data:2023/9/21 11:24
 * @Description: 注解测试
 */
public class PersonMapperAnnotationTest {

    private static SqlSession session = null;
    private static PersonMapperAnnotation mapper = null;
    @BeforeClass
    public static void init(){
        session = MybatisUtils.getSqlSession();
        mapper = session.getMapper(PersonMapperAnnotation.class);
    }

    @AfterClass
    public static void destroy(){
        session.close();
    }

    @Test
    public void selectPersonById() {
        int id = 1;
        Person person = mapper.selectPersonById(id);
        System.out.println("person = " + person);
        System.out.println("通过注解查询级联属性成功");
    }
}

一对多映射关系

  • 实体类
package com.yangyi.entity;

import java.util.List;

/**
 * @Projectname: mybatis
 * @Filename: User
 * @Author: 杨逸
 * @Data:2023/9/21 15:48
 * @Description: 实体类
 */
public class User {
    private Integer id;
    private String name;
    private List<Pet> pets;

    public User() {
    }

    public User(Integer id, String name, List<Pet> pets) {
        this.id = id;
        this.name = name;
        this.pets = pets;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public List<Pet> getPets() {
        return pets;
    }

    public void setPets(List<Pet> pets) {
        this.pets = pets;
    }
}

package com.yangyi.entity;

/**
 * @Projectname: mybatis
 * @Filename: Pet
 * @Author: 杨逸
 * @Data:2023/9/21 15:59
 * @Description: 实体类
 */
public class Pet {
    private Integer id;
    private String nickname;
    private User user;

    public Pet() {
    }

    public Pet(Integer id, String nickname, User user) {
        this.id = id;
        this.nickname = nickname;
        this.user = user;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

  • 映射接口
public interface PetMapper {

    List<Pet> selectPetByUserId(Integer id);

    Pet selectPetById(Integer id);

}
public interface UserMapper {
    User selectUserById(Integer id);
}
  • 使用注解的映射接口
package com.yangyi.mapper;

import com.yangyi.entity.Pet;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * @Projectname: mybatis
 * @Filename: PetMapperAnnotation
 * @Author: 杨逸
 * @Data:2023/9/21 16:59
 * @Description: 使用注解实现一对多级联查询
 */
public interface PetMapperAnnotation {

    @Select(value = "select * from pet where user_id = #{id}")
    //使用id属性设置ResultMap的名称,可以进行复用
    @Results(id = "PetMap",value = {
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "nickname",column = "nickname"),
            @Result(property = "user",column = "user_id",one = @One(select = "com.yangyi.mapper.UserMapperAnnotation.selectUserById"))
    })
    List<Pet> selectPetByUserId(Integer id);

    @Select(value = "select * from pet where id = #{id}")
    //使用@ResultMap注解复用以前定义的ResultMap
    @ResultMap(value = "PetMap")
    Pet selectPetById(Integer id);
}

package com.yangyi.mapper;

import com.yangyi.entity.User;
import org.apache.ibatis.annotations.*;

/**
 * @Projectname: mybatis
 * @Filename: UserMapperAnnotation
 * @Author: 杨逸
 * @Data:2023/9/21 16:54
 * @Description: 演示通过注解实现一对多的级联查询
 */
public interface UserMapperAnnotation {
    @Select(value = "select * from user where id = #{id}")
    @Results(value = {
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "name",column = "name"),
            //返回的集合类型使用的是many属性
            @Result(property = "pets",column = "id",many = @Many(select = "com.yangyi.mapper.PetMapperAnnotation.selectPetByUserId"))
    })
    User selectUserById(Integer id);
}

  • 映射配置文件
  • 使用collection标签进行一对多的级联映射,ofType属性表示集合元素的类型
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTP Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yangyi.mapper.PetMapper">
    <!--演示一对多的级联查询-->
    <resultMap id="PetMap" type="Pet">
        <id property="id" column="id"/>
        <result property="nickname" column="nickname"/>
        <association property="user" column="user_id" select="com.yangyi.mapper.UserMapper.selectUserById"/>
    </resultMap>

    <select id="selectPetByUserId" parameterType="Integer" resultMap="PetMap">
        select * from pet where user_id = #{id}
    </select>

    <select id="selectPetById" parameterType="Integer" resultMap="PetMap">
        select  * from pet where id = #{id}
    </select>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTP Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yangyi.mapper.UserMapper">
    <!--演示一对多的级联查询-->
    <resultMap id="UserMap" type="User">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <!--使用collection标签进行一对多的级联映射,ofType属性表示集合元素的类型-->
        <collection property="pets" column="id"  ofType="Pet" select="com.yangyi.mapper.PetMapper.selectPetByUserId"/>
    </resultMap>

    <select id="selectUserById" parameterType="Integer" resultMap="UserMap">
        select * from user where id = #{id}
    </select>
</mapper>

缓存

  • 缓存是将从数据库查询到的数据保存到内存中,避免重复操作数据库,减少数据库的开销
  • 缓存中有目标数据时,mybatis就不会向数据库发起查询请求
缓存的全局配置
  • 配置缓存的全局开关,默认true,如果关闭了则所有缓存都失效
<settings>
        <!--配置缓存的全局开关,默认true,如果关闭了则所有缓存都失效-->
        <setting name="cacheEnabled" value="true"/>        
    </settings>

一级缓存

  • 一级缓存默认开启,一级缓存是Session会话级别的,同一个会话,会有一级缓存的效果
  • 当会话关闭后,一级缓存会失效
  • 使用session.clearCache()手动清空缓存后,一级缓存也会失效
  • 当修改了同一个对象时,一级缓存也会失效

二级缓存

  • 二级缓存和一级缓存都是为了提高检索效率的技术
  • 最大的区别就是作用域的范围不一样,一级缓存的作用域是sqlSession会话级别,在一次会话有效,而二级缓存作用域是全局范围,针对不同的会话都有效
  • 启用二级缓存时,实体推荐实现Serializable可序列化接口,因为有些缓存策略会讲数据进行序列化,实现可序列化能避免出现异常
  • 当session关闭时,一级缓存的数据会放到二级缓存,如果二级缓存开启的话
  • 不会出现一级缓存和二级缓存中有同一个数据。因为二级缓存是在一级缓存关闭之后才有的
在映射配置文件中开启二级缓存
  • size表示最多保存数据引用的数量
  • readOnly表示缓存只读不能被修改,默认是false
  • flushInterval表示缓存刷新的时间,单位是毫秒
  • eviction表示缓存刷新的策略,LRU表示最近最少使用
<!--开启二级缓存,size表示最多保存数据引用的数量,readOnly表示缓存只读不能被修改,默认是false,
    flushInterval表示缓存刷新的时间,单位是毫秒,eviction表示缓存刷新的策略,LRU表示最近最少使用-->
    <cache size="1024" readOnly="true" flushInterval="60000" eviction="LRU"></cache>

  • 可以在接口映射文件指定方法不使用缓存
<select useCache="false" flush="true"></select>