mybatis

参考文档:

视频:【狂神说Java】

官方文档:https://mybatis.org/mybatis-3/zh/getting-started.html

1. Mybatis介绍

1.1 概念

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

1.2 持久化(动作)

数据持久化:持久化是程序数据在瞬时状态和持久状态间转换的过程

  • 持久化方式:数据库(jdbc),io文件持久化
  • 瞬时状态:如果存在内存,可能断电即失
1.3 持久层(名词)

完成持久化工作的代码块

2.第一个mybatis程序

步骤:

2.1 新建一个普通的maven项目

2.2 删除src

2.3 导入依赖:

mysql驱动,mybatis,junit(Java单元测试框架)

数据库版本5.7,所以驱动版本为5

    <dependencies>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.47version>
        dependency>
        
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatisartifactId>
            <version>3.5.9version>
        dependency>
        
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>
    dependencies>
2.4 创建模块Module:

编写mybatis的核心配置文件:mybatis-config.xml

XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。


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&useUnicode=true&characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            dataSource>
        environment>
    environments>
    
    <mappers>
        <mapper resource="com/company/dao/UserMapper.xml"/>
    mappers>
configuration>

编写mybatis的工具类

package com.company.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;

/**
 * author: Sheryl
 * create time: 2022/4/3 4:30 下午
 */
public class mybatisUtils {
    private static SqlSessionFactory sqlSessionFactory = null;
    //SqlSessionFactoryBuilder -> sqlSessionFactory - > sqlSession
    static{
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

流程:mybatis-config.xml -> SqlSessionFactoryBuilder -> SqlSessionFactory -> SqlSession

生命周期: 理解我们之前讨论过的不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder:一旦创建了 SqlSessionFactory,就不再需要它了。设置为一个局部变量

SqlSessionFactory: 类似于数据库连接池。一旦被创建就应该在应用的运行期间一直存在,可以重用生成多个SqlSession。最简单的就是使用单例模式(只创建一个,否则造成资源浪费)或者静态单例模式。

SqlSession: 连接到连接池的请求。每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。

类似于http请求,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。

2.5 编写代码
  1. 实体类:pojo数据库对应的实体类(将数据库用java编写)
//实体类
public class User {
    private int id;
    private String name;
    private String pwd;

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}
  1. dao接口 :
public interface userDao {
    List<User> getUserList();
}
  1. 接口实现类:UserMapper.xml

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.company.dao.userDao">
    
    <select id="getUserList" resultType="com.company.pojo.User">
        select * from mybatis.user
    select>
mapper>
2.6 测试:
public class userDaoTest {
    @Test
    public void test(){
        //获得SqlSession对象
        try (SqlSession sqlSession = mybatisUtils.getSqlSession()) {
            //执行sql
            userDao mapper = sqlSession.getMapper(userDao.class);
            List<User> userList = mapper.getUserList();
			//输出结果
            for (User user : userList) {
                System.out.println(user);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

问题1:

org.apache.ibatis.binding.BindingException: Type class com.company.dao.userDao is not known to the MapperRegistry.

分析:新建mapper没有在配置文件mybatis-config.xml中注册


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

问题2:

The error may exist in com/company/dao/UserMapper.xml

分析:由于maven在构建项目时默认是不支持打包自建的xml或者properties配置文件,所以需要添加在项目pom.xml中配置信息,支持资源导出。这里没有按照往常写法,排除src/main/resorces目录下的xml文件是因为会导致mybatis-config.xml无法找到,即问题3。

<build>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>falsefiltering>
        resource>
    resources>
build>

关于标签: 表示关于资源的信息,资源往往不是代码,无需编译,而是一些properties或XML配置文件,构建过程中会往往会将资源文件从源路径复制到指定的目标路径。

filtering,构建过程中是否对资源进行过滤,默认false
directory,资源文件的路径,默认位于${basedir}/src/main/resources/目录下
includes,一组文件名的匹配模式,被匹配的资源文件将被构建过程处理。
excludes,一组文件名的匹配模式,被匹配的资源文件将被构建过程忽略。

问题3:

Could not find resource mybatis-config.xml

分析:

注意mybatis-config.xml放在root resorces文件下,如果没有,需要对文件夹标注

由于按照往常写法,排除src/main/resorces目录下的xml文件是因为会导致mybatis-config.xml无法找到,所以不添加以下部分代码。

修改配置pom.xml文件后记得clean清除缓存,重启idea

错误写法:

<resource>
    <directory>src/main/resourcesdirectory>
    <excludes>
        <exclude>**/*.propertiesexclude>
        <exclude>**/*.xmlexclude>
    excludes>
    <filtering>falsefiltering>
resource>

3. CRUD

3.1 大致流程:
  • 编写接口方法
  • 编写mapper文件中对应的sql语句
  • 测试: 增删改需要提交事务sqlSession.commit()
package com.company.dao;
import com.company.pojo.User;

import java.util.List;

/**
 * author: Sheryl
 * create time: 2022/4/3 4:58 下午
 */
public interface userMapper {
    //查询全部用户
    List<User> getUserList();

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

    //insert
    int addUser(User user);

    //update
    int updateUser(User user);

    //delete
    int deleteUser(int id);

}


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.company.dao.userMapper">
    <select id="getUserList" resultType="com.company.pojo.User">
        select * from mybatis.user
    select>
    
    <select id="getUserByID" parameterType="int" resultType="com.company.pojo.User">
        select * from mybatis.user where id = #{id}
    select>
    
    <insert id="addUser" parameterType="com.company.pojo.User">
        insert into mybatis.user values (#{id}, #{name}, #{pwd})
    insert>

    <update id="updateUser" parameterType="com.company.pojo.User">
        update user set name = #{name}, pwd = #{pwd} where id = #{id}
    update>
    
    <delete id="deleteUser" parameterType="com.company.pojo.User">
        delete from user where id = #{id}
    delete>
mapper>
public class userMapperTest {
    @Test
    public void test(){
        //获得SqlSession对象
        try (SqlSession sqlSession = mybatisUtils.getSqlSession()) {
            //执行sql
            userMapper mapper = sqlSession.getMapper(userMapper.class);
            List<User> userList = mapper.getUserList();

            for (User user : userList) {
                System.out.println(user);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /*
    2. select
    - Id:对应的namespace中的方法名
    - ResultType:sql语句执行的返回值
    - ParamterType:传入参数的类型
    */
    @Test
    public void getUserByIDTest(){
        try(SqlSession sqlSession = mybatisUtils.getSqlSession()) {
            userMapper mapper = sqlSession.getMapper(userMapper.class);
            User user = mapper.getUserByID(1);
            System.out.println(user);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Test
    //增删改需要提交事务
    public void addUserTest(){
        try(SqlSession sqlSession = mybatisUtils.getSqlSession()) {
            userMapper mapper = sqlSession.getMapper(userMapper.class);
            int res = mapper.addUser(new User(4, "李六", "89437957"));
            sqlSession.commit();
            if(res > 0){
                System.out.println(res + "  insert sucess!");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Test
    public void updateUserTest(){
        try(SqlSession sqlSession = mybatisUtils.getSqlSession()){
            userMapper mapper = sqlSession.getMapper(userMapper.class);
            int res = mapper.updateUser(new User(1, "你好","ahjkffj3"));
            sqlSession.commit();
            if(res > 0){
                System.out.println(res + "  update sucess!");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Test
    public void deleteUserTest(){
        try(SqlSession sqlSession = mybatisUtils.getSqlSession()){
            userMapper mapper = sqlSession.getMapper(userMapper.class);
            int res = mapper.deleteUser(3);
            sqlSession.commit();
            if(res > 0){
                System.out.println(res + "  delete sucess!");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
3.2 使用map作为参数
  • 用map不需要知道数据库的具体内容,可以自己设置需要修改的参数(参数个数,参数内容自己设置),适用于参数过多/字段过长的情况
  • Map传递参数,直接在sql中取出参数即可 parameterType="map"
  • 如果需要传多个参数,用map或者注解
  • 对象传递参数,直接在sql中取对象的属性即可 parameterType="com.company.pojo.User"
  • 只有一个基本类型参数的情况下,可以直接在sql中取到 parameterType="int"
3.3 模糊查询

两种方式:推荐第一种更安全

  1. java代码执行的时候,传递通配符% %
//查询名字中含有”李“字的记录
List<User> userList = mapper.getUserLike("%李%");

<select id="getUserLike" resultType="com.company.pojo.User">
	select * from user where name like #{value}
select>
  1. 在sql拼接中使用通配符
//拼接sql有sql注入的风险
select * from user where name like "%"#{value}"%"

4. (mybatis-config.xml)配置解析

在xml标签都是有顺序的,必须按照顺序填写,否则会报错

configuration(配置):

properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
    environment(环境变量)
        transactionManager(事务管理器)
        dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
4.1 环境配置(environments)

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

尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

变换环境通过修改实现

注意一些关键点:

  • 默认使用的环境 ID(比如:default=“development”)。
  • 每个 environment 元素定义的环境 ID(比如:id=“development”)。
  • 事务管理器的配置(比如:type=“JDBC”)。
    • 在 MyBatis 中有两种类型的事务管理器(也就是 type=“[JDBC|MANAGED]”):
      • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
      • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期
  • 数据源(连接数据库)的配置(比如:type=“POOLED”)。
    • 有三种内建的数据源类型(也就是 type=“[UNPOOLED|POOLED|JNDI]”)
    • POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
    • UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。速度较慢。

Mybatis默认的事物管理器就是jdbc,连接池:POOLED

4.2 属性(properties)

属性可以在外部进行配置,并可以进行动态替换(environmnet中的property)。

外部配置:可以直接引入外部配置文件db.properties,或者在标签中直接添加属性

  1. 编写外部配置文件:db.properties(在src/main/resources/db.properties目录下)
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8
username = root
password = 123456
  1. 在mybatis-config.xml文件中引入,用标签

<properties resource="db.properties">
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
properties>
  1. 也可以在 SqlSessionFactoryBuilder.build() 方法中传入属性值。(不推荐)

如果一个属性在不只一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载:

  • 首先读取在 properties 元素体内指定的属性。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,并覆盖之前读取过的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。

因此,通过方法参数传递的属性具有最高优先级,resource 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。

如果使用上述两种方法:environment中可以不用修改

<environment id="test">
    <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>

如果动态替换,可以直接修改environmnet中的property达到相同效果。

4.3 类型别名(typeAliases)

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


<typeAliases>
	<typeAlias type="com.company.pojo.User" alias="User"/>
typeAliases>

方法二、也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean。扫描实体类的包,默认别名就为类名(一般推荐首字母消息,即“user”),可以使用注解修改别名。


<typeAliases>
        <package name="com.company.pojo"/>
typeAliases>

在实体类中添加注解,修改别名

@Alias("user")
public class User {
    ...
}

在实体类比较少的时候,推荐使用第一种方式

实体类比较多得的时候,推荐使用第二种方式

4.4 设置(settings)

设置会改变 MyBatis 的运行时行为

部分设置:

cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
4.5 映射器(mappers)

MapperRegistry:注册绑定我们的mapper文件

方式一:resources(推荐)


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

方式二:class


    <mappers>
        <mapper class="com.company.dao.userMapper"/>
    mappers>

方式三:package


<mappers>
	<package name="com.company.dao"/>
mappers>

注意点

映射接口和mapper配置文件必须同名

映射接口和mapper配置文件必须在同一个包下

5. ResultMap(解决属性名和字段名不一致的问题)

数据库:

(id, name, pwd)

实体类:

(id, name, password)

解决方式:

  1. 起别名
    <select id="getUserByID" parameterType="int" resultType="com.company.pojo.User">
        select id, name, pwd as password from mybatis.user where id = #{id}
    select>
  1. resultmap
5.1 resultMap

结果集映射:

  • id – 当前命名空间中的一个唯一标识,用于标识一个结果映射。
  • type-类的完全限定名, 或者一个类型别名。
  • result – 注入到字段或 JavaBean 属性的普通结果
  • association一个复杂类型的关联(多对一);许多结果将包装成这种类型
    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
  • collection一个复杂类型的集合(一对多)
    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
    <resultMap id="UserMap" type="User">
        
        <result column="pwd" property="password"/>
    resultMap>

多对一:关联

一对多:集合

5.2 多对一的处理

MyBatis 有两种不同的方式加载关联:

  • 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
  • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。
public class Student {
    private int id;
    private String name;
    //学生关联老师(使用teacher, 而不直接用int)
    private Teacher teacher;
}
public class Teacher {
    private int id;
    private String name;
}

按照查询嵌套处理:

    <select id="getStudent" resultMap="StudentTeacher">
        select * from student s, teacher t where s.tid = t.id
    select>
    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    resultMap>
    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id = #{id}
    select>

${column}:会被直接替换,不会转义

#{value} 会使用 ? 预处理,之后会转义成相应的数据

按照结果嵌套处理:

注意查询中的连接,以及为确保结果能够拥有唯一且清晰的名字

    <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid, s.name sname, t.name tname
        from mybatis.student s, mybatis.teacher t
        where s.tid = t.id
    select>
    <resultMap id="StudentTeacher2" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        association>
    resultMap>
5.3 一对多处理
public class Teacher {
    private int id;
    private String name;
    private List<Student> students;
}

public class Student {
    private int id;
    private String name;
    private int tid;
}
  1. 按照结果嵌套:
    <select id="getTeacher" resultMap="TeacherMap">
        select s.id sid, s.name sname, t.id tid, t.name tname
        from mybatis.student s, mybatis.teacher t
        where s.tid = t.id and t.id = #{id}
    select>
    <resultMap id="TeacherMap" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        collection>
    resultMap>
  1. 按照查询嵌套处理:
    <select id="getTeacher2" resultMap="TeacherMap2">
        select * from teacher where id = #{tid};
    select>
    <resultMap id="TeacherMap2" type="Teacher">
        
        <id property="id" column="id"/>
        <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudent"/>
    resultMap>
    
    <select id="getStudent" resultType="Student">
        select * from student where tid = #{tid}
    select>
  1. JavaType用来指定实体类中的类型
  2. ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型

6. 日志

指定 MyBatis 所用日志的具体实现,未指定时将自动查找

形式:SLF4J | LOG4J(deprecated since 3.5.9) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING

例如标准日志(STDOUT_LOGGING)实现:

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

输出结果:可以看到整个数据库连接,查询,释放连接的过程

Opening JDBC Connection
Created connection 1586845078.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e955596]
==>  Preparing: select id, name, pwd from mybatis.user where id = ?
==> Parameters: 1(Integer)
<==    Columns: id, name, pwd
<==        Row: 1, 你好, ahjkffj3
<==      Total: 1
User{id=1, name='你好', password='ahjkffj3'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e955596]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e955596]
Returned connection 1586845078 to pool.

7. 分页

分页的作用:减少数据的处理量

  1. Limit:
select * from user limit startIndex, pageSize
  1. mybatis实现分页:
  • 接口
//分页查询
List<User> getUserByLimit(Map<String, Object> map);
  • Mappe.xml
<select id="getUserByLimit" parameterType="map" resultType="User">
	select * from user limit #{start}, #{size}
</select>
  • 测试
    public void getUserByLimitTest(){
        try(SqlSession sqlSession = mybatisUtils.getSqlSession()) {
            userMapper mapper = sqlSession.getMapper(userMapper.class);
            Map<String, Object> map = new HashMap<>();
            map.put("start",1);
            map.put("size", 3);
            List<User> userByLimit = mapper.getUserByLimit(map);
            for (User user: userByLimit){
                System.out.println(user);
            }
        }
    }
  1. 分页插件(了解)

Mybatis pagehelper

8. 注解开发

8.1 使用注解CRUD

编写注解,和接口

public interface userMapper {
    //查询全部用户
    @Select("select * from user")
    List<User> getUserList();

    //根据ID查询用户
    //方法存在多个参数,所有的参数前面必须加上@Param("id")注解
    @Select("select * from user where id = #{id}")
    User getUserByID(@Param("id") int id);

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

    //update
    @Update("update user set name=#{name}, pwd=#{password} where id=#{id}")
    int updateUser(User user);

    //delete
    @Delete("delete from user where id=#{uid}")
    int deleteUser(@Param("uid") int id);
}

必须将接口绑定到注册文件:最好不要用通配符

    
    <mappers>
        <mapper class="com.company.dao.userMapper"/>
    mappers>

关于@Param( )注解

  • 基本类型的参数或者String类型,需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型的话,可以忽略,但是当有多个(基本类型的参数或者String类型),必须加上注解
  • 我们在SQL中引用的就是我们这里的@Param()中设定的属性名

9. Lombok使用

介绍:

Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。(getter setter等)

导入依赖:

    
    
        org.projectlombok
        lombok
        1.18.22
    

在实体类上添加注解。

常用注解:

@Data	//无参构造, get, set, tostring, hashcode,equals
@AllArgsConstructor	//有参构造
@NoArgsConstructor	//无参构造
//实体类
public class User {
    private int id;
    private String name;
    private String password;
}

10. 动态SQL

动态SQL:根据不同的条件去拼接SQL,生成不同的SQL语句

动态SQL种类:

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

if条件前需要添加and 或者or,从而判断多个条件的关系

    //if查询
    List<Blog> queryBlogIF(Map map);

可以用标签代替where

如果不传入“title”和“author”,那么就查询select * from blog

    <select id="queryBlogIF" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            
            <if test="title != null">
                and title = #{title}
            if>
            <if test="author != null">
                and author = #{author}
            if>
        where>
    select>
    @Test
    public void queryBlogIFTest(){
        try(SqlSession sqlSession = mybatisUtils.getSqlSession()) {
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            Map map = new HashMap();
            map.put("title", "mybatis");
            map.put("author","小李");
            List<Blog> blogs = mapper.queryBlogIF(map);
            for (Blog b : blogs){
                System.out.println(b);
            }
        }
    }
10.2 trim、where、set

< where > 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

where语句中可能带前缀prefixOverrides = “and/or”,根据情况去除。

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
trim>

set语句中可能会有后缀suffixOverrides = “,” 根据情况去除。

<trim prefix="SET" suffixOverrides=",">
  ...
trim>

必须要使用where和set标签才有用

set

    <update id="updateBlog" parameterType="Blog">
        update Blog
        <set>
            <if test="title != null">
                title = #{title},
            if>
            <if test="author != null">
                author = #{author}
            if>
        set>
        where id = #{id}
    update>
10.3 choose、when、otherwise

有点像 Java 中的 switch 语句,从多个条件中选择一个,条件或的关系

官网例子:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG。

switch -> choose

Case ->when

Default ->otherwise

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    when>
    <otherwise>
      AND featured = 1
    otherwise>
  choose>
select>
10.4 foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)

Foreach允许你指定一个集合(id表示集合名),声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。

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


<select id="queryBlogForeach" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <foreach collection="ids" item="id" open="(" close=")" separator="or">
            id = #{id}
        foreach>
    where>
select>

这里使用了list

    @Test
    public void queryBlogForeachTest(){
        try(SqlSession sqlSession = mybatisUtils.getSqlSession()) {
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            Map map = new HashMap();
            ArrayList<Integer> ids = new ArrayList<>();
            ids.add(1);
            ids.add(2);
            ids.add(3);
            map.put("ids", ids);
            List<Blog> blogs = mapper.queryBlogForeach(map);
            for (Blog b : blogs){
                System.out.println(b);
            }
        }
    }
10.5 sql

将公共部分放入< sql >内,方便进行复用。使用 引用元素

<sql id="if-select">
    <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 blog
    <where>
        <include refid="if-select">include>
    where>

select>

11. 缓存

11.1 简介
  1. 什么是缓存 [ Cache ]?
    存在内存中的临时数据。
    将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

  2. 为什么使用缓存?
    减少和数据库的交互次数,减少系统开销,提高系统效率。

  3. 什么样的数据能使用缓存?
    经常查询并且不经常改变的数据。

  4. Mybatis缓存

    1. MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
    2. MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
      1. 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地的会话缓存)
      2. 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。需要在你的 SQL 映射文件中添加一行
      3. 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
  5. 可用的清除策略有:

    1. LRU – 最近最少使用:移除最长时间不被使用的对象。
    2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
    3. SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
    4. WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
11.2 一级缓存
  • 一级缓存也叫本地缓存:

    • 与数据库同一次会话期间查询到的数据会放在本地缓存中。(sqlSession获得与释放的过程中)

    • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

    • 一级缓存默认开启

11.3 二级缓存

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

步骤:

  1. 开启全局缓存(默认开启,在mybatis-config.xml中setting中可以显式定义)
  2. 在mapper中启动cache

方式一:


<cache/>

1.什么是序列化和反序列化?

Java序列化是指把Java对象转换为字节序列的过程;Java反序列化是指把字节序列恢复为Java对象的过程;

2. 为什么要序列化?

  • 其实我们的对象不只是存储在内存中,它还需要在传输网络中进行传输,并且保存起来之后下次再加载出来,这时候就需要序列化技术。
  • 一般Java对象的生命周期比Java虚拟机短,而实际开发中如果需要JVM停止后能够继续持有对象,则需要用到序列化技术将对象持久化到磁盘或数据库。
  • 在多个项目进行RPC调用时,需要在网络上传输JavaBean对象,而网络上只允许二进制形式的数据进行传输,这时则需要用到序列化技术。
  • Java的序列化技术就是把对象转换成一串由二进制字节组成的数组,然后将这二进制数据保存在磁盘或传输网络。需要用到这对象时,磁盘或者网络接收者可以通过反序列化得到此对象,达到对象持久化的目的。

3.实现序列化

实现序列化的要求:目标对象实现Serializable接口

方式二:


<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>
  • 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
  • 查出的数据都会被默认先放在一级缓存中
  • 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
11.3 缓存失效的情况:

查询不同的记录(一级)

增删改操作,可能改变原来的数据,所以会更新缓存(一级)

手动清理缓存(一级)

sqlSession.clearCache();

查询不同的mapper.xml(2级)

先查找二级缓存,再查找一级缓存,都没有就查询数据库

你可能感兴趣的:(java后端,java)