MyBatis用法详解,基于SpringBoot优雅使用MyBatis,动态SQL,MyBatis缓存

文章目录

  • MyBatis
    • 简介
    • 基于SpringBoot的MyBatis示例
    • Mybatis架构
    • MyBatis核心组件
    • MyBatis的CRUD操作
    • 动态SQL
      • if标签
      • choose、when、otherwise 标签
      • where
      • set
      • trim
      • foreach
      • sql、include和bind
      • #{}和${}区别
      • 模糊查询like语句
    • MyBatis缓存
      • 一级缓存
      • 二级缓存

MyBatis

简介

MyBatis前身是iBatis,是 Apache 软件基金会下的一个开源项目。2010年该项目从 Apache 基金会迁出,并改名为 MyBatis。

  • MyBatis是一款优秀的半自动化的持久层的框架。
  • 它支持定制化SQL、存储过程一级高级映射。
  • 避免了几乎所有的JDBC代码和手动参数配置以及获取结果集。
  • 使用简单的XML或者注解来配置和映射原生类型、接口和Java POJO(Plain Old Java Object普通老式Java 对象)为数据库中的记录。

基于SpringBoot的MyBatis示例

由于现在SpringBoot工程盛行,下面用SpringBoot举个简单的例子:

新建一个maven工程,pom.xml引入MyBatis、MySQL和test依赖:

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.2.7.RELEASEversion>
    parent>
    <dependencies>
        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>1.3.2version>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

启动类Application.Java,要加上@MapperScan扫描mapper接口:

@SpringBootApplication
@MapperScan("com.**.mapper*")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

配置文件application.yml,配置数据库和MyBatis的SQL映射文件位置:

spring:
  datasource:
    url: jdbc:mysql://localhost/springbootest?characterEncoding=UTF-8&useUnicode=true&useSSL=false&autoReconnect=true&serverTimezone=UCT
    username: root
    password: root
    driverClassName: com.mysql.cj.jdbc.Driver
mybatis:
  mapper-locations: classpath:mapping/*Mapping.xml

User.java,实体:

public class User {
    private Integer id;
    private String name;
    private String sex;
    private Integer age;
    private String des;
	//加上getter、setter和toString
}

UserService.java

public interface UserService {
    List<User> findAll();
}

UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;
    public List<User> findAll(){
        return userMapper.findAll();
    }
}

UserMapper.java

@Mapper
public interface UserMapper {
    List<User> findAll();

}

UserMapping.xml,写一个查询映射



<mapper namespace="com.mybatis.mapper.UserMapper">
    <select id="findAll" resultType="com.mybatis.entity.User">
        select id,name,sex,age,des from test_user;
    select>
mapper>

TestMyBatis.java

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TestMyBatis {
    @Autowired
    SqlSessionFactory sqlSessionFactory;
    @Test
    public void test01(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
        List<User> list =  userMapper.findAll();
        for (User user : list) {
            System.out.println(user.toString());
        }
    }
}

工程结构如下
MyBatis用法详解,基于SpringBoot优雅使用MyBatis,动态SQL,MyBatis缓存_第1张图片

建表语句和插入语句

DROP TABLE IF EXISTS `test_user`;
CREATE TABLE `test_user` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(40) DEFAULT NULL,
  `sex` varchar(2) DEFAULT NULL,
  `age` int(3) DEFAULT NULL,
  `des` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`id`)
);
INSERT INTO `test_user` VALUES (1,'张三','男',20,'程序员'),(2,'李四','女',18,'程序员');

上面测试代码中注入SqlSessionFactory工厂,该工厂的用途是创建SqlSession。用SqlSession为数据库访问接口UserMapper创建一个代理对象。MyBatis会把接口方法findAll和sql映射文件中配置的SQL关联起来,这样调用该方法等于执行相关的SQL。

运行测试代码,结果如下:

User{id=1, name='张三', sex=, age=20, des='程序员'}
User{id=2, name='李四', sex=, age=18, des='程序员'}

Mybatis架构

(1)接口层:负责增删改查的接口

(2)数据处理层:

​ 参数映射(ParameterHandler):参数映射配置、参数映射解析和参数类型解析。

​ SQL解析(SQLSource):SQL获取、SQL解析和动态SQL。

​ 配置加载(Executor):SimpleExecutor、BatchExecutor和ResultExecutor。

​ 结果映射(ResultSetHandler):结果映射配置、结果类型转换和结果数据拷贝。

(3)基础支撑层:连接管理、事务管理、配置加载和缓存处理。

MyBatis核心组件

  • SqlSessionFactoryBuilder:读取配置、创建SqlSessionFactory。
  • SqlSessionFactory:会话工厂,在应用运行期间不建议多次创建,建议使用单例模式,用来创建SqlSession。
  • SqlSession:类似于JDBC的Connection,默认使用DefaultSqlSession,提供操作数据库的增删改查方法,可以调用操作方法,也可以操作mapper组件。
  • Executor:执行器,SqlSession本身不能直接操作数据库,需要Executor来完成,该接口有两个实现:缓存执行器(默认);基本执行器。
  • MappedStatement:映射语句封装和执行语句时的信息:SQL、入参和结果等信息。

MyBatis用法详解,基于SpringBoot优雅使用MyBatis,动态SQL,MyBatis缓存_第2张图片

MyBatis的CRUD操作

insert、update和delete标签常用参数

参数 作用 备注
id 命名空间中的唯一标识 一般和接口名同名
parameterType 传入参数的全限定类名或别名 一般不用写,MyBatis会自动推断出参数类型,默认unset
flushCache 任何修改语句被调用二级缓存都会被清空 默认值true
timeout 等待数据库返回请求结果的秒数 默认unset依赖驱动,超时返回异常

select标签常用参数

参数 作用 备注
parameterType 传入参数的全限定类名或别名 一般不用写,MyBatis会自动推断出参数类型,默认unset
resultType 返回值类型,全限定类名或别名 如果返回集合应该是集合可以包含的类型,而不是集合本身;不能和resultMap同时使用
resultMap 外部resultMap的命名引用 不能和resultType同时使用
useCache 本条语句结果会被二级缓存 select默认值true
timeout 等待数据库返回请求结果的秒数 默认unset依赖驱动,超时返回异常

这里写一个插入语句:

//UserService.java
public interface UserService {
    List<User> findAll();
    int addUser(User user);
}

//UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;
    public List<User> findAll(){
        return userMapper.findAll();
    }
    @Override
    public int addUser(User user) {
        return userMapper.addUser(user);
    }
}

//UserMapper.java
@Mapper
public interface UserMapper {
    List<User> findAll();
    int addUser(@Param("user") User user);
}

//UserMapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.mapper.UserMapper">
    <insert id="addUser">
        insert into test_user (name,sex,age,des)  values (#{user.name},#{user.sex},#{user.age},#{user.des})
    </insert>
    <select id="findAll" resultType="com.mybatis.entity.User">
        select id,name,sex,age,des from test_user
    </select>
</mapper>

//TestMyBatis.java
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TestMyBatis {
    @Autowired
    SqlSessionFactory sqlSessionFactory;
    @Test
    public void test01(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setName("王五");
        user.setAge(10);
        user.setDes("农民工");
        user.setSex("男");
        userMapper.addUser(user);
        List<User> list =  userMapper.findAll();
        for (User u : list) {
            System.out.println(u.toString());
        }
    }
}

执行测试方法打印如下:

User{id=1, name='张三', sex=男, age=20, des='程序员'}
User{id=2, name='李四', sex=女, age=18, des='程序员'}
User{id=3, name='王五', sex=男, age=10, des='农民工'}

动态SQL

MyBatis的强大特性之一就是它的动态SQL,动态SQL就是对SQL语句进行灵活操作,通过表达式进行判断,对SQL进行灵活的拼装或者组装。

if标签

<if test="boolean表达式"></if>

例如用名字查询的时候,这里只演示映射文件写法:

<select id="findAll" resultType="com.mybatis.entity.User">
    select id,name,sex,age,des from test_user
    <if test="name!=null and name !=''">
        where name = #{name}
	</if>
</select>

但是要注意传入参数的时候要使用@Param

List<User> findAll(@Param("name") String name);

choose、when、otherwise 标签

类似于Java中的switch判断

<select id="findAll" resultType="com.mybatis.entity.User">
    select id,name,sex,age,des from test_user where 1 = 1
    <choose>
        <when test="name!=null and name!=''">
            and name = #{name}
        </when>
        <otherwise>
            and sex = #{sex}
        </otherwise>
    </choose>
</select>

where

如果where标签中的查询语句开头有一个and或者or,会把第一个and或者or替换成where,这样就避免了where 1=1的形式;如果没有就直接加上where。

<select id="findAll" resultType="com.mybatis.entity.User">
    select id,name,sex,age,des from test_user
    <where>
        <if test="name != null and name != ''">
            and name = #{name}
    	</if>
    </where>
</select>

set

set标签和where功能类似,也能根据sql动态去掉最后的逗号,并在前面加上set关键字。

<update id="update">
    update test_user
    <set>
        <if test="user.name != null and user.name != ''">
            name = #{user.name},
        </if>
        <if test="user.sex != null and user.sex != ''">
            sex = #{user.sex},
        </if>
    </set>
    where id = #{user.id}
</update>

trim

trim的功能更强大:

  • prefix:给语句加前缀
  • prefixOVerrides:去掉语句最前面的字符
  • suffix:给语句加后缀
  • suffixOverrides:去掉语句最后面的字符
<select id="findAll" resultType="com.mybatis.entity.User">
    select id,name,sex,age,des from test_user
    <trim prefix="where" suffixOverrides="and">
        <if test="id!=null">
            id=#{id} and
        </if>
        <if test="name!=null">
            name=#{name} and
        </if>
    </trim>
</select>

foreach

用于迭代集合或者数组

例如使用in的时候,ids传入id的数组:

<select id="findAll" resultType="com.mybatis.entity.User">
    select id,name,sex,age,des from test_user where id in
    <foreach collection="ids" open="(" close=")" separator=",">
    </foreach>
</select>

或者批量插入数据时:

<insert id="addUser">
    insert into test_user (name,sex,age,des)  values 
    <foreach collection="users" separator="," item="user">
    	(#{user.name}, #{user.sex}, #{user.age}, #{user.des})
    </foreach>
</insert>

sql、include和bind

使用sql标签可以吧相同的SQL片段提取出来,使用include可以在任意位置中使用,bind标签可以使用OGNL表达式创建一个变量,并将其绑定在上下文中:

<sql id="test_user">
    select id,name,sex,age,des from test_user
</sql>
<select id="findAll" resultType="com.mybatis.entity.User">
    <include refid="test_user"></include>
    <where>
        <bind name="nameOrId" value="'%'+ nameOrId +'%'"/>
        and (id like #{nameOrId} or name like #{nameOrId})
    </where>
</select>

#{}和${}区别

先看代码

//映射文件
<select id="queryByIdAndName" resultType="com.mybatis.entity.User">
    select id,name,sex,age,des from test_user where id = #{id} and name = '${name}'
</select>
//测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TestMyBatis {
    @Autowired
    SqlSessionFactory sqlSessionFactory;
    @Test
    public void test01(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
        List<User> list =  userMapper.queryByIdAndName("王五",2);
    }
}

开启debug

logging:
  level:
    com.mybatis.mapper: debug

输出:

: HikariPool-1 - Starting...
: HikariPool-1 - Start completed.
: ==>  Preparing: select id,name,sex,age,des from test_user where id = ? and name = '王五' 
: ==> Parameters: 2(Integer)
: <==      Total: 0
: HikariPool-1 - Shutdown initiated...
: HikariPool-1 - Shutdown completed.
  • #{}是占位符,预编译处理;${}是拼接符,字符串替换,没有预编译处理。
  • MyBatis在处理#{}时,传入的参数是以字符串传入,会将sql中的#{}替换为?,调用PreparedStatement的set方法来赋值。
  • MyBatis在处理${}时,是原值传入,就是把${}替换成变量的值,相当于JDBC中的Statement编译。
  • 替换变量后,#{}会给变量加上单引号;${}不会给变量加上带引号。
  • #{}可以有效防止SQL注入,提高系统安全性;${}不能防止SQL注入。

模糊查询like语句

  • ‘%${name}%’可能引起SQL注入,不推荐。
  • "%"#{name}"%"在解析#{}时会自动在两侧加上单引号,所有这里不能使用单引号。
  • concat('%',#{name},'%')使用concat函数,推荐。
  • 使用bind标签,参考上面bind标签使用。

MyBatis缓存

简单来讲,缓存就是把数据存储在缓冲区里的内容,或者可以理解为存储在内存中的内容。用户可以将经常查询的数据放到缓存中,再次使用时直接从缓存中取值,而不需要再查询数据库。这样做的优点是响应迅速,减少了系统资源(网络资源、CPU资源等)开销;缺点是需要占用内存资源,服务器一旦关机,缓存就会丢失。

一级缓存

作用域是SqlSession级别的,每个SqlSession对象都有一个哈希表用于存放缓存数据,不同的SqlSession缓存数据不共享。同一个SqlSession对象执行两次相同的查询,第一次查询完毕之后会将结果缓存起来,第二次就不会在数据库查询,从缓存中直接获取。如果执行了增删改操作,缓存会被清空。一级缓存默认开启。

二级缓存

作用域是namespace级别的,SqlSession共享,不同的SqlSession对象查询两次相同的语句,第一次会查数据库,第二次会从缓存从获取。如果执行了增删改操作,缓存会被清空。二级缓存默认不开启,可以通过配置文件开启:

<settings>
    <setting name="cacheEnabled" value="true"/>
settings>

或者SpringBoot配置开启,这里只演示yaml格式:

mybatis:
  configuration:
    cache-enabled: true

二级缓存参数:

参数名 作用 备注
eviction 清除缓存回收策略LRU、FIFO、SOFT和WEAK
flushInterval 到时间缓存清空 单位毫秒
readOnly 只读,只读的缓存会给调用者返回缓存对象相同的实例 比较耗性能,默认false
size 要记住缓存的对象数目和运行环境的可用内存资源数目 默认1024

回收策略:

策略 作用 备注
LRU 最近最少使用的,最长时间不被使用的 默认值
FIFO 先进先出
SOFT 移除基于垃圾回收器状态和软引用规则的对象
WEAK 更积极的移除基于垃圾收集器状态和弱引用规则的对象

你可能感兴趣的:(java,java,MyBatis,动态SQL,MyBatis缓存,MyBatis详解)