mybatis delete返回值_万字 MyBatis 带你从入门到精通!

本文详细介绍了 MyBatis 的入门使用、CRUD 操作、生命周期、配置解析、resultMap、分页、注解开发、高级结果映射、日志、动态 SQL 以及缓存机制。内容涵盖了从导入 Maven 依赖、创建配置文件到使用注解、处理复杂类型映射和分页查询的全过程,同时解析了 MyBatis 的核心配置文件,讨论了动态 SQL 的 if、choose、trim 和 foreach 元素。此外,还介绍了 MyBatis 的一级缓存和二级缓存,帮助读者深入理解 MyBatis 的工作原理和实践技巧。
摘要由CSDN通过智能技术生成

MyBatis1. 入门使用1.1 导入相关 Maven 依赖1.2 创建并配置核心文件 1.3 编写 MyBatis 工具类1.4 编写实体类1.5 编写实体类对应的 Mapper 接口1.6 编写 Mapper 配置文件1.7 测试1.8 一些可能的问题2. CRUD2.1 Select2.2 Update、Delete、Insert —(增删改需要提交事务)2.3 参数传递2.4 模糊查询3. 生命周期和生命周期3.1 SqlSessionFactoryBuilder:3.2 SqlSessionFactory:3.3 SqlSession4. 配置解析—核心配置文件4.1 属性4.2  设置3.3 类型别名4.4 环境配置4.5 映射器4.6 其他配置5. 初识 resultMap5.1 遇到的问题:数据库中的字段名 和 实体类中的字段名 不一致。5.2 解法办法1:在 SQL 语句中加别名。5.3 解法办法2:resultMap(结果集映射)6. 分页6.1 使用Mybatis实现分页6.2 使用分页插件—PageHelper7. 使用注解开发7.1 基本使用7.3 有参数的情况8. 再遇 resultMap —— 高级结果映射8.1 构建环境 8.2 复杂类型 association —— 多对一8.2.1 查询结果映射8.2.2 嵌套查询8.3 复杂类型集合 collection —— 一对多8.3.1 查询结果映射8.3.2 嵌套查询8.4 8.5 总结9. 日志10. 动态 SQL10.1 if10.2 choose10.3 trim、where、set10.3.1 where10.3.2 set10.3.3 trim10.4 foreach10.5 总结10. 缓存11. MyBatis 总结

MyBatis

首先,什么是 MyBatis ?

官方介绍的很清楚:MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

1. 入门使用

在 Maven 项目中的使用。

先看下结构,避免一些不必要的问题。

f61d8dbe671438634efe2c5f54c4495a.png

1.1 导入相关 Maven 依赖

        <dependencies>                <dependency>            <groupId>mysqlgroupId>            <artifactId>mysql-connector-javaartifactId>            <version>5.1.47version>        dependency>                        <dependency>            <groupId>org.mybatisgroupId>            <artifactId>mybatisartifactId>            <version>3.5.2version>        dependency>                <dependency>            <groupId>junitgroupId>            <artifactId>junitartifactId>            <version>4.12version>        dependency>    dependencies>

1.2 创建并配置核心文件

在 resources 中创建一个 mybatis-config.xml. (名字可随意)

注意:这个配置中的数据库连接信息要换成你自己数据库的。

<?xml  version="1.0" encoding="UTF-8" ?>/span>        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"        "http://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.jdbc.Driver"/>                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>                <property name="username" value="root"/>                <property name="password" value="1024"/>            dataSource>        environment>    environments>    <mappers>        <mapper resource="com/yuanxion/dao/UserMapper.xml"/>    mappers>configuration>

1.3 编写 MyBatis 工具类

编写 MyBatis 工具类可以方便我们使用 MyBatis ,就不需要我们每次都去写这一堆代码。

public class MyBatisUtils {    private static SqlSessionFactory sqlSessionFactory;    static{        try {            //使用Mybatis第一步:获取sqlSessionFactory对象            String resource = "mybatis-config.xml";            InputStream inputStream = Resources.getResourceAsStream(resource);            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);        } catch (IOException e) {            e.printStackTrace();        }    }    //有了 SqlSessionFactory 之后,我们就可以从中获得 SqlSession 的实例了。    // SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。    public static SqlSession getSqlSession(){        return sqlSessionFactory.openSession();    }}

1.4 编写实体类

这个实体类是数据库中所要操作表的对应的实体类。类名、字段名,都要和数据库表中的一样,并提供相应的 get、set 方法。

public class User {    private int id;    private String name;    private String pwd;    public User() {    }    public User(int id, String name, String pwd) {        this.id = id;        this.name = name;        this.pwd = pwd;    }    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getPwd() {        return pwd;    }    public void setPwd(String pwd) {        this.pwd = pwd;    }    @Override    public String toString() {        return "User{" +                "id=" + id +                ", name='" + name + '\'' +                ", pwd='" + pwd + '\'' +                '}';    }}

c57aaf96c5c4b93caa6585ffbbb22a00.png

1.5 编写实体类对应的 Mapper 接口

编写实体类对应的 Mapper 接口,并在这个借口中添加需要进行的操作,比如查询出表中所有的数据。

public interface UserMapper {    //查询所有用户信息    List<User> getUserList();}

1.6 编写 Mapper 配置文件

原来是要实现上面的Mapper接口,现在在 Mapper 配置文件绑定和配置。

<?xml  version="1.0" encoding="utf-8" ?>/span>        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.yuanxion.dao.UserMapper">            <select id="getUserList" resultType="com.yuanxion.pojo.User">       select * from user   select>    mapper>

select 标签中:

  • id : 对应的 namespace 绑定的接口中的方法名。

  • resultType:Sql 语句执行之后的返回值。

1.7 测试

测试一下数据库连接和查询:

@Test    public void test() {                //1. 得SqlSession对象        //因为前面已经写好了工具类,直接使用工具类来获取即可。        SqlSession sqlSession = MyBatisUtils.getSqlSession();        //2. 从 sqlSession 中获取所需实体类的 Mapper        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);                //3. 使用该 Mapper 调用我们在 Mapper 配置文件中配置的查询方法        List<User> userList = userMapper.getUserList();        //4. 遍历我们的查询结果并输出        for (User user : userList) {            System.out.println(user);        }        //5. 关闭SqlSession        sqlSession.close();    }

如果没什么问题,我们就可以链接并查询成功了。

edca34a9f603a0ff06f6db6408e2c1bb.png

1.8 一些可能的问题

如果发生了异常,可能是如下原因中的一种:

  1. 核心配置文件中的数据库连接信息没有改成自己的。

  2. 核心配置文件中的 Mapper 配置文件的位置 没有改成自己的。

  3. 方法名或返回类型写错了。

  4. 没有导入相应的 Maven 资源

  5. 字符集问题。

  6. 配置文件中的写了中文注释。虽然我写了中文注释并没有什么问题,但是发现有些人报错的原因在这里。可以删除试试。

  7. 更多可能的问题,根据报错信息可以去百度、谷歌等查询解决办法。

2. CRUD

2.1 Select

2.1.1 在对应 Mapper 类 UserMapper 中添加接口

//根据用户 ID 查询用户User getUserById(int id);

2.1.2 在 Mapper 配置文件中编写 SQL 语句


select * from user where id = #{id}

select 标签中的:

  • id : 对应的 namespace 绑定的接口中的方法名。

  • resultType:Sql 语句执行之后的返回值。

  • parameterType:这个SQL所对应方法传入的参数的类型。

方法传入的参数用 #{参数名} 接收,类似于一个占位符。

比如这个方法中传入的是 id,那这里就使用 #{id} 接收。

2.1.3 测试

@Testpublic void testSelect() {    //第一步:获得SqlSession对象    SqlSession sqlSession = MyBatisUtils.getSqlSession();    //方式一:getMapper    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);    User user = userMapper.getUserById(1);    System.out.println(user);    //关闭SqlSession    sqlSession.close();}

2.2 Update、Delete、Insert —(增删改需要提交事务)

注意:增删改这3个操作需要提交事务!!(sqlSession.commit();)

2.2.1  在对应 Mapper 类 UserMapper 中添加接口

public interface UserMapper {    //根据用户 ID 查询用户    User getUserById(int id);    //新增一个用户    void addUser(User user);    //修改一个用户    void updateUser(User user);    //根据 id 删除一个用户    void deleteUser(int id);}

2.2.2 在 Mapper 配置文件中编写 SQL 语句

<?xml version="1.0" encoding="utf-8" ?>
br /> PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
insert into user (id, name, pwd) values (#{id}, #{name}, #{pwd});
update user set name=#{name},pwd=#{pwd} where id = #{id} ;
delete from user where id = #{id};
  • parameterType 传入的参数类型可以是一个对象,#{参数} 可以直接从传入的对象中获取。

    比如例子中传入的是 User 对象,#{id} 就可以直接获取传入 User 对象的 id。

2.2.3 测试

我们前面两步写好之后,增、删、改 这些操作都差不多。

但是一定要注意的是:增、删、改 之后要提交事务。(sqlSession.commit();)

    @Test    public void testAddUser() {        //第一步:获得SqlSession对象        SqlSession sqlSession = MyBatisUtils.getSqlSession();        //方式一:getMapper        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);        //创建一个 User 对象        User user = new User(33, "3333", "3333");        //添加一个User对象        userMapper.addUser(user);        //注意:增、删、改 之后,要提交事务。        sqlSession.commit();        //关闭SqlSession        sqlSession.close();    }@Test    public void testUpdateUser() {        //第一步:获得SqlSession对象        SqlSession sqlSession = MyBatisUtils.getSqlSession();        //方式一:getMapper        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);        //创建一个 User 对象        User user = new User(33, "4444", "4444");        //添加一个User对象        userMapper.updateUser(user);        //注意:增、删、改 之后,要提交事务。        sqlSession.commit();        //关闭SqlSession        sqlSession.close();    }@Test    public void testDeleteUser() {        //第一步:获得SqlSession对象        SqlSession sqlSession = MyBatisUtils.getSqlSession();        //方式一:getMapper        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);        //删除一个User对象        userMapper.deleteUser(33);        //注意:增、删、改 之后,要提交事务。        sqlSession.commit();        //关闭SqlSession        sqlSession.close();    }

2.3 参数传递

可以使用 Map 传参

当数据库中的表或者实体类字段过多,可以使用 Map 来传参。

//参数类型改为 Map
int addUser(Map map);

insert into mybatis.user (id, pwd) values (#{id},#{passWord});
    @Test
public void addUser2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map map = new HashMap();
map.put("id",6666);
map.put("passWord","6666");
userMapper.addUser(map);
sqlSession.commit();
sqlSession.close();
}

2.4 模糊查询

模糊查询有两种写法:

  1. 在 Java 程序传入参数时,加上通配符 % %

    List userList = mapper.getUserNameLike("%段%");
  2. 在 SQL 语句中拼接上通配符  % %

    select * from user where name like "%"#{value}"%"

3. 生命周期和生命周期

不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。

3.1 SqlSessionFactoryBuilder:

生命周期:一旦创建了 SqlSessionFactory,就不再需要它了。

作用域:最佳作用域是方法作用域(也就是局部方法变量)。

3.2 SqlSessionFactory:

生命周期:一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。

作用域:最佳作用域是应用作用域。最简单的就是使用单例模式或者静态单例模式实现。

3.3 SqlSession

生命周期: 一次请求。也就是每个请求,可以打开一个 SqlSession,返回一个响应后,就关闭这个SqlSession。

作用域:因为SqlSession 的实例是线程不安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域

注意:每次使用完一个 SqlSession ,要记得关闭,不然会一直占用资源。可以把关闭操作放到 finally 块中。

一个确保 SqlSession 关闭的标准模式的实例:

try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}

4. 配置解析—核心配置文件

核心配置文件:mybatis-config.xml (可以不取这个名字。)

  • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。

0405713f9de89798cad62c0073a7e5f8.png

几个比较重要的配置:属性、设置、类型别名、环境配置、映射器。

4.1 属性

属性(properties)

我们可以通过properties属性来实现引用配置文件。

这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。

演示:

我们在资源文件夹resource中加入一个 db.properties 文件,并编写相关信息:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
username=root
password=1024

然后在配置文件 mybatis-config.xml 中就可以直接引用

<?xml version="1.0" encoding="UTF-8" ?>
br /> PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

977fc58a52af17ab465228ac07e3e3cd.png

并且,还可以通过 properties 元素的子元素来传递

<?xml version="1.0" encoding="UTF-8" ?>
br /> PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

注意1:如果 db.properties 和 配置文件中配置了同一个字段,会优先使用外部配置文件db.properties 的。

注意2:这核心配置文件中,标签是有顺序规定的。不过不用去记顺序,放错的顺序会有提示,根据提示修改即可。

4.2  设置

设置(settings):MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

比如可以设置:是否开启驼峰命名自动映射、指定MyBatis所用日志的具体实现 等。

更多设置可查看:https://mybatis.org/mybatis-3/zh/configuration.html

3.3 类型别名

类型别名 (typeAliases):可为 Java 类型设置一个缩写名字。它仅用于 XML 配置,意在降低冗余的全限定类名书写。

比如我们在 mybatis-config.xml 中这样配置:

这样配置之后,在 Mapper配置文件中 用这个 com.yuanxion.pojo.User 对象就可以直接使用别名 User :


select * from user
select * from user

可以指定一个包,MyBatis 会在包名下面搜索需要的 Java Bean。

也就是说会扫描我们指定的实体类的包,然后自动给包内的实体类起别名,别名默认为 首字母小写的类名

如果是指定了一个包,并且还想自定义别名的话,可以在实体类上增加 @Alias("xxx别名") 注解:

public class User {
}

4.4 环境配置

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

例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射 等场景。

<environments default="development">    <environment id="development">    <transactionManager type="JDBC"/>        <dataSource type="POOLED">            <property name="driver" value="${driver}"/>            <property name="url" value="${url}"/>            <property name="username" value="${username}"/>            <property name="password" value="${password}"/>        dataSource>    environment>environments>

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。简单来说就是 想连接几个数据库,就需要创建几个 SqlSessionFactory 实例。

关键点:

  • 默认使用的环境 ID(比如:default="development")。

  • 每个 environment 元素定义的环境 ID(比如:id="development")。

  • 事务管理器的配置(比如:type="JDBC")。

  • 数据源的配置(比如:type="POOLED")。

Mybatis 默认的事务管理器JDBC ,默认使用的连接池是 :POOLED。

不过如果使用的是 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

4.5 映射器

映射器 (mappers):我们配置了上述一系列 MyBatis 的行为之后,要来定义 SQL 映射语句了,而首先,我们需要告诉 MyBatis 到哪里去找到这些语句,也就是要告诉 MyBatis 到哪里去找映射文件。

简单来说就是 注册绑定我们写的的Mapper文件

可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等方式。

方式一: 使用相对于类路径的资源引用【推荐使用】

方式二:使用映射器接口实现类的完全限定类名

需要注意的是:

  1. 接口和他的Mapper配置文件必须同名

  2. 接口和他的Mapper配置文件必须在同一个包下

方式三:使用扫描包进行注入绑定

同样需要注意的是:

  1. 接口和他的Mapper配置文件必须同名

  2. 接口和他的Mapper配置文件必须在同一个包下

4.6 其他配置

需要使用其他配置时,可去官网查看:https://mybatis.org/mybatis-3/zh/configuration.html

5. 初识 resultMap

5.1 遇到的问题:数据库中的字段名 和 实体类中的字段名 不一致。

比如 :数据库中的字段是:id、name、pwd ;

实体类中的字段是:id、name、password 。

这样的话,当我们去查询并输出时会发现,虽然还是查询到数据,password 却为null。

因为 数据库中的字段名 和 实体类中的字段名 不一致时,无法将数据映射到不一致的字段上。

5.2 解法办法1:在 SQL 语句中加别名。

比如数据库中字段是 pwd,实体类中字段是 password,那么我们在写SQL语句时,给 pwd 起一个别名为 passw 即可。


select id,name,pwd as password from user where id = #{id}

5.3 解法办法2:resultMap(结果集映射)

编写对应数据库表和实体类的 resultMap 。

然后将 SQL语句上 resultType 改为 resultMap,并绑定对应的 resultMap 的 id。


select * from user where id = #{id}

注意,对于那些 数据库中的字段名 和 实体类中的字段名 一致的字段,在 resultMap 中可以省略不写:


select * from user where id = #{id}

resultMap 元素是 MyBatis 中最重要最强大的元素,上面只是简单的结果映射,高级结果映射会复杂些,后面再说。

6. 分页

首先回顾一下在数据库中我们分页的语法:

SELECT * from user limit startIndex,pageSize;

简单理解就是:从第 startIndex 个开始,找 pageSize 个符合条件的数据,包含 startIndex  这个。

比如:

找出从第 0 个开始,找到 1 条符合条件的数据:

SELECT * from user limit 0,1;

找出从第 3 个开始,找到 4 条符合条件的数据:

SELECT * from user limit 3,4;

而在我们的程序中,分页的方法有很多,下面说两种:

6.1 使用Mybatis实现分页

使用方法其实和其他 Mybatis 中的 SQL 没什么区别,就是传参的时候注意一下。

用 map 传参比较方便(对因为象中的属性,可以直接取出来。我们只需传递map的key即可找到对应参数。当然也可以用其他的方式传参)。

这里说一下核心的地方:

  1. mapper 接口

    List<User> getUserByLimit(Map<String,Integer> map);
  2. Mapper.xml 文件

    <select id="getUserByLimit" parameterType="map" resultMap="UserMap">    select * from user limit #{startIndex},#{pageSize}select>
  3. 使用测试:

    @Testpublic void getUserByLimit(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);    //用 map 存放分页条件    HashMap<String, Integer> map = new HashMap<String, Integer>();    map.put("startIndex",1);    map.put("pageSize",2);    //分页查询    List<User> userList =  mapper.getUserByLimit(map);        //遍历查看查询结果    for (User user : userList) {    System.out.println(user);    }    sqlSession.close();}

6.2 使用分页插件—PageHelper

使用插件来实现分页就很简单了,这里说的的是 PageHelper ,当然也可以使用其它分页插件。

PageHelper:https://pagehelper.github.io/

下面简单说一下使用方法:

  1. 在 pom.xml 中导入 PageHelper 的依赖

    com.github.pagehelperpagehelper5.1.10
  2. 在 MyBatis 核心配置 MyBatis-config.xml 中配置拦截器插件(提示:如果是 Spring 和 MyBatis 一起使用,那在二者的整合配置文件中配置即可。)

     
  3. 调用方法就是在正常查询语句之前,设置一下分页参数即可:

        @Test
    public void testPageHelper() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //设置要查询第几页的数据,以及每页的大小
    //比如要查询第2页的数据,每页2个数据
    PageHelper.startPage(2, 2);
    //设置完之后,就可以查询了
    List userList = userMapper.getUserList();
    //遍历查看查询到的数据
    for (User user : userList) {
    System.out.println(user);
    }
    //关闭SqlSession
    sqlSession.close();
    }

    使用起来还是很简单很方便的,不过这里只是简单的示范了一下使用,更多是使用方法和使用细节,可以去查看官方文档:https://pagehelper.github.io/

    大家不要一听官方文档就觉得很难,其实官方文档大部分都很通俗易懂。

7. 使用注解开发

简单的 SQL 语句,我们可以直接在相应 Mapper接口中的方法 上使用注解来实现。

7.1 基本使用

  1. 首先还是要在核心配置文件中还是要绑定相应接口的。比如我们要给实现 UserMapper 接口中的方方法,那就要绑定 UserMapper  :

  2. 在 UserMapper 接口中的 getUserList() 上使用注解:

    @Select("select * from user")
    List getUserList();
  3. 就可以使用 getUserList() 方法了。

7.3 有参数的情况

有参数的时候,需要在方法参数前加上 @Param("xxx") 绑定字段:

@Select("select * from students where garde = #{garde} and age = #{agedeff}")
List getStudentsByGardeAndAge(@Param("garde") int garde,@Param("agedeff") int age);

注意1: 参数是基本数据类型或者String类型才要加 @Param("xxx") 注解;如果是引用类型的参数,不需要加。

注意2:

  1. @Select 注解中 #{} 中用的是 @Param() 中的字段名

  2. @Param() 中的字段名可以和数据字段名中不一样(不过推荐还写一样的)。

看个图可能更清楚:

98228ea3d31d12fe956029ee24bc5562.png

(另外如果只有一个参数,可以省略@Param注解 ,不过推荐还是加上。)

8. 再遇 resultMap —— 高级结果映射

回顾:

上面我们已经简单的使用过 resultMap ——结果映射。

他的作用简单来说,就像它的名字一样,结果映射,也就是将我们从数据库中查询到的 列数据 映射到我们的 实体类中的 字段 上去。

比如

实体类的属性是:id,name,password;

数据库中的列是:id,name,pwd;

那么 resultMap 可以把我们从数据库中查询出的每条数据中的 id,name,pwd 依次分别对应到一个实体类的 id,name,password 上。

以上是简单的使用,很简单,没什么问题。

问题来了,上面都是数据库中的一个 列 对应实体类的中一个 属性,那么:

  1. 如果实体类中的字段是一个复杂类型,比如对象 student ,那么该怎么映射?

  2. 如果实体类中的字段是一个复杂类型的集合,比如 ArrayList ,那么又该怎么映射?

放心,resultMap 已经帮我们解决了这些问题。

来看一下 resultMap 中的子元素:

我们来说一下其中个常用的:

  1. resutlt:用于完成普通列的映射(上面已经使用过了)。

  2. id:用于完成主键值的映射(用法和 result 一样,唯一区别就是 id 是用于标识 主键,可提高整体性能)。

  3. association:一个复杂类型的关联,可用于嵌套结果映射。

  4. collection:多个复杂类型的集合,同样可用于嵌套结果映射。

其中 resutlt 和 id 用法简单就不再反复说了,下面结合案例重点说一下 associationcollection

8.1 构建环境

先建数据库表。

一个学生表,每个学生有一个老师;

一个老师表,一个老师有多个学生。

建表 SQL:

CREATE TABLE `teacher`(`id` INT NOT NULL,`name` VARCHAR(30) DEFAULT NULL,PRIMARY KEY(`id`))ENGINE=INNODB DEFAULT CHARSET=utf8CREATE TABLE `student`(`id` INT NOT NULL,`name` VARCHAR(30) DEFAULT NULL,`tid` INT DEFAULT NULL,PRIMARY KEY(`id`),KEY `fktid`(`tid`),CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`))ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO teacher(`id`,`name`)VALUES (1,'张三老师');INSERT INTO teacher(`id`,`name`)VALUES (2,'李四老师');INSERT INTO `student`(`id`,`name`,`tid`) VALUES(1,'王秀儿',1);INSERT INTO `student`(`id`,`name`,`tid`) VALUES(2,'孙六六',1);INSERT INTO `student`(`id`,`name`,`tid`) VALUES(3,'段诗云',1);INSERT INTO `student`(`id`,`name`,`tid`) VALUES(4,'叶菲儿',1);INSERT INTO `student`(`id`,`name`,`tid`) VALUES(5,'红红',2);INSERT INTO `student`(`id`,`name`,`tid`) VALUES(6,'静静',2);

203b5ee415b0e3e1d4e22bcbab59fe0f.png

8.2 复杂类型 association —— 多对一

数据库表如上:相对于学生,是多个学生对一个老师。

实体类:

Teacher 实体类

public class Teacher {    private int id;    private String name;        //记得生成 无参、有参构造器,get、set方法,toString方法    }

Student 实体类

public class Student {    private int id;    private String name;    private Teacher teacher;    //记得生成无参、有参构造器,get、set方法,toString方法}

(MyBatis 的环境搭建就不重复写了,这里主要是学 association 。)

需求:查询所有学生的信息以及对应老师的信息。

先看这个需求的原始 SQL 语句:

SELECT s.id sid, s.name sname, t.name tname,t.id tidFROM student s,teacher tWHERE s.tid = t.id
8.2.1 查询结果映射

我们可以先从数据库中查询出结果,也就是四个列的数据:sid,sname,tname,tid。

而学生实体类的字段为:id、name、teacher。

那么我们就需要将结果中的:

sid 映射到 id 上;

sname 映射到 name 上;

tname 和 tid 一起映射到 teacher 上。

具体实现就是:


SELECT s.id sid, s.name sname, t.name tname,t.id tid
FROM student s,teacher t
WHERE s.tid = t.id

解释一下:

4ac1266ec0b70500bba9300528ec052b.png

简单来说就是,先查询出所有字段,然后使用 resultMap 自定义映射;其中普通字段直接映射;而复杂类型,其实就是用多个字段映射成一个对象。

8.2.2 嵌套查询

思路其实就是嵌套子查询:先查出所有学生信息,再根据学生信息中的 tid 找到相应老师。

具体实现:

<select id="GetStudentAndTeacher2" resultMap="StudentAndTeacher2">    SELECT * FROM studentselect><resultMap id="StudentAndTeacher2" type="com.yuanxion.pojo.Student">    <result property="id" column="id"/>    <result property="name" column="name"/>        <association property="teacher" column="tid" javaType="com.yuanxion.pojo.Teacher" select="getTeacher" />resultMap><select id="getTeacher" resultType="com.yuanxion.pojo.Teacher">    SELECT * FROM teacher    WHERE    id = #{tid}select>

解释一下:

90f895d9371b689116eeecfaeb173b1a.png

简单来说就是嵌套子查询,子查询的整个结果作为复杂类型字段。

8.3 复杂类型集合 collection —— 一对多

collection 其实和 association 用法差不多,来看一下。

数据库表还是上面:相对于老师,是一个老师对多个学生。

实体类:

Teacher 实体类

public class Teacher {    private int id;    private String name;    //一个老师对应多个学生    private ArrayList<Student> students;        //记得生成 无参、有参构造器,get、set方法,toString方法    }

Student 实体类

public class Student {    private int id;    private String name;    private tid;    //记得生成无参、有参构造器,get、set方法,toString方法}

(MyBatis 的环境搭建同样就不重复写了,这里主要是学 collection。)

需求:查询所有学生的信息以及对应老师的信息。

先看这个需求的原始 SQL 语句:

select s.id sid, s.name sname, t.name tname,t.id tidfrom student s,teacher twhere s.tid = t.id and t.id = #{tid}
8.3.1 查询结果映射

我们可以先从数据库中查询出结果,也就是四个列的数据:tname,tid,sid,sname。

而老师实体类的字段为:id、name、ArrayList students

那么我们就需要将结果中的:

tid 映射到 id 上;

tname 映射到 name 上;

将每一对 sid 和 snname 映射到集合中的每一个对象上。

<select id="getTeacherAndStudent1" resultMap="StudentAndTeacher">    select s.id sid, s.name sname, t.name tname,t.id tid    from student s,teacher t    where s.tid = t.id and t.id = #{tid}select><resultMap id="StudentAndTeacher" type="com.yuanxion.pojo.Teacher">    <result property="id" column="tid"/>    <result property="name" column="tname"/>        <collection property="students" ofType="com.yuanxion.pojo.Student">        <result property="id" column="sid"/>        <result property="name" column="sname"/>        <result property="tid" column="tid"/>    collection>resultMap>

collection 和 association 的区别在于:

association 类型元素是 javaType = ”复杂类型所对应的实体类“

collection 类型元素是 ofType= ” 集合中复杂类型所对应的实体类“

8.3.2 嵌套查询

思路其实就是嵌套子查询:先查出所有老师;再根据老师id,去学生表中查出该老师的所有学生,放在一个集合中。

<select id="getTeacherAndStudent2" resultMap="TeacherAndStudent2">    SELECT *    FROM teacher    WHERE id = #{tid}select><resultMap id="TeacherAndStudent2" type="com.yuanxion.pojo.Teacher">    <result property="id" column="id"/>    <result property="name" column="name"/>        <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudent" />resultMap><select id="getStudent" resultType="com.yuanxion.pojo.Student">    SELECT * FROM student    WHERE    tid = #{tid}select>

简单来说就是嵌套子查询,子查询中的所有结果作为复杂类型集合。

8.4

8.5 总结

关于 association 和 collection:

  1. association:用于实体类的字段是 复杂类型。比如老师对象:teacher。

  2. collection :用于实体类的字段是 复杂类型的集合。比如学生对象的集合:ArrayList students 。

  3. JavaType :用来指定实体类中普通字段的类型。比如老师实体类的类型:Teacher。

  4. ofType :用来指实体类中集合字段中集合元素的类型。比如学生对象的集合中元素的类型:Student。

关于 查询结果映射 和 嵌套查询:

  1. 查询结果映射:①先查询出所有字段;②普通字段,直接映射;③复杂类型字段,先将相应的普通字段映射成一个对象,再将这个对象映射到实体类中的复杂类型字段上。

  2. 嵌套查询:①普通字段,直接查询出来,并映射。②复杂类型字段,嵌套一个子查询,用子查询查询出数据,再映射到实体类中的复杂类型字段上。

9. 日志

日志可以帮助我们了解和记录查询的执行过程。

比如对于,MyBatis,开启日志之后,我们可以看到执行的 SQL 语句;发生错误时,便于我们排错。

MyBatis 支持的日志:

  1. SLF4J

  2. LOG4J

  3. LOG4J2

  4. JDK_LOGGING

  5. COMMONS_LOGGING

  6. STDOUT_LOGGING

  7. NO_LOGGING

其中常用的是 LOG4JSTDOUT_LOGGING

STDOUT_LOGGING 使用很简单,在核心配置文件 mybatis-config.xml 中加上下面这段配置即可:

LOG4J 使用使用相对复杂些,但其实也简单:

  1. 导入 log4j 的包

    log4jlog4j1.2.17
  2. 编写 log4j 的配置文件:log4j.properties 或 log4j.xml

    (这里面配置很多,具体可以去百度查看)

  3. 在在核心配置文件 mybatis-config.xml 将日志配置成 log4j

实际开发中,可能用 log4j 比较多。

我们这里是为了方便学习下一个知识点(动态SQL),使用 STDOUT_LOGGING 就够用了,

10. 动态 SQL

动态 SQL 是 MyBatis 的强大特性之一。

动态 SQL 可以根据方法传入的不同参数,生成不同的 SQL 。

动态 SQL 可以简化我们拼装 SQL 的操作,而无需在 Java 代码去判断和拼接。

下面就来看一下 动态 SQL 。

先搭建一下环境:

数据库:

CREATE TABLE `blog` (
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`state` VARCHAR(30) NOT NULL COMMENT '状态',
`tag` VARCHAR(30) NOT NULL COMMENT '标签'
) ENGINE=INNODB DEFAULT CHARSET=utf8

实体类:

public class Blog {
private int id;
private String title;
private String author;
private String state;
private String tag;
//构造函数、get & set 方法,toString方法。
}

10.1 if

if 可根据我们方法传入的不同条件动态生成不同的 SQL

演示:

BlogMapper 中方法:

public interface BlogMapper {
ArrayList findActiveBlogWithCondition(Map map);
}

BlogMapper.xml 中的动态 SQL :


SELECT * FROM BLOG
WHERE state = '上架'
AND title like #{title}
AND tag like #{tag}

我们的参数放在一个 map 中,根据我们写的动态 SQL 语句:

  1. 当我们的 map 中没有参数时,SQL 语句是

    SELECT *
    FROM BLOG
    WHERE state = '上架' ;
  2. 我们的 map 中有 一个键值对时,SQL 语句是  

    SELECT *
    FROM BLOG
    WHERE state = '上架'
    AND tag like '算法';
  3. 我们的 map 中有 , 时,SQL 语句是  

    SELECT *
    FROM BLOG
    WHERE state = '上架'
    AND title like 'Java'
    AND tag like '算法';

10.2 choose

上面的 if 会使用所有传入的满足条件的条件,但有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用

针对这种情况,MyBatis 提供了 choose 元素,它有点**像when 有一个条件满足时,会拼接这个满足条件 when 中的语句,不会再拼接 otherwise 中的语句。

演示:

BlogMapper 中方法:

public interface BlogMapper {
ArrayList findActiveBlogWithOneCondition(Map map);
}

BlogMapper.xml 中的动态 SQL :


SELECT * FROM BLOG
WHERE state = '上架'
AND title like #{title}
AND author like #{author}
AND tag like 'Java'

我们的参数放在一个 map 中,根据我们写的动态 SQL 语句:

  1. 当我们的 map 中没有参数时,SQL 语句是

    SELECT *
    FROM BLOG
    WHERE state = '上架'
    AND tag like 'Java'

    when 里面条件没满足,就会默认拼接 otherwise 中的语句。

  2. 我们的 map 中有 一个键值对时,SQL 语句是  

    SELECT *
    FROM BLOG
    WHERE state = '上架'
    AND author like '猿兄';

    when 有一个条件满足时,会拼接这个满足条件 when 中的语句,不会再拼接 otherwise 中的语句。

  3. 我们的 map 中有 , 时,SQL 语句是  

    SELECT *
    FROM BLOG
    WHERE state = '上架'
    AND title like 'Mybatis解析';

    when 有多个条件满足时,会拼接满足条件 when 中,位置最上面的语句,不会再拼接其他 when 中的语句 以及 otherwise 中的语句。

10.3 trim、where、set

10.3.1 where

来看个问题,我们上面的动态语句中,where 后面是有个默认条件 state = '上架' 的:


SELECT * FROM BLOG
WHERE state = '上架'
AND title like #{title}
AND tag like #{tag}

如果这个 state = '上架'  也是可选条件,那么就变成了下面这种写法:


SELECT * FROM BLOG
WHERE
state like #{state}
AND title like #{title}
AND tag like #{tag}

这样的写法,如果每次查找条件中都有 state 条件那就没问题;

可是如果条件中没有第一个 state 条件,动态生成的 SQL 就会出问题。

比如,只给了 tag = 'Java' 条件,那么动态生成的 SQL就变成了:

SELECT * FROM BLOG
WHERE
AND tag like 'Java'

可以看到,WHERE 后面多了一个 AND  。

这是不是就不太智能了?

放心,其实将 WHERE 改为 元素即可:

    
SELECT * FROM BLOG
state like #{state}
AND title like #{title}
AND tag like #{tag}

元素,如果第一个条件不存在,他拼接后面的 SQL 时,会自动删除掉 WHERE 子句开头多余的 AND 或 OR

10.3.2 set

set 元素和 where 元素功能差不多。

where 元素是删除自动删除 WHERE 子句开头多余的 AND 或 OR;

set 元素是自动删除 SET 子句结尾多余的 "," 。


update blog
state like #{state},
title = #{title},
author = #{author}
where id = #{id}
10.3.3 trim

当上面的 where 元素和 set 元素不能满足你的动态SQL 时,可以使用自定义的 trim 元素。

trim 属性:

属性描述
prefix给sql语句拼接的前缀
suffix给sql语句拼接的后缀
prefixOverrides去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"
suffixOverrides去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定

比如我们可以用 trim 自定义成一个和 set 一样的功能的元素:

<update id="updateBlog" parameterType="map">    update blog    <trim prefix="SET" suffixOverrides=",">        <if test="state != null">                state like #{state},            if>        <if test="title != null">            title = #{title},        if>        <if test="author != null">            author = #{author}        if>    trim>    where id = #{id}update>

10.4 foreach

动态 SQL 的可以利用 foreach 元素对集合进行遍历。

比如,基于上面的环境,我们想实现是一个方法,查找指定id的一些博客:

Mapper:

public interface BlogMapper {    ArrayList<Blog> findBlogByIds(List<Integer> list);}

Mapper.xml:

<select id="findBlogByIds" parameterType="list" resultType="com.yuanxion.pojo.Blog">        SELECT *        FROM BLOG        WHERE ID in        <foreach collection="list" item="id" index="index"                 open="(" separator="," close=")">            #{id}        foreach>    select>

foreach 元素允许指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。

它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素会智能的添加,不会错误地添加多余的分隔符。

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

那我们上面写的 feach 的动态SQL 来说:

  1. 如果 list 中只有一个值 1,那么动态生成的 SQL 就是:

    SELECT *FROM BLOGWHERE ID in (1)
  2. 如果 list 中只有3个值 1、3、4,那么动态生成的 SQL 就是:

    SELECT *FROM BLOGWHERE ID in (1,3,4)
  3. ... ...

10.5 总结

简单来说,动态 SQL 其实就是可以根据不同的条件,动态不同的SQL ;让我们可以简化拼装 SQL 的操作,而无需在 Java 代码中去判断和拼接。

10. 缓存

MyBatis 内置了一个事务性缓存机制。

(这个了解即可,真要使用缓存的话,还是要使用 Redis 或 Memcached 实现缓存。)

MyBatis 的缓存有两个级别:一级缓存二级缓存

一级缓存(默认):本地的会话缓存;可以理解为 SqlSession级别的缓存,也就是这个级别的缓存只在一个 SqlSession 中有效,然后这个 SqlSession 关闭了,这个缓存就没了。

二级缓存:全局缓存;可以理解为 SqlSessionFactory 级别的缓存,也就是这个级别的缓存,在所有这个  SqlSessionFactory 生成的 SqlSession  中共享。当一个 SqlSession 被关闭之前,会将其缓存保存。

默认是开启一级缓存,如果想开启二级缓存,只需要在你的 Mapper.xml 映射文件中添加一行:

<cache/>

加上之后的默认效果:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。

  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。

  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。

  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。

  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。

  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

提示 :缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。

然后上述的效果,可以通过 cache 元素的属性来修改,比如:

<cache  eviction="FIFO"  flushInterval="60000"  size="512"  readOnly="true"/>

这个配置的意思是:创建了一个清除策略为 FIFO 的缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

清除策略是当缓存空间不足时,删除已有的缓存,来清理空间的策略,可用的清除策略有:

  • LRU – 最近最少使用:移除最长时间不被使用的对象。

  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。

  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

注意:因为增删改操作,可能会改变原来的数据,所以增删改操作会刷新缓存!

然后缓存的方式除掉上面的方式,还可以自定义缓存方式,了解就好;当然,有兴趣的可以去研究研究。(我就不去研究自定义缓存了,我选择用 Redis /手动狗头。)

11. MyBatis 总结

首先 MySQL 的基本使用很简单。

基本使用流程:

  1. 在pom.xml 中导入相关依赖

  2. 创建核心配置文件:mybatis-config.xml

    2.1 在配置文件中配置数据库连接信息;可以引入外部资源文件中的数据库连接信息。

  3. 创建实体类

  4. 创建实体类对应的 Mapper 接口,在接口中定义相关方法。

  5. 创建 Mapper 接口对于的 Mapper.xml 映射文件5.1 在核心配置文件 mybatis-config.xml 中绑定,或者说注册这个 Mapper.xml 映射文件。

    5.2 在映射文件中用 namespace 声明所有绑定的对应的 Mapper 接口。5.3 实现 Mapper 接口中定义的方法。

  6. 开始使用6.1 读取核心配置文件  Mapper.xml ,生成相应的 SqlSessionFactory;6.2 使用这个SqlSessionFactory 生成相应的 SqlSession 。6.2 使用这个 SqlSession 去调用接口中的定义的方法。(其中 6.1、6.2  可以封装成一个 Utils,方便多次使用。)

并且知道了,MyBatis 不止可以通过映射文件来开发还可以通过注解来开发

不过复杂的 SQL 语句用注解开发不方便,还得同 Mapper.xml 映射文件来开发。

然后又知道了 resultMap——结果集映射

其中

  1. 简单的字段,可以用 resutlt 直接映射。

  2. 复杂类型的字段,要用 association 来映射。

  3. 复杂类型集合的字段,要用 collection 来映射。

这里面,顺便还知道了复杂查询的两种解决方法:

  1. 查询结果映射:先查询出所有结果;简单字段直接映射;复杂字段,先映射成对应的对象再整个映射。

  2. 嵌套查询:其实就是分步查询,或者说嵌套子查询。

然后还知道了怎么开启日志,来查看具体的执行信息。

再然后,知道了动态SQL,简单来说也就是根据传入的参数的不同,动态生成不同的 SQL 。

  1. if

  2. choose

  3. foreach

最后的最后还了解一下MyBatis的缓存

  1. 一级缓存 :本地会话缓存。

  2. 二级缓存:全局缓存。

至此,MyBatis 大部分内容应该都说到了。

如果有没有提到的内容,而项目又需要的话,可以去官方文档查阅:中文官方文档

往期推荐

设计模式(一) ——单例模式 (6种实现、优缺点、以及应用场景)

设计模式(二) ——工厂模式 (3种工厂模式的实现及优缺点)

点个关注,不再迷路

不要明知会后悔却还不去努力!一起加油!

这里是猿兄,为你分享程序员的世界。

非常感谢各位优秀的程序员们能看到这里,如果觉得文章还不错的话,求点赞? 求关注? 求分享?,对我来说真的 非常有用!!!

持续更新文章,欢迎微信搜索关注:  猿兄  第一时间获取最新文章。

关注后回复【电子书】,有我为大家准备的各类技术书籍。

关注后回复【电子书】,有我为大家准备的各类技术书籍。

27873d0152a266932308d9802209d20f.png

你点的每一个在看,我都认真当成了喜欢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值