Mybatis使用手册

本文最后更新于:2021年1月13日 晚上

一、简介

1)什么是Mybatis

【官网介绍】:MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。

  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

  • 趣味知识:Mybatis本来是apache的开源项目iBatis,其中iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)

2)持久化

什么是持久化:

  • 把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。
  • JDBC就是一种持久化机制,文件IO也是一种持久化机制。
  • 在生活中 : 将鲜肉冷藏,吃的时候再解冻的方法也是。将水果做成罐头的方法也是。

为何要持久化:

  • 内存中的数据断电即失,但有一些对象是无论如何都不能丢失的,比如银行账号等,遗憾的是,人们还无法保证内存永不掉电。
  • 内存的价格相比于硬盘,要贵好多

3)持久层

  • 完成持久化工作的代码块 . ——> dao层 【DAO (Data Access Object) 数据访问对象】
  • 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。

4)为什么需要Mybatis

  • 传统的jdbc操作 , 有很多重复代码块。比如 : 数据取出时的封装 , 数据库的建立连接等等… , 通过框架可以减少重复代码,提高开发效率 .

  • Mybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 ;一个半自动化的ORM框架 (Object Relationship Mapping) —>对象关系映射

  • MyBatis的优点

    • 简单易学:没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件就可以了
    • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响;sql写在xml里,便于统一管理和优化。
    • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试;sql和代码的分离,提高了可维护性。
    • 提供xml标签,支持编写动态sql。
  • 最重要的一点,使用的人多!公司需要!

二、HelloMybatis

1)搭建数据库

CREATE DATABASE `mybatis`;

USE `mybatis`;

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
        `id` INT (20) NOT NULL PRIMARY KEY,
        `name` VARCHAR (30) DEFAULT NULL,
        `pwd` VARCHAR (30) DEFAULT NULL
) ENGINE = INNODB DEFAULT CHARSET = utf8 ;

INSERT  INTO `user`(`id`,`name`,`pwd`) VALUES (1,'狂神','123456'),(2,'张三','abcdef'),(3,'李四','987654');

2)创建模块并编写代码

一旦写完,pom.xmlmybatutils.javamybatis-config.xmlUser.java几乎就完全不用改了,以后只需要改UserDao.javaUserMapper.xmlUserDaoTest.java即可

  • 编写maven配置文件,导入mybatis及mysql

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
  • 编写Mybatis核心配置文件,连接数据库,并配置Mapper.xml的路径(?干嘛用啊,需进一步考究)

    <?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>
    
        <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&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    	
        <!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册 -->
        <mappers>
            <mapper resource="com/gaowl/dao/UserMapper.xml"/>
        </mappers>
    
    </configuration>
  • 编写Mybatis工具类,读取mybatis核心配置文件,获取SqlSession连接

    package com.gaowl.utils;
    
    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 MybatisUtils {
        private static SqlSessionFactory sqlSessionFactory;
    
        static {
            try {
                // 获取sqlSessionFactory对象
                String resource = "mybatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);   // 获取核心配置文件
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        // 获取SqlSession连接,SqlSessionFactory完全包含了面向数据库执行SQL命令所需的方法
        public static SqlSession getSession(){
            return sqlSessionFactory.openSession();
        }
    }
  • 创建实体类

    public class User {
       // 对应数据库中的字段
       private int id;  
       private String name;   
       private String pwd;   
       
       // 构造,有参,无参
       //set/get
       //toString()  
    }
  • 编写UserMapper接口

    package com.gaowl.dao;
    
    import com.gaowl.pojo.User;
    import java.util.List;
    
    public interface UserMapper {
        List<User> getUserList();
    }
  • 编写UserMapper.xml代替原先的实现类,设置namespace绑定Mapper接口,拿到具体的方法,并执行相关SQL 命令作为方法的返回值

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--  namespace绑定一个对应的DAO/Mapper接口 -->
    <mapper namespace="com.gaowl.dao.UserMapper">
        <select id="getUserList" resultType="com.gaowl.pojo.User">
            select * from mybatis.user
        </select>
    </mapper>

3)测试

import com.gaowl.pojo.User;
import com.gaowl.dao.UserMapper;
import com.gaowl.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class MyTest {
    @Test
    public void selectUser() {
        // 通过工具类获取SqlSession连接,该连接可以执行所需的SQL命令
        SqlSession session = MybatisUtils.getSession();
        
        // 获取SelSession的实例,并执行相关方法
        //方法一:
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.getUserList();   // UserMapper.java和UserMapper.xml通过namespace进行了绑定,此处执行方法会调用xml中的具体实现语句(此例子中为查询所有用户的SQL命令)。。有点多态编译看左,执行看右的意思了
        
        //方法二:(不推荐)
        //List<User> users = session.selectList("com.gaowl.dao.UserMapper.selectUser");
        
        for (User user: users){
            System.out.println(user);
        }
        
        // 关闭连接
        session.close();
    }
}

4)(可能)遇到的问题

  • 配置文件没有注册:mybatis核心配置文件中未注册所需的UserMapper.xml
  • 绑定接口错误:jdbc和MySQL的接口
  • 是否使用ssl:我在测试时,将useSSL=true会报错!!但改为false就好了

  • 方法名不对:UserMapper.xml中的namespace不对应

  • 返回类型不对:UserMapper中的resultType
  • Maven导出资源:静态资源过滤问题。加上下面语句后,更手动更新下!!!
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

三、CRUD操作

1)select等语句

  • 路径:UserMapper.xml中,<select id=“ ” resultType=“ ”> 具体sql语句 </select>
    • id:对应的namespace中的方法名
    • resultType:该方法的返回值(也是Sql语句的返回值)
    • parameterType:传递的参数的类型

2)修改UserMapp.java接口

package com.gaowl.dao;

import com.gaowl.pojo.User;
import java.util.List;

public interface UserMapper {
    List<User> getUserList();

    User getUserByID(int id);

    void addUser(User user);

    void updateUser(User user);

    void deleteUser(User user);

}

3)修改其实现类UserMapper.xml

<?xml version="1.0" encoding="UTF8" ?>    <!-- 若“1字节的UTF-8序列的字节1无效”,将编码方式由UTF-8修改为UTF8即可 -->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gaowl.dao.UserMapper">
    <!-- 1.查询所有用户 -->
    <select id="getUserList" resultType="com.gaowl.pojo.User">
        select * from mybatis.user
    </select>

    <!-- 2.根据id查询单个用户 -->
    <select id="getUserByID" parameterType="int" resultType="com.gaowl.pojo.User">
        select * from mybatis.user where id = #{id}
    </select>

    <!-- 3.增加一个新的用户 -->
    <insert id="addUser" parameterType="com.gaowl.pojo.User">
        insert into mybatis.user (id, name, pwd) values(#{id}, #{name}, #{pwd})
    </insert>

    <!-- 4.更新用户信息 -->
    <update id="updateUser" parameterType="com.gaowl.pojo.User">
        update mybatis.user set name=#{name}, pwd=#{pwd} where id =#{id}
    </update>

    <!-- 5.删除某个用户 -->
    <delete id="deleteUser" parameterType="com.gaowl.pojo.User">
        delete from mybatis.user where id = #{id}
    </delete>

</mapper>

4)测试

import com.gaowl.pojo.User;
import com.gaowl.dao.UserMapper;
import com.gaowl.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class MyTest {
    @Test
    public void selectUser() {
        SqlSession session = MybatisUtils.getSession();
        //方法一:
        //List<User> users = session.selectList("com.gaowl.mapper.UserMapper.selectUser");
        //方法二:
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.getUserList();

        for (User user: users){
            System.out.println(user);
        }
        session.close();
    }

    @Test
    public void getUserByID() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        User users = mapper.getUserByID(1);

        System.out.println(users);

        session.close();
    }

    @Test
    public void addUser() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = new User(4,"gaowl","root");
        int i = mapper.addUser(user);
        if(i>0){
            System.out.println("数据插入成功...");
        }

        // 增删改均要提交事务
        session.commit();
        session.close();
    }

    @Test
    public void updateUser() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
//        User user = new User(4,"gaowl","newpwd");
        User user = mapper.getUserByID(4);
        user.setPwd("root");
        mapper.updateUser(user);

        // 增删改均要提交事务
        session.commit();
        session.close();
    }


    @Test
    public void deleteUser() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = mapper.getUserByID(4);
        mapper.deleteUser(user);

        // 增删改均要提交事务
        session.commit();
        session.close();
    }
}

5)小结

  • 所有的增删改操作都需要提交事务session.commit,查询操作不需要!
  • 输出的xml文件中存在中文乱码,更改其编码格式即可

  • 参数过多时,考虑采用Map实现(注解也行,后续讲。。)

    // 例一:向数据库中插入新用户
    // UserMapper.java
    int addUser2(Map<String, Object> map);
    
    // UserMapper.xml
    <insert id="addUser2" parameterType="Map">
          insert into mybatis.user (id, name, pwd) VALUES (#{userId}, #{userName}, #{userPassword})
    </insert>
        
    // Mytest.java
    @Test
    public void addUser2(){
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
    
        Map<String, Object> map = new HashMap<>();
        map.put("userId",6);   // 仅插入id是可以的,这是其他两项默认为null
        map.put("userName","mybatis");
    
        mapper.addUser2(map);
    
        session.commit();
        session.close();
    }
    
    
    
    
    // 例二:根据用户名称和密码查询用户
    // UserMapper.java
    User getUserByNp(Map<String, Object> map);
    
    // UserMapper.xml
    <!-- 传入多个参数,采用map -->
    <select id="getUserByNp" parameterType="map" resultType="com.gaowl.pojo.User">
        select * from mybatis.user where name=#{userName} and pwd = #{userPassword}
    </select>
        
    // Mytest.java
    @Test
    public void getUserByNP(){
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
    
        HashMap<String, Object> map = new HashMap<>();
    
        map.put("userName", "gaowl");
        map.put("userPassword", "root");
    
        User user = mapper.getUserByNp(map);
    
        System.out.println(user);
    
        session.close();
    }
    • Map传递参数,直接在sql中取出key即可 parameterType="Map"
    • 对象传递参数,直接在sql中取出对象的属性即可 parameterType="User"
    • 只有一个 基本类型参数时,可以直接在sql中拿到,直接传递参数即可
  • 模糊查询Like

    // UserMapper.java
    List<User> getUserLike(String name);
    
    // UserMapper.xml
    <!-- 也可以在sql语句中拼接通配符,如select * from user where name like "%"#{name}"%",但容易造成SQL注入问题-->
    <select id="getUserLike" resultType="com.gaowl.pojo.User">
        select * from mybatis.user where name like #{name}
    </select>
    
    // MyTest.java
    @Test
    public void getUserLike(){
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
    	// 通过通配符传递参数!!
        List<User> users = mapper.getUserLike("%李%");
    
        for (User user : users) {
            System.out.println(user);
        }
    
        session.close();
    }

四、配置解析

Mybatis核心配置文件包含:

  • 注意元素节点的顺序!顺序不对会报错

  • configuration(配置)

  • properties(属性)

  • settings(设置)

  • typeAliases(类型别名)

  • typeHandlers(类型处理器)

  • objectFactory(对象工厂)

  • plugins(插件)

  • environments(环境配置)

    • environment(环境变量)
      • transactionManager(事务管理器)
      • dataSource(数据源)
    • databaseIdProvider(数据库厂商标识)
  • mappers(映射器)

1)环境配置(environments)

<?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>

    <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&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
	
    <!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册 -->
    <mappers>
        <mapper resource="com/gaowl/dao/UserMapper.xml"/>
    </mappers>

</configuration>
  • 其可以配置多个环境,通过default选项选择要用的环境;每个SqlSessionFactory实例只能选择一种环境

  • Mybatis默认的事务管理器为JDBC,默认的数据源类型为POOLED

    • 事务管理器transactionManager:JDBC和MANAGED两种;

    • 数据源datasource:type包括unpooled、pooled、jndi三种类型;

2)属性(properties)

支持在resource文件下新建properties配置文件,如db.properties,然后通过${字段名称}拿到配置文件中的值。

  • 直接引入配置文件
  • 在properties中设置property的属性
  • 如果两个文件有同一字段,优先使用配置文件的字段
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf8
username=root
password=root
<?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>
    <properties resource="db.properties"/>  <!--导入外部配置文件-->
    <properties resource="db.properties">
        <property name="pwd" value="1111"/>  <!-- 外部配置的优先级高于此处 -->
    </properties>

    <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>

    <mappers>
        <mapper resource="com/gaowl/dao/UserMapper.xml"/>
    </mappers>

</configuration>

3)类型别名(typeAliases)

减少完全限定名的冗余

  • 通过typealias指定,比如将com.kuang.pojo.User指定为User
<!--配置别名,注意位置-->
<typeAliases>
   <typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
  • 也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,
    • 比如在没有注解时,扫描实体类的包,它的默认别名为这个类的类名 首字母小写
    • 若有注解,则别名为其注解值
<typeAliases>
   <package name="com.kuang.pojo"/>
</typeAliases>
@Alias("user")
public class User {
  ...
}
  • 在实体类比较少时,使用第一种;如果实体类比较多,建议使用第二种。

4)设置(setting)

  • 懒加载:lazyLoadingEnabled
  • 日志实现方式:logImpl
  • 缓存开启关闭:cacheEnabled
<settings>
     <setting name="cacheEnabled" value="true"/>     <!-- 是否开启缓存 -->
     <setting name="lazyLoadingEnabled" value="true"/>    <!-- 懒加载 -->
     <setting name="multipleResultSetsEnabled" value="true"/>
     <setting name="useColumnLabel" value="true"/>
     <setting name="useGeneratedKeys" value="false"/>
     <setting name="autoMappingBehavior" value="PARTIAL"/>
     <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
     <setting name="defaultExecutorType" value="SIMPLE"/>
     <setting name="defaultStatementTimeout" value="25"/>
     <setting name="defaultFetchSize" value="100"/>
     <setting name="safeRowBoundsEnabled" value="false"/>
     <setting name="mapUnderscoreToCamelCase" value="false"/>   <!-- 自动驼峰命名规则映射 -->
     <setting name="localCacheScope" value="SESSION"/>
     <setting name="jdbcTypeForNull" value="OTHER"/>
     <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

5)映射器(mapper)

MapperRegistry:注册绑定所需要的Mapper文件

映射器就是直接告诉 MyBatis 到哪里去找映射文件

  • 使用相对于类路径的资源引用
  • 使用映射器接口实现类的完全限定类名(好用)
  • 使用扫描包进行注册
<!-- 使用相对于类路径的资源引用【更推荐】 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>


<!-- 使用映射器接口实现类的完全限定类名 -->
<!-- 接口和他的配置文件必须同名,必须放在同一个包下-->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>


<!-- 使用扫描包进行注册 -->
<!-- 接口和他的配置文件必须同名,必须放在同一个包下-->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

6)作用域和生命周期

错误地使用作用域和生命周期会导致严重的并发问题

  • SqlSessionFactoryBuilder
    • 其作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用。
    • 所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在;最佳作用域是方法作用域(也就是局部方法变量
  • SqlSessionFactory
    • 其可以看做数据库连接池,一旦被创建在应用运行期间就一直存在
    • 最佳作用域为 应用作用域;最简单的就是使用单例模式或者静态单例模式
  • SqlSession
    • 可以看做连接到连接池的一个请求
    • SqlSession的实例不是线程安全的,因此是不能共享的,所以它的最佳作用域为 请求或者方法作用域
    • 用完后,需要关闭,否则资源被占用
  • 每一个Mapper代表一个具体的业务

五、实体属性名和Sql字段名不一致时

1)设置别名

<?xml version="1.0" encoding="UTF8" ?>    <!-- 若“1字节的UTF-8序列的字节1无效”,将编码方式由UTF-8修改为UTF8即可 -->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gaowl.dao.UserMapper">

    <select id="getUserByID" parameterType="int" resultType="com.gaowl.pojo.User">
        select id,name,pwd as password from mybatis.user where id = #{id}
    </select>

</mapper>

2)ResultMap结果集映射

id   name   pwd
id   name   password

所谓结果集映射是指,将查询到的数据库的字段转换为所需的实体属性

<?xml version="1.0" encoding="UTF8" ?>    <!-- 若“1字节的UTF-8序列的字节1无效”,将编码方式由UTF-8修改为UTF8即可 -->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gaowl.dao.UserMapper">    <!-- 将该xml文件和相应的接口进行绑定(或者说该xml为该接口的实现类,需要对其中的抽象方法进行重写) -->
    
    <!-- 建立结果集映射 -->
    <resultMap id="UserMap" type="com.gaowl.pojo.User">
        <!-- column为数据库中的字段,property为实体类中的属性 -->
		<!-- <result column="id" property="id"/>
        <result column="name" property="name"/>  -->
        <result column="pwd" property="password"/>
    </resultMap>

    <select id="getUserByID" resultMap="UserMap">
        select * from mybatis.user where id = #{id}
    </select>

</mapper>
  • ResultMap的设计思想为:对于简单的语句根本不需要配置显性的结果映射,而对应复杂一点的语句只需要描述他们之间的关系即可

六、日志工厂

更便捷地进行数据库排错

mybatis核心配置文件里有一个setting选项,其可以指定Mybatis所用日志的具体实现,如SLF4J、LOG4J、LOG4J2、STDOUT_LOGGING、NOLOGGING等等

1)STDOUT_LOGGING(标准日志输出,无需配置)

<?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>
    <properties resource="db.properties"/>  <!--导入外部配置文件-->

    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <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>

    <mappers>
        <mapper resource="com/gaowl/dao/UserMapper.xml"/>
    </mappers>

</configuration>

2)LOG4J(更好用)

  • 先导入包

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
  • 编写配置文件log4j.properties

    # 将等级为Debug的日志信息输出到console和file两个目的地
    log4j.rootLogger=DEBUG,console,file
    
    # 控制台相关的输出设置
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target = System.out
    log4j.appender.console.Threshold = DEBUG
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern = [%c]-%m%n
    
    # 文件输出的相关设置
    log4j.appender.file = org.apache.log4j.RollingFileAppender
    log4j.appender.file.File = ./log/gaowl.log
    log4j.appender.file.MaxFileSize = 10mb
    log4j.appender.file.Threshold = DEBUG
    log4j.appender.file.layout = org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern = [%p][%d{yy-MM-dd}][%c]-%m%n
    
    # 日志输出级别
    log4j.logger.org.mybatis = DEBUG
    log4j.logger.java.sql = DEBUG
    log4j.logger.java.sql.Statement = = DEBUG
    log4j.logger.java.sql.ResultSet = DEBUG
    log4j.logger.java.sql.PreparedStatement = DEBUG
    
  • 配置Mybatis核心配置文件,修改日志实现方式

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
  • 开始测试

    import com.gaowl.dao.UserMapper;
    import com.gaowl.pojo.User;
    import com.gaowl.utils.MybatisUtils;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.log4j.Logger;
    import org.junit.Test;
    
    public class MyTest {
    
       static Logger logger = Logger.getLogger(MyTest.class);
    
        // 根据ID查询用户
        @Test
        public void getUserByID() {
            SqlSession session = MybatisUtils.getSession();
            UserMapper mapper = session.getMapper(UserMapper.class);
            User users = mapper.getUserByID(1);
            logger.info("info:进入getUserByID方法成功");
            System.out.println(users);
            session.close();
        }
    
        @Test
        public void testLog4J(){
            logger.info("info:进入了testLog4J");
            logger.debug("debug:进入了testLog4J");
            logger.error("error:进入了testLog4J");
        }
    
    }

七、分页

减少数据的处理量

1)使用Sql的limit命令

select * from user limit startindex,count;

select * from user limit 3;  #[0,3]

2)使用Mybatis进行分页

// UserMapper.java 
List<User> getUserByLimit(Map<String,Integer> map);


// UserMapper.xml
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
    select * from mybatis.user limit #{startIndex},#{pageSize}
</select>

    
// Mytest.java
@Test
public void getUserByLimit(){
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);

    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",2);
    map.put("pageSize",3);

    List<User> users = mapper.getUserByLimit(map);

    for (User user : users) {
        System.out.println(user);
    }

    session.close();
}

3)RowBounds(不推荐,速度不如上面快)

接口、mapper.xml、测试

// UserMapper.java 分页2
List<User> getUserByRowBounds();


// UserMapper.xml
<select id="getUserByRowBounds"  resultMap="UserMap">
    select * from mybatis.user
</select>
  
    
// Mytest.java
@Test
public void getUserByRowBounds(){
    SqlSession session = MybatisUtils.getSession();

    // RowBounds
    RowBounds rowBounds = new RowBounds(1, 2);
    List<User> users = session.selectList("com.gaowl.dao.UserMapper.getUserByRowBounds",null,rowBounds);

    for (User user : users) {
        System.out.println(user);
    }

    session.close();
}

4)插件

八、使用注解开发

1)面向接口

接口的本身反映了系统设计人员对系统的抽象理解。

  • 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法 .

  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现 .

  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构

2)使用注解开发具体流程

  • 注解在接口上实现

    @Select("select * from mybatis.user")
    List<User> getUsers();
  • 在Mybatis核心配置文件中绑定接口

    <!--不绑定xml配置文件,改为绑定接口 -->
    <mappers>
        <mapper class="com.gaowl.dao.UserMapper"/>
    </mappers>
  • 进行测试

    @Test
    public void UserMapperTest(){
        SqlSession session = MybatisUtils.getSession();
    
        // 反射机制
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.getUsers();
        for (User user : users) {
            System.out.println(user);
        }
        session.close();
    }

3)Mybatis详细开发流程

4)CRUD操作

  • 在工具类创建的时候自动创建事务
// 获取SqlSession连接,SqlSessionFactory完全包含了面向数据库执行SQL命令所需的方法
public static SqlSession getSession(){
    return sqlSessionFactory.openSession(true);
}
  • 编写接口
public interface UserMapper {

    @Select("select * from mybatis.user")
    List<User> getUsers();

    // 通过id查询用户
    @Select("select * from user where id = #{id}")
    User getUserByID(@Param("id") int id);

    // 方法存在多个参数时,在每个参数前面均添加上 @Param注解
    //User getUserByID(@Param("id") int id,@Param("name") String name);

    // 增加用户
    @Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
    int addUser(User user);

    // 修改用户信息
    @Update("update user set name=#{name},pwd=#{password} where id =#{id}")
    int updateUser(User user);

    // 删除用户
    @Delete("delete from user where id = #{uid}")
    int delete(@Param("uid") int id);

}
  • 测试
public class MyTest {

    @Test
    public void UserMapperTest(){
        SqlSession session = MybatisUtils.getSession();

        // 反射机制
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.getUsers();   // 查询所有用户
        for (User user : users) {
            System.out.println(user);
        }

        session.close();
    }

    @Test
    public void UserMapperTest2(){
        SqlSession session = MybatisUtils.getSession();

        // 反射机制
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = mapper.getUserByID(1);   // 通过id查询用户
        System.out.println(user);
        session.close();
    }


    @Test
    public void UserMapperTest3(){
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);

        mapper.addUser(new User(7, "anno_add", "root"));   // 增加用户
        mapper.updateUser(new User(7, "anno_add", "root2333"));   // 修改用户信息
        mapper.delete(5);   // 删除用户
        session.close();
    }
  • 切记要将接口绑定到Mybatis核心配置文件

  • 关于@Param()注解

    • 基本类型的参数或者String类型,需要加上该注解
    • 引用类型的数据,如Map,则不需要加
    • 如果只有一个基本类型的话,可以忽略该注解,但建议不要忽略的好
    • 在SQL中引用的就是@Param中设置的属性名,比如uid
    • #{ } 取值(预编译,可以很大程度上防止sql注入)

九、Lombok

Lombok是一款优秀的Java开发插件,使得Java开发者可以通过其定义的一些注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的Java模型对象(POJO)。在开发环境中使用 Lombok插件后,Java开发人员可以节省出重复构建,诸如 hashcode和 equals这样的方法以及各种业务对象模型的 accessor/和 ToString等方法的大量时间。对于这些方法,它能够在编译源代码期间自动帮我们生成这些方法,并没有如 反射那样降低程序的性能。

1)IDEA安装lombok插件

2)在项目中导入包

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
    <scope>provided</scope>
</dependency>

3)在实体类上使用相关注解

  • @Data:自动生成无参构造、get、set、tostring、hashcode、equals等

  • @AllArgsConstructor : 全参构造
    @NoArgsConstructor:无参构造,设置全参构造时,需手动设置无参构造才可以

  • @ToString@EqualsAndHashCode
  • @Getter@Setter:放在类上面时,自动生成所有的get和set方法;放在某个字段上时,仅生成该字段的get和set方法

4)优缺点

简化代码编写,但也大大降低了代码的可读性

十、多对一和一对多

创建所需的数据库

Use mybatis;

CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL PRIMARY KEY,
  `name` VARCHAR(30) DEFAULT NULL
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); 

CREATE TABLE `student` (
  `id` INT(10) NOT NULL PRIMARY KEY,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

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', '小王', '1');

示例1:查询所有的学生信息,及其对应的老师的信息(多对一)

1)按照 查询 嵌套处理(查询多次)

public interface StudentMapper {
    // 查询所有的学生的信息,及其对应的老师的信息
    public List<Student> getStudents();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gaowl.dao.StudentMapper">
    
    <!-- 先获取所有学生的信息;
         之后根据获取到的学生信息中的老师的id,获取老师的信息;
     由于学生的结果集中包含老师,此处采用关联查询(association)。-->
    
    <select id="getStudents" resultMap="Student2Teacher">
        select * from mybatis.student;
    </select>

    <resultMap id="Student2Teacher" type="Student">
        <!-- association关联属性  column数据库表中的列名 property实体类属性名 javaType属性类型-->
        <association column="tid" property="teacher" javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getTeacher" resultType="Teacher">
        select * from mybatis.teacher where id = #{id}
    </select>

</mapper>
@Test
public void test2(){
    SqlSession session = MybatisUtils.getSession();
    StudentMapper mapper = session.getMapper(StudentMapper.class);
    List<Student> students = mapper.getStudents();

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

    session.close();
}

思路总结:

我们先查询Student所有的数据;由于Student中的数据和Teacher中的数据存在着某种关联,因此在查到Student数据时,可以根据其中的tid进行映射,到Teacher表中再次进行查询以获取需要的数据。

<association column="tid" property="teacher" javaType="Teacher" select="getTeacher"/>的含义为:将tid映射为Teacher类型的teacher,而teacher是根据select中的方法(参数为此处的tid)获取

2)根据 结果 嵌套处理(查询一次)

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gaowl.dao.StudentMapper">

    <select id="getStudents2" resultMap="Student2Teacher2" >
        select s.id sid, s.name sname, s.tid stid, t.name tname from mybatis.student s, mybatis.teacher t where s.tid = t.id;
    </select>

    <resultMap id="Student2Teacher2" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="id" column="stid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>

</mapper>

思路总结

先把所需的数据通过联表查询全部拿到,然后通过resultMap对这些数据进行一一映射。

示例2:一个老师拥有的多个学生(多对一)

  • 编写实体类Student.java、Teacher.java及其Mapper接口
package com.gaowl.pojo;
import lombok.Data;

@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}
package com.gaowl.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

@Data
public class Teacher {
    private int id;
    private String name;

    // 一个老师关联多个学生
    private List<Student> students;
}
package com.gaowl.dao;

import com.gaowl.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import java.util.List;

public interface TeacherMapper {

    // 获取所有老师
    List<Teacher> getAllTeachers();

    // 获取指定老师的信息,及其所管理的学生的信息
    Teacher getTeacher(@Param("id") int id);   // 此处参数为id,若写为tid会报错

    // 获取指定老师的信息,及其所管理的学生的信息
    Teacher getTeacher2(@Param("id") int id);   // 此处参数为id,和后面sql的传入参数对应,若写为tid后面也需要修改
}
  • 编写其实现类
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gaowl.dao.TeacherMapper">
    <!-- 此处结果类型为Teacher,不是List -->
    <select id="getAllTeachers" resultType="Teacher">
        select * from mybatis.teacher
    </select>

    <!--=============  按结果嵌套查询【更好用】 ===============-->
    <select id="getTeacher" resultMap="Teacher2Student">
        select t.id tid, t.name tname, s.id sid, s.name sname
        from mybatis.teacher t, mybatis.student s
        where t.id = s.tid and t.id = #{id};
    </select>
    
    <resultMap id="Teacher2Student" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!-- 复杂属性时,我们需要单独对其处理:
                 对象:采用association
                 集合:采用collection,其中泛型信息可通过 ofType 获取
             javaType指定属性的类型-->
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>


    <!--=============  子查询 ===============-->
    <select id="getTeacher2" resultMap="Teacher2Student2">
        select * from mybatis.teacher where id = #{id}
    </select>
    
    <resultMap id="Teacher2Student2" type="Teacher">
        <!-- 返回值的类型(实体类属性的类型、students的属性)采用 javaType 指定,其泛型的类型采用 ofType 指定-->
        <result property="id" column="id"/>
        <collection property="students" javaType="ArrayList" ofType="Student" select="getStudents" column="id"/>
    </resultMap>

    <!-- 传过来的值来自 column属性 -->
    <select id="getStudents" resultType="Student">
        select * from mybatis.student where tid = #{tid}
    </select>
</mapper>
  • 开始测试
import com.gaowl.dao.StudentMapper;
import com.gaowl.dao.TeacherMapper;
import com.gaowl.pojo.Student;
import com.gaowl.pojo.Teacher;
import com.gaowl.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class MyTest {

    // 获取所有的老师
    @Test
    public void test(){
        SqlSession session = MybatisUtils.getSession();
        TeacherMapper mapper = session.getMapper(TeacherMapper.class);
        List<Teacher> teachers = mapper.getAllTeachers();

        for (Teacher teacher : teachers) {
            System.out.println(teacher);
        }
        session.close();
    }


    // 获取指定老师的信息,及其所管理的学生的信息---联表查询(推荐)
    @Test
    public void test2(){
        SqlSession session = MybatisUtils.getSession();
        TeacherMapper mapper = session.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher(1);

        System.out.println(teacher);
        session.close();
    }

    // 获取指定老师的信息,及其所管理的学生的信息---子查询
    @Test
    public void test3(){
        SqlSession session = MybatisUtils.getSession();
        TeacherMapper mapper = session.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher2(1);

        System.out.println(teacher);
        session.close();
    }
}

3)小结

  • 关联 association —》 一对多
  • 集合 collection —-》 多对一
  • javaType 和 ofType

    • 前者指定 实体类属性 的类型
    • 后者指定 实体类属性中泛型 的类型
  • 更推荐使用 按结果查询,其仅查询一次即可,拿到需要的数据后再进行一一映射;普通的属性采用result即可,关联对象采用association,集合采用collection

  • 注意 属性名property 和 字段column 的对应

十一、动态SQL

根据不同的条件生成不同的SQL语句,避免再疯狂的拼接SQL语句

1)搭建环境

  • 创建对应数据库

    CREATE TABLE `blog`(
          `id` VARCHAR(50) NOT NULL COMMENT '博客id',
          `title` VARCHAR(100) NOT NULL COMMENT '博客标题',
          `author` VARCHAR(30) NOT NULL COMMENT '博客作者',
          `create_time` DATETIME NOT NULL COMMENT '创建时间',
          `views` INT(30) NOT NULL COMMENT '浏览量'
    )ENGINE=INNODB DEFAULT CHARSET=utf8
  • 创建实体类

    package com.gaowl.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.util.Date;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Blog {
        private String id;
        private String title;
        private String author;
        private Date createTime;   // 此处属性名和字段名不一致,可以通过mybatis开启自动驼峰命名解决
        private int views;
    }
  • 创建实体类的Mapper接口及其实现类

    package com.gaowl.dao;
    
    import com.gaowl.pojo.Blog;
    
    import java.util.List;
    import java.util.Map;
    
    public interface BlogMapper {
    
        // 插入数据
        int addBlog(Blog blog);
    
        // 查询
        List<Blog> queryBlogIf(Map map);
    
        List<Blog> queryBlogChoose(Map map);
    
        // 更新博客
        int updateBlog(Map map);
    
        // 查询第1、2、3号博客
        List<Blog> queryBlogForeach(Map map);
    
    }
    <?xml version="1.0" encoding="UTF8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.gaowl.dao.BlogMapper">   <!--绑定接口-->
    
        <!-- 此处SQL插入命令,输入ins,即可有快捷指示-->
        <insert id="addBlog" parameterType="blog">
            insert into mybatis.blog(id, title, author, create_time, views)
            values (#{id}, #{title}, #{author}, #{create_time}, #{views});
        </insert>
    
    <!--
        &lt;!&ndash; parameterType为传入参数的类型;resultType为返回值的类型 &ndash;&gt;
        <select id="queryBlogIf" parameterType="map" resultType="blog">
            select * from mybatis.blog where 1=1
            <if test="title != null">
                and title = #{title}
            </if>
            <if test="author != null">
                and author = #{author}
            </if>
    
        </select>-->
    
        <select id="queryBlogIf" parameterType="map" resultType="blog">
            select * from mybatis.blog
            <where>
                <if test="title != null">
                    and title = #{title}
                </if>
                <if test="author != null">
                    and author = #{author}
                </if>
            </where>
        </select>
    
    
        <select id="queryBlogChoose" parameterType="map" resultType="blog">
            select * from mybatis.blog
            <where>
                <choose>
                    <when test="title != null">
                        title = #{title}
                    </when>
                    <when test="author != null">
                        and author = #{author}
                    </when>
                    <otherwise>
                        and views = #{views}
                    </otherwise>
                </choose>
            </where>
        </select>
        
        
        <update id="updateBlog" parameterType="map" >
            update mybatis.blog
            <set>
                <if test="title != null">
                    title = #{title},
                </if>
                <if test="author != null">
                    author = #{author},
                </if>
            </set>
            where id = #{id}
        </update>
    
        <!-- 通过map传递元素,其中map中包含一个集合 -->
        <select id="queryBlogForeach" parameterType="map" resultType="blog">
            select * from mybatis.blog
            <where>
                <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                    id = #{id}
                </foreach>
            </where>
        </select>
    </mapper>
  • 测试

    import com.gaowl.dao.BlogMapper;
    import com.gaowl.pojo.Blog;
    import com.gaowl.utils.IDUtils;
    import com.gaowl.utils.MybatisUtils;
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.List;
    
    public class MyTest {
    
        @Test
        public void addBlog(){
            SqlSession session = MybatisUtils.getSession();
            BlogMapper mapper = session.getMapper(BlogMapper.class); // 此处传入的是接口,切记
    
            Blog blog = new Blog(IDUtils.getID(),"HelloJava","gaowl",new Date(),1000);
            int res = mapper.addBlog(blog);
    
            if(res>0){
                System.out.println("数据插入成功...");
            }
    
            blog = new Blog(IDUtils.getID(),"Mybatis冲冲冲","gaowl",new Date(),10000);
            res = mapper.addBlog(blog);
    
            if(res>0){
                System.out.println("数据插入成功...");
            }
            session.close();
        }
    
    
        @Test
        public void queryBlogIf(){
            SqlSession session = MybatisUtils.getSession();
            BlogMapper mapper = session.getMapper(BlogMapper.class); // 此处传入的是接口,切记
    
            HashMap hashMap = new HashMap();
            // hashMap.put("title","HelloJava");
            hashMap.put("author","gaowl");
    
            List<Blog> blogs = mapper.queryBlogIf(hashMap);
    
            for (Blog blog : blogs) {
                System.out.println(blog);
            }
            session.close();
        }
    
        @Test
        public void queryBlogChoose(){
            SqlSession session = MybatisUtils.getSession();
            BlogMapper mapper = session.getMapper(BlogMapper.class); // 此处传入的是接口,切记
    
            HashMap hashMap = new HashMap();
            // hashMap.put("title","HelloJava");
            hashMap.put("author","gaowl");
            List<Blog> blogs = mapper.queryBlogIf(hashMap);
    
            for (Blog blog : blogs) {
                System.out.println(blog);
            }
            session.close();
        }
    
        @Test
        public void updateBlog(){
            SqlSession session = MybatisUtils.getSession();
            BlogMapper mapper = session.getMapper(BlogMapper.class); // 此处传入的是接口,切记
    
            HashMap hashMap = new HashMap();
            hashMap.put("title","Hello_Java");
            hashMap.put("author","gaowl");
            hashMap.put("id","6c3ed22ddc794e678be7240397831903");
            mapper.updateBlog(hashMap);
    
            session.close();
        }
    
    
    
        @Test
        public void queryBlogForeach(){
            SqlSession session = MybatisUtils.getSession();
            BlogMapper mapper = session.getMapper(BlogMapper.class); // 此处传入的是接口,切记
    
            HashMap hashMap = new HashMap();
            ArrayList<Integer> ids = new ArrayList<>();
            ids.add(1);
            ids.add(2);
            hashMap.put("ids",ids);
    
            List<Blog> blogs = mapper.queryBlogForeach(hashMap);
            for (Blog blog : blogs) {
                System.out.println(blog);
            }
    
            session.close();
        }
    
    }

2)相关语句详解

所谓的动态SQL,其本质还是SQL语句,只是在SQL层面,去执行一个逻辑代码

🌍 if 命令

提供可选的文本查找功能;当符合条件时,将相应的SQL命令拼接到初始的SQL命令中

<!-- parameterType为传入参数的类型;resultType为返回值的类型 -->
<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from mybatis.blog where 1=1
    <if test="title != null">
        and title = #{title}
        </if>
    <if test="author != null">
        and author = #{author}
    </if>

</select>

🌍 choose 命令

choose元素类似Java中的switch语句,其从多个条件中选择一个实现,when标签类似case

<select id="queryBlogChoose" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <choose>
            <when test="title != null">
                title = #{title}
            </when>
            <when test="author != null">
                and author = #{author}
            </when>
            <otherwise>
                and views = #{views}
            </otherwise>
        </choose>
    </where>
</select>

🌍 trim(where、set)

  • where标签替换之前的where命令,其只会在子元素有返回值时才生效;若子元素的开头为AND或者OR,where标签也会自动将ANDOR去除。
<!-- parameterType为传入参数的类型;resultType为返回值的类型 -->
<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </where>
</select>
  • set标签用于动态包含需要更新的列,忽略其它不更新的列;其会动态地在行首插入 SET 关键字,并删掉额外的逗号
<update id="updateBlog" parameterType="map" >
    update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author},
        </if>
    </set>
    where id = #{id}
</update>

🌍 for each

可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach

  • 当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。

  • 当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

  • 下面的例子中,collection为传入的数组,item为每次迭代获取到的元素,open和close为每次迭代的起始和末尾元素,separator为分隔符
    • select * from mybatis.blog where (id = 1 or id = 2)
<!-- 通过map传递元素,其中map中包含一个集合 -->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id = #{id}
        </foreach>
    </where>
</select>

代码复用

通过sql标签抽取公共的代码段,然后通过include标签对其引用。

  • 公共代码段中尽量不要包含where、set标签等
<sql id="if-title-author">
    <if test="title != null">
        and title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>

动态SQL就是在拼接SQL语句,我们首先要保证SQL的正确性,然后根据SQL的格式,将其排列组合即可

  • 先写原始的SQL语句,再进行相应的修改,成为动态SQL

十二、缓存

1)简介

缓存cache是指 将用户经常查询的数据放在缓存(内存)中,用户查询数据时,不再去磁盘上(关系型数据库数据文件)查询,而是直接从缓存中查询

  • 使用缓存可以减少和数据库的交互次数,减少系统开销,提高系统效率;

2)Mybatis缓存

MyBatis系统中默认定义了两级缓存:

    • 一级缓存:SqlSession级别的缓存,也称为本地缓存;默认情况下,只有一级缓存开启。
    • 二级缓存:基于namespace级别的缓存,也称为全局缓存;需要手动开启和配置。
      • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

3)一级缓存

一级缓存也叫本地缓存,与数据库同一次会话期间查询到的数据会放在本地缓存中;以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

  • 实体类

    package com.gaowl.pojo;
    
    import lombok.Data;
    
    @Data
    public class User {
        private int id;
        private String name;
        private String pwd;
    }
  • 接口及其Mapper实现

    package com.gaowl.dao;
    
    import com.gaowl.pojo.User;
    import org.apache.ibatis.annotations.Param;
    
    public interface UserMapper {
        // 根据ID查询用户
        User queryUserByID(@Param("id") int id);
    }
    <?xml version="1.0" encoding="UTF8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.gaowl.dao.UserMapper">
    
        <!-- 根据ID查询用户 -->
        <select id="queryUserByID" parameterType="int" resultType="User">
            select * from mybatis.user where id = #{id}
        </select>
    
    </mapper>

  • 当发生增、删、改请求时,会更新本地缓存

  • 手动清理缓存:sqlSeession.clearCache();

4)二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了基于namespace级别的二级缓存,一个namespace,对应一个二级缓存

  • 使用步骤

    • 序列化实体类

      package com.gaowl.pojo;
      
      import lombok.Data;
      
      import java.io.Serializable;
      
      @Data
      public class User implements Serializable {
          private int id;
          private String name;
          private String pwd;
      }
    • 开启全局缓存

      <!-- Mybatis的核心配置文件mybatis-config.xml -->
      <setting name="cacheEnabled" value="true"/>
    • 到每个Mapper.xml中使用二级缓存

      <!-- 开启当前Mapper文件的二级缓存
           此处为创建一个FIFO缓存,刷新时间为60s,最多可以存储512条引用,返回对象只读-->
      <cache
       eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"/>
    • 测试

      // 根据ID查询用户
      @Test
      public void test2(){
          SqlSession session = MybatisUtils.getSession();
          SqlSession session2 = MybatisUtils.getSession();
      
          UserMapper mapper = session.getMapper(UserMapper.class);
          User user = mapper.queryUserByID(2);
          System.out.println(user);
          session.close();
      
      
          UserMapper mapper2 = session2.getMapper(UserMapper.class);
          User user2 = mapper2.queryUserByID(2);
          System.out.println(user2);
          session2.close();
      }

  • 工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;如果开启了二级缓存,新的会话查询信息时,可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

5)缓存原理

  • 查询顺序
    • 先到二级缓存中取,没有时再到一级缓存中取,如果还没有,访问数据库,获取相应的数据


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!