Mybatis

目录

  • Mybatis
    • 1.简介
      • 1.什么是Mybatis
      • 2.持久层
        • 数据持久化
        • 为什么需要持久化?
      • 3.持久层
      • 4.为什么需要Mybatis
    • 2.第一个Mybatis程序
      • 1.搭建环境
      • 2.创建一个模块
        • 编写mybatis核心配置文件
        • 编写mybatis的工具类
      • 3.编写代码
        • 实体类
        • Dao接口
        • 接口实现类
      • 4.测试
        • 第一种方式:
        • 在测试的过程中可能遇到的问题:
        • 第二种方式:
        • 三个重要的类
        • 七个步骤
    • 3.CRUD(增删改查)
      • 1.namespace
      • 2.select
      • 3.Insert
      • 4.update
      • 5.delete
      • 具体步骤
      • 6.分析错误
      • 7.万能的map
      • 8.模糊查询怎么写
    • 4.配置解析
      • 1.核心配置文件
      • 2.环境配置(environments)
        • 事务管理
        • 数据源
      • 3.属性(properties)
        • 编写一个配置文件
        • 在核心配置文件中映入
      • 4.类型别名(typeAliases)
        • 第一种方式:
        • 第二种方式:
        • 默认别名
      • 5.设置(settings)
      • 6.其他
      • 7.映射器(mappers)
        • 方式一:使用类名路径的资源引用
        • 方式二:使用映射器接口实现类的完全限定类名
        • 方式三:使用包名进行注册绑定
      • 8.生命周期和作用域
        • 流程:
        • SqlSessionFactoryBulider:
        • SqlSessionFactory
        • SqlSeesion
    • 5.解决属性名和字段名不一致的问题
      • 1.问题
      • 2.resultMap
    • 6.日志
      • 1.日志工厂
      • 2.Log4j
        • 什么是LOG4J?
        • 1.先导入log4j的包
        • 2.log4j.properties
        • 3.配置为log4j为日志的实现
        • 4.Log4j的使用
        • 简单使用
    • 7.分页
      • 思考:为什么要分页?
      • 1.使用limit分页
      • 2.RowBounds分页
      • 3.分页插件
    • 8.使用注解开发
      • 1.面向接口编程
      • 2.使用注解编程
      • Mybatis详细的执行流程!
      • 3.CRUD
      • 4.关于@param注解
    • 9.Lombok
    • 10.多对一处理
      • 测试搭建环境
      • 复杂的SQL查询
        • 按照查询嵌套处理
        • 按照结果嵌套处理
    • 11.一对多
      • 环境搭建
      • 小结
      • 面试高频问题:
    • 12.动态SQL
      • 搭建环境
        • 创建一个基础工程:
      • IF
      • choose、when、otherwise
      • trim、where、set
        • trim的用法
      • SQL片段
      • Foreach
    • 13.缓存
      • 简介
      • Mybatis缓存
      • 一级缓存
      • 二级缓存
      • 缓存原理
      • 自定义缓存-ehcache

1.简介

1.什么是Mybatis

Maven依赖:

<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version>
</dependency>

2.持久层

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
  • 内存:断电即失
  • 数据库(jdbc),io文件持久化
  • 生活中的持久化:冷藏

为什么需要持久化?

  • 有一些对象不能让他丢掉。
  • 内存太贵

3.持久层

Dao层,Service层,Controller层

  • 完成持久化工作的代码块
  • 层界限十分明显

4.为什么需要Mybatis

  • 方便
  • 传统的JDBC代码太复杂,简化框架
  • 不用Mybatis也可以
  • 优点:
    • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
    • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
    • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
    • 提供映射标签,支持对象与数据库的orm字段关系映射
    • 提供对象关系映射标签,支持对象关系组建维护
    • 提供xml标签,支持编写动态sql。

最重要的一点:使用的人多

2.第一个Mybatis程序

思路:搭建环境-导入Mybatis-编写代码-测试

1.搭建环境

搭建数据库

CREATE DATABASE mybatis;
USE mybatis;
CREATE TABLE user(id INT(20) NOT NULL,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,'李四','123456'),
(3,'王五','123456')

新建项目

01.新建一个普通的Maven项目

02.删除src目录

03.导入依赖数据库,mybatis junit

    <!--导入依赖--><dependencies><!--mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.46</version></dependency><!--mybatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version></dependency><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency></dependencies>

2.创建一个模块

编写mybatis核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//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?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments>
</configuration>

编写mybatis的工具类

package xiaoqi.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;//获取sqlSessionFactory
public class MybatisUtils {private static SqlSessionFactory sqlSessionFactory;static {String resources = "mybatis-config.xml";try {//使用mybatis获取SqlsessionFactory对象InputStream inputStream = Resources.getResourceAsStream(resources);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (IOException e) {e.printStackTrace();}}//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法public static SqlSession getSqlSession(){//SqlSession是Mybatis最重要的构建之一,可以简单的认为Mybatis一系列的配置目的是生成类似// JDBC生成的Connection对象的SqlSession对象,这样才能与数据库开启“沟通”,// 通过SqlSession可以实现增删改查(当然现在更加推荐是使用Mapper接口形式)//SqlSession sqlSession = sqlSessionFactory.openSession();return sqlSessionFactory.openSession();}}

3.编写代码

实体类

package xiaoqi.pojo;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 String getName() {return name;}public String getPwd() {return pwd;}public void setId(int id) {this.id = id;}public void setName(String name) {this.name = name;}public void setPwd(String pwd) {this.pwd = pwd;}
}

Dao接口

public interface UserDao {//可以避免JDBC代码和手动设置参数List<User> getUserList();
}

接口实现类

在JDBC中,如果想要实现接口必须重写方法,在这里我们把原来的接口转变为一个Mapper配置文件。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="xiaoqi.dao.UserDao"><!--select查询语句--><!--id相当于重写方法的名字们这里是重写方法,当实现一个接口的时候以前是需要重写方法的--><!--resultType时返回类型--><select id="getUserList" resultType="xiaoqi.pojo.User">select * from mybatis.user</select>
</mapper>

4.测试

主要是为了获得SqlSession对象

第一种方式:

package xiaoqi.dao;import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import xiaoqi.pojo.User;
import xiaoqi.utils.MybatisUtils;import java.util.List;public class UserDaoTest {@Testpublic void test(){//第一步获取SqlSession对象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:执行sql getMapperUserDao userDao = sqlSession.getMapper(UserDao.class);List<User> userList = userDao.getUserList();for (User user : userList){System.out.println(user);}sqlSession.close();}
}

在测试的过程中可能遇到的问题:

1.没有注册mapper:org.apache.ibatis.binding.BindingException: Type interface com.kuang.dao.UserDao is not known to the MapperRegistry.

​ 在mybatis的核心配置文件里面添加注册

2… xml文件找到不到:java.io.IOException: Could not find resource xiaoqi/dao/UserMapper.xml

3.方法名错误

4.返回类型不对

5.Maven导出资源问题

第二种方式:

package xiaoqi.dao;import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import xiaoqi.pojo.User;
import xiaoqi.utils.MybatisUtils;import java.util.List;public class UserDaoTest {@Testpublic void test(){//第一步获取SqlSession对象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:执行sql getMappertry {UserDao userDao = sqlSession.getMapper(UserDao.class);List<User> userList = userDao.getUserList();for (User user : userList){System.out.println(user);}//方式二:不推荐使用List<User> userList1 = sqlSession.selectList("xiaoqi.dao.UserDao.getUserList");for (User user : userList1){System.out.println(user);}} catch (Exception e) {e.printStackTrace();}finally {sqlSession.close();}}
}

三个重要的类

七个步骤

3.CRUD(增删改查)

1.namespace

namespace中的包名要和Dao/mapper接口的包名一致!

2.select

选择,查询语句:

  • id:就是对应namespace中的方法名
  • resultType:sql语句执行的返回值
  • prameterType:参数类型

3.Insert

4.update

5.delete

具体步骤

  1. 编写接口

    //获取全部用户List<User> getUserList();//根据ID查询用户User getUserById(int id);//insert用户int addUser(User user);//修改用户int updateUser(User user);//删除一个用户int deleteUser(int id);
  2. 编写mapper对应的sql语句

    <select id="getUserList" resultType="xiaoqi.pojo.User">select * from mybatis.user</select><select id = "getUserById" resultType="xiaoqi.pojo.User">select * from mybatis.user where id = #{id}</select><!--对象中的属性可以直接取出来--><insert id="addUser" parameterType="xiaoqi.pojo.User" >insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd})</insert><update id="updateUser" parameterType="xiaoqi.pojo.User">update mybatis.user set name = #{name}, pwd = #{pwd} where id = #{id}</update><delete id="deleteUser" parameterType="xiaoqi.pojo.User">delete from mybatis.user where id = #{id}</delete>
    
  3. 测试代码

    public void test(){//第一步获取SqlSession对象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:执行sql getMappertry {UserMapper userDao = sqlSession.getMapper(UserMapper.class);List<User> userList = userDao.getUserList();for (User user : userList){System.out.println(user);}//方式二:不推荐使用List<User> userList1 = sqlSession.selectList("xiaoqi.dao.UserMapper.getUserList");for (User user : userList1){System.out.println(user);}} catch (Exception e) {e.printStackTrace();}finally {sqlSession.close();}}@Testpublic void getUserById(){SqlSession sqlSession1 = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession1.getMapper(UserMapper.class);User userById = mapper.getUserById(1);System.out.println(userById);sqlSession1.close();}@Test//增删改需要提交事务public void addUser(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper userMapper = sqlSession.getMapper(UserMapper.class);int res =userMapper.addUser(new User(4,"za","123456"));if (res > 0){System.out.println("插入成功");}//提交事务sqlSession.commit();sqlSession.close();}@Testpublic void updateUser(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);mapper.updateUser(new User(1,"xx","789456"));sqlSession.commit();sqlSession.close();}@Testpublic void deleteUser(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);mapper.deleteUser(1);sqlSession.commit();sqlSession.close();}
    

    注意:增删改需要提交事务

6.分析错误

  • 标签要一一对应
  • resources绑定mapper,需要使用路径
  • 程序配置文件必须符合规范
  • NullPointerException,没有注册到资源
  • 输出的xml中有乱码,需要修改UTF-8
  • maven资源没有导出

7.万能的map

使用Map传递参数:方便,灵活

假设我们的实体类,或者数据库中的表,字段或者参数过多,我们应该考虑使用map!

//万能的map
//视频中有错误
int addUser2(Map<String,Object> map);
<insert id="addUser2" parameterType="map">insert into mybatis.user (id,name,pwd) values (#{userid},#{username},#{password});
</insert>
public void addUser2(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);Map<String, Object> map = new HashMap<>();map.put("userid",5);map.put("username","hh");map.put("password","123456");mapper.addUser2(map);sqlSession.commit();sqlSession.close();
}

查询map代码块

User getUserById2(Map<String,Object> map);
<select id="getUserById2" resultType="xiaoqi.pojo.User">select * from mybatis.user where id = #{id}
</select>
@Test
public void getUserById2(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);Map<String,Object> map = new HashMap<>();map.put("id",5);User userById2 = mapper.getUserById2(map);System.out.println(userById2);sqlSession.commit();sqlSession.close();}

Map传递参数,直接在sql中取出key即可! parameterType=“map”

对象传递参数,直接在sql中取对象的属性即可 parameterType=“Object”

只有一个参数的情况下,可以直接在sql中取到

多个参数用map或者注解

8.模糊查询怎么写

java代码执行的时候,传递通配符:%

为了避免sql注入,说一在sql拼接中使用通配符

4.配置解析

1.核心配置文件

  • mybatis-config.xml
  • Mybatis的配置文件包含了回深深影响MyBatis行为的设置和属性信息

2.环境配置(environments)

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中,尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

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

事务管理

数据源

3.属性(properties)

我们可以properties属性实现引用配置文件

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置【db.properties】

编写一个配置文件

driver = com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=false
username=root
password=123456

在核心配置文件中映入

<!--引入外部配置文件-->
<properties resource="db.properties"><property name="username" value="username"/><property name="password" value="password"/>
</properties>

可以直接映入外部配置文件不在properties中写属性

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

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

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

4.类型别名(typeAliases)

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

第一种方式:

<!--可以给实体类起别名,减少冗余-->
<typeAliases><typeAlias type="xiaoqi.pojo.User" alias="User"/>
</typeAliases>

第二种方式:

扫描实体类的包,它的默认别名就为这个类的类名首字母大/小写

<!--可以给实体类起别名,减少冗余-->
<typeAliases><package name="xiaoqi.pojo"/>
</typeAliases>

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

如果实体类比较多,建议使用第二种

第一种可以自定义别名,第二种则不行【注解可以作为别名使用】

默认别名

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

5.设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。

设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 true | false false (在 3.4.1 及之前的版本中默认为 true)
multipleResultSetsEnabled 是否允许单个语句返回多结果集(需要数据库驱动支持)。 true | false true
useColumnLabel 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 true | false true
useGeneratedKeys 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 true | false False
autoMappingBehavior 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 NONE, PARTIAL, FULL PARTIAL
autoMappingUnknownColumnBehavior 指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARNFAILING: 映射失败 (抛出 SqlSessionException) NONE, WARNING, FAILING NONE
defaultExecutorType 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 SIMPLE REUSE BATCH SIMPLE
defaultStatementTimeout 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 任意正整数 未设置 (null)
defaultFetchSize 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 任意正整数 未设置 (null)
defaultResultSetType 指定语句默认的滚动策略。(新增于 3.5.2) FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置) 未设置 (null)
safeRowBoundsEnabled 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 true | false False
safeResultHandlerEnabled 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。 true | false True
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False
localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 SESSION | STATEMENT SESSION
jdbcTypeForNull 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 OTHER
lazyLoadTriggerMethods 指定对象的哪些方法触发一次延迟加载。 用逗号分隔的方法列表。 equals,clone,hashCode,toString
defaultScriptingLanguage 指定动态 SQL 生成使用的默认脚本语言。 一个类型别名或全限定类名。 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5) 一个类型别名或全限定类名。 org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 true | false false
returnInstanceForEmptyRow 当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2) true | false false
logPrefix 指定 MyBatis 增加到日志名称的前缀。 任何字符串 未设置
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
proxyFactory 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 CGLIB | JAVASSIST JAVASSIST (MyBatis 3.3 以上)
vfsImpl 指定 VFS 的实现 自定义 VFS 的实现的类全限定名,以逗号分隔。 未设置
useActualParamName 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) true | false true
configurationFactory 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) 一个类型别名或完全限定类名。 未设置
shrinkWhitespacesInSql 从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5) true | false false
defaultSqlProviderType Specifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type(or value) attribute on sql provider annotation(e.g. @SelectProvider), when these attribute was omitted. A type alias or fully qualified class name Not set

6.其他

  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
    • mybatis-plus
    • mybatis-generator-core
    • 通用mapper

7.映射器(mappers)

注册绑定我们的Mapper文件

方式一:使用类名路径的资源引用

<mappers><mapper resource="UserMapper.xml"/>
</mappers>

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

<mappers><mapper class="xiaoqi.dao.UserMapper"/>
</mappers>
  • 接口和mapper配置文件必须同名
  • 接口和mappper配置文件必须在同一个包下

方式三:使用包名进行注册绑定

<mappers><package name="xiaoqi.dao"/>
</mappers>
  • 接口和mapper配置文件必须同名
  • 接口和mappper配置文件必须在同一个包下

8.生命周期和作用域

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

流程:

SqlSessionFactoryBulider:

  • 一旦创建SqlSessionFactory,就不需要它了
  • 局部变量

SqlSessionFactory

  • 可以想象为:数据库连接池
  • 一旦创建就一直存在,没有任何理由丢弃它或创建新的实例
  • 因为SqlSessionFactory的最佳作用域是应用作用域
  • 最简单的就是使用单例模式或者静态单例模式

SqlSeesion

  • 每个线程都应该有自己的SqlSession实例;连接到连接池的一个请求

  • 需要开始和关闭

  • SqlSession不是线程安全的,所以不能被共享,最佳的作用域是放到一个方法里,用完就关闭,否则资源被占用

这里面的每一个mapper,就代表一个具体的业务

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

1.问题

数据库中的字段

新建一个项目,拷贝之前的,测试实体类字段不一致

出现了问题

//  select * from mybatis.user where id = #{id}
//  select id,name,pwd from mybatis.uer where id  =#{id}

解决方法:

  • 起别名

2.resultMap

结果集映射

id name pwd
id name password
<!--结果集映射-->
<resultMap id="UserMap" type="User"><!--column 数据库中的字段 property 实体类中的属性--><result column="id" property="id"></result><result column="name" property="name"></result><result column="pwd" property="password"></result>
</resultMap>
<select id = "getUserById" resultMap="UserMap">select * from mybatis.user where id = #{id}
</select>
  • resultMap 元素是 MyBatis 中最重要最强大的元素
  • 对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。

  • ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们
  • 世界总是这么简单就好啦

6.日志

1.日志工厂

如果一个数据库操作出现了异常,我们需要排错,日志就是最好的助手!

曾经的:sout、debug

现在的日志工厂

  • SLF4J
  • LOG4J
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING
  • NO_LOGGING

在mybatis中具体使用哪一个实现,在设置中设定

STDOUT_LOGGING 标准日志输出

在Mybatis核心配置文件中,配置我们的日志!

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

2.Log4j

什么是LOG4J?

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等
  • 我们也可以控制每一条日志的输出格式;
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
  • 最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

1.先导入log4j的包

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>

2.log4j.properties

#将等级为DEBUG的日志信息输出到console和file这两个目的地,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/kuang.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

3.配置为log4j为日志的实现

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

4.Log4j的使用

简单使用

  1. .在要使用Log4j的类中导入 import org.apache.log4j.Logger;

  2. 生成一个日志对象,参数为当前类的class

    static Logger logger = Logger.getLogger(UserDaoTest.class); //UserDaoTest为相关的类

  3. 日志级别

logger.info("info:进入了log4j");//进入了log4j
logger.debug("debug:进入了log4j");
logger.error("error:进入了log4j");

7.分页

思考:为什么要分页?

  • 减少数据的处理量

1.使用limit分页

SELECT * from user limit startIndex,pagesize

使用Mybatis实现分页,核心SQL

  1. 接口

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

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

    public void getUserByLimit(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);HashMap<String,Integer> map = new HashMap<>();map.put("startIndex",1);map.put("pageSize",2);List<User> userByLimit = mapper.getUserByLimit(map);for (User user : userByLimit){System.out.println(user);}sqlSession.close();
    }
    

2.RowBounds分页

不再使用sql语言实现分页

  1. 接口

    List<User> getUserByRowBounds();
    
  2. Mapper.xml

    <select id = "getUserByRowBounds"  resultMap="UserMap">select * from mybatis.user
    </select>
    
  3. 测试

     @Testpublic  void getUserByRowBounds(){SqlSession sqlSession = MybatisUtils.getSqlSession();//通过java代码层面实现分页//RowBounds实现RowBounds rowBounds = new RowBounds(0,2);//每一页有两个List<User> userList = sqlSession.selectList("xiaoqi.dao.UserMapper.getUserByRowBounds",null,rowBounds);for (User user : userList){System.out.println(user);}sqlSession.close();}
    

3.分页插件

了解即可

8.使用注解开发

1.面向接口编程

大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
根本原因:解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得更容易,规范性更好
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之前的协作关系则成为系统设计的关键,小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容,面向接口编程就是指按照这种思想来编程。

关于接口的理解

接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离)的分离。

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

接口应有两类:

- 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
- 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);

一个体有可能有多个抽象面,抽象体与抽象面是有区别的

三个面向区别

  • 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法
  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现。
  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构

2.使用注解编程

使用注解来映射简单语句会使代码显得更加简洁,然而对于稍微复杂一点的语句,Java 注解就力不从心了,并且会显得更加混乱。 因此,如果你需要完成很复杂的事情,那么最好使用 XML 来映射语句。

  1. 注解在接口上实现

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

    <mappers><mapper class="xiaoqi.dao.UserMapper"></mapper>
    </mappers>
    
  3. 测试

    @Testpublic void test(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> users = mapper.getUsers();for (User user : users) {System.out.println(user);}sqlSession.close();
    }
    
    • 底层实现机制:反射
    • 底层:动态代理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V27wTVeN-1635337754510)(C:/Users/77/AppData/Roaming/Typora/typora-user-images/image-20211020152954413.png)]

Mybatis详细的执行流程!

3.CRUD

我们可以在工具类创建的时候实现自动提交事务

  1. 在MybatisUtils中的openSession 设置为true自动提交

     public static SqlSession getSqlSession(){return sqlSessionFactory.openSession(true);}
    
  2. 编写接口,增加注解

       /*@Param是MyBatis所提供的(org.apache.ibatis.annotations.Param),作为Dao层的注解,作用是用于传递参数,从而可以与SQL中的的字段名相对应,一般在2=<参数数<=5时使用最佳。*/@Select("select * from mybatis.user where id = #{id}")User getUserById(@Param("id") int id);@Insert("insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd})")int addUser(User user);@Update("update mybatis.user set name = #{name},pwd = #{pwd} where id = #{id}")int updateUser(User user);@Delete("delete from user where id = #{id}")int deleteUser(@Param("id") int id);
    
  3. 测试【我们必须要将接口绑定注册到核心配置文件中mybatis-config.xml】

         SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);/* List<User> users = mapper.getUsers();for (User user : users) {System.out.println(user);User userById = mapper.getUserById(1);System.out.println(userById);mapper.addUser(new User(5,"hh","123456"));mapper.updateUser(new User(5,"zaa","456"));}*/mapper.deleteUser(5);
    

4.关于@param注解

  • 基本类型的参数或者String类型,需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型的话,可以忽略,但是建议都加上
  • 我们在SQL中引用的就是我们在@Param("")中设定的东西

9.Lombok

Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.

Project Lombok是一个java库,它可以自动插入编辑器和构建工具,使java更加丰富多彩。永远不要再编写另一个getter或equals方法,使用一个注释,你的类就有了一个功能齐全的构建器,自动化你的日志变量,等等。

  • java library
  • plugs
  • build tools
  • 使用一个注释,你的类就有了一个功能齐全的构建器

使用步骤:

  1. 在IDEA中安装Lombok插件

  2. 在项目中导入jar包

      <dependencies><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version></dependency></dependencies>
    
  3. 在实体类上加注解即可‘

    package xiaoqi.pojo;import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;@Data
    @AllArgsConstructor
    @NoArgsConstructor
    @ToString
    public class User {private int id;private String name;private String pwd;}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k3hjracc-1635337754511)(C:/Users/77/AppData/Roaming/Typora/typora-user-images/image-20211021094112970.png)]

注解:

@Data:无参构造,get、set、toString、hashcode、equals

@AllArgsConstructor:有参构造

@NoArgsConstructor:无参构造

@ToString:重写ToString方法

10.多对一处理

  • 多个学生对应一个老师

  • 对于学生而言,关联…多个学生关联一个老师【多对一】

  • 对于老师而言,集合,一个老师有很多学生【一对多】

  • 关联了一个外键

SQL语句:

CREATE TABLE `teacher` (`id` INT(10) NOT NULL,`name` VARCHAR(30) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO teacher(`id`, `name`) VALUES (1, 秦老师); CREATE TABLE `student` (`id` INT(10) NOT NULL,`name` VARCHAR(30) DEFAULT NULL,`tid` INT(10) DEFAULT NULL,PRIMARY KEY (`id`),KEY `fktid` (`tid`),CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT 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. 导入lombok

    <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version>
    </dependency>
    
  2. 新建实体类Teacher、student

    package xiaoqi.pojo;import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;@ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public class Student {private int id;private String name;//学生需要关联一个老师private Teacher teacher;}
    
    package xiaoqi.pojo;import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;@Data
    @AllArgsConstructor
    @NoArgsConstructor
    @ToString
    public class Teacher {private int id;private String name;
    }
    
  3. 建立Mapper接口

    package xiaoqi.dao;import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.annotations.Select;
    import xiaoqi.pojo.Teacher;public interface TeacherMapper {@Select("select * from mybatis.teacher where id = #{id}")Teacher getTeacher(@Param("id") int id);
    }
    
  4. 建立Mapper.xml文件

    <?xml version="1.0" encoding="UTF8" ?>
    <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="xiaoqi.dao.TeacherMapper"></mapper>
    
  5. 在核心配置文件中绑定注册我们的Mapper.xml文件

    <mappers><!--mapper不能用通配符 *--><mapper resource="xiaoqi/dao/TeacherMapper.xml"/>
    </mappers>
    
  6. 测试查询

    package xiaoqi.dao;import org.apache.ibatis.session.SqlSession;
    import xiaoqi.pojo.Teacher;
    import xiaoqi.utils.MybatisUtils;public class TTest {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.getSqlSession();TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);Teacher teacher = mapper.getTeacher(1);System.out.println(teacher);sqlSession.close();}
    }
    

复杂的SQL查询

按照查询嵌套处理

<mapper namespace="xiaoqi.dao.StudentMapper"><!--思路:1.查询所有学生的信息2.根据查询出来学生的tid,寻找对应的老师--><select id="getStudent" resultMap="student">select * from Mybatis.student;</select><resultMap id="student" type="Student"><result property="id" column="id"/><result property="name" column="name"/><!--复杂的属性\对象使用 association\集合使用 collection--><association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/></resultMap><select id="getTeacher" resultType="Teacher">select * from mybatis.teacher where id = #{id}</select></mapper>

按照结果嵌套处理

<select id="getStudent2" resultMap="student2">select s.id sid,s.name sname,t.name tnamefrom student s ,teacher twhere s.tid = t.id
</select>
<resultMap id="student2" type="Student"><result property="id" column="sid"/><result property="name" column="sname"/><association property="teacher" javaType="Teacher"><result property="name" column="tname"/></association>
</resultMap>

回顾Mysql 多对一查询方式:

  • 子查询
  • 连表查询

11.一对多

比如:一个老师拥有多个学生,一对多的关系

环境搭建

  1. 导入包

  2. 实体类

    public class Teacher {private int id;private String name;//一个老师拥有多个学生private List<Student> students;
    }
    
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public class Student {private int id;private String name;//学生需要关联一个老师private int tid;
    }
    
  3. 建立Mapper接口

    <!--按结果嵌套-->
    <select id="getTeacher" resultMap="TeacherStudent">select s.id sid,s.name sname, t.name tname, t.id tidfrom mybatis.student s , mybatis.teacher twhere s.tid = t.id and t.id = #{tid}
    </select>
    <resultMap id="TeacherStudent" type="Teacher"><result property="id" column="tid"/><result property="name" column="tname"></result><!--javaType 指定属性的类型集合中的泛型信息,我们使用ofType获取--><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="TeacherStudent2">select * from mybatis.teacher where id = #{tid}</select><!--选出来要查询的老师id,得到查询结果,这个是一个集合所以要写javaType--><resultMap id="TeacherStudent2" type="Teacher"><collection property="students" javaType="ArrayList" ofType="Stundet" select="getStudentByTeacherID" column="id"/></resultMap><select id = "getStudentByTeacherID" resultType="Student">select * from mybatis.student where  tid = #{tid}</select>
    
  4. 在核心配置文件中绑定注册mapper

    <mappers><!--mapper不能用通配符 *--><mapper resource="xiaoqi/dao/TeacherMapper.xml"/><mapper resource="xiaoqi/dao/StudentMapper.xml"/>
    </mappers>
    
  5. 测试

    public class MyTest {@Testpublic void test(){SqlSession sqlSession = MybatisUtils.getSqlSession();TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);Teacher teacher = mapper.getTeacher(1);System.out.println(teacher);sqlSession.close();}
    }
    
Teacher(id=1, name=秦老师, students=[Student(id=1, name=小明, tid=1), Student(id=2, name=小红, tid=1), Student(id=3, name=小张, tid=1), Student(id=4, name=小李, tid=1), Student(id=5, name=小王, tid=1)])

小结

  1. 关联-association【多对一】
  2. 集合-collection【一对多】
  3. javaType ofType
    1. javaType 用来指定属性实体类中的类型
    2. ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型

注意点:

  • 保证sql的可读性,尽量保证通俗易懂
  • 注意一对多和多对一中,属性和字段的问题
  • 如果问题不好排查错误,可以使用日志,建议使用Log4J

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QhHyUYSa-1635337754513)(C:/Users/77/AppData/Roaming/Typora/typora-user-images/image-20211022093756999.png)]

面试高频问题:

  • mysql引擎
  • InnoDB底层原理
  • 索引
  • 索引优化

12.动态SQL

什么是动态SQL:动态SQL就是指根据不同的条件生成不同的SQL语句

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

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

搭建环境

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;

创建一个基础工程:

  1. 导入包

  2. 编写配置文件,注册绑定mapper文件

    <settings><!--标准的日志工厂实现--><setting name="logImpl" value="STDOUT_LOGGING"/><!--是否开启自动驼峰命名股则映射--><setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <mappers><mapper class="xiaoqi.dao.BlogMapper"/>
    </mappers>
    
  3. 编写实体类

    package xiaoqi.pojo;import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;import java.util.Date;@Data
    @AllArgsConstructor
    @NoArgsConstructor
    @ToString
    public class Blog {private String id;private String title;private String author;private Date createtime;private int views;
    }
    
  4. 编写实体类对应的Mapper接口和xml

    package xiaoqi.dao;import lombok.Data;
    import xiaoqi.pojo.Blog;public interface BlogMapper {//插入数据int addBlog(Blog blog);
    }
    
    <?xml version="1.0" encoding="UTF8" ?>
    <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="xiaoqi.dao.BlogMapper"><insert id = "addBlog" parameterType="blog">insert into mybatis.blog (id,title,author,create_time,views)values (#{id},#{title},#{author},#{createtime},#{views});</insert>
    </mapper>
    
  5. 工具类,生成随机的ID

    package xiaoqi.utils;import org.junit.jupiter.api.Test;import java.util.UUID;public class IDutils {public static String getId(){return UUID.randomUUID().toString().replace("-","");}@Testpublic void test(){System.out.println(IDutils.getId());}
    }
    
  6. 测试

    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    import xiaoqi.dao.BlogMapper;
    import xiaoqi.pojo.Blog;
    import xiaoqi.utils.IDutils;
    import xiaoqi.utils.MybatisUtils;import java.util.Date;public class MyTest {@Testpublic void addBlogTest() {SqlSession sqlSession = MybatisUtils.getSqlSession();BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);Blog blog = new Blog();blog.setId(IDutils.getId());blog.setTitle("Mybatis");blog.setAuthor("狂神说");blog.setCreatetime(new Date());blog.setViews(9999);mapper.addBlog(blog);blog.setId(IDutils.getId());blog.setTitle("Java");mapper.addBlog(blog);blog.setId(IDutils.getId());blog.setTitle("Spring");mapper.addBlog(blog);blog.setId(IDutils.getId());blog.setTitle("微服务");mapper.addBlog(blog);sqlSession.close();}}
    

IF

<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 != author">and author = #{author}</if>
</select>
@Test
public void queryBlogIF(){SqlSession sqlSession = MybatisUtils.getSqlSession();BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);HashMap map = new HashMap();map.put("title","Java");List<Blog> blogs = mapper.queryBlogIF(map);for (Blog blog : blogs) {System.out.println(blog);}sqlSession.close();}

choose、when、otherwise

<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

<select id="queryBlogIF" parameterType="map" resultType="blog">select * from mybatis.blog<where><if test ="title != null">and title = #{title}</if><if test="author != author">and author = #{author}</if></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>

trim的用法

< trim prefix="(" prefixOverrides="OR" suffixOverrides="," suffix=")">子句</trim>

这里的子句会对其进行trim()处理,忽略掉换行、空格等字符,所以本文中的子句都是指trim()处理后的字符串。如果子句为空,那么整个< trim>块不起作用,相当于不存在。本< trim>块的作用就是:去掉子句首的OR和子句尾的逗号,并在子句前后分别加上(和),比如,“orabc,”–>"(abc)"。

  • prefixOverrides:子句首的命中词列表,以|分隔,忽略大小写。如果命中(轮询命中词,最多只命中一次),会删除子句首命中的词;没命中就算了。
  • prefix:删除子句句首后,在子句最前边加上单个空格+prefix。
  • suffixOverrides:子句尾的命中词列表,以|分隔,忽略大小写。如果命中(轮询命中词,最多只命中一次),会删除子句尾命中的词;没命中就算了。
  • suffix:删除子句句尾后,在子句最后边加上单个空格+suffix。

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

SQL片段

有的时候,我们可能会将一些公共的部分抽取出来方便复用

  1. 使用sql标签抽取公共的部分

    <sql id = "if-title-author"><if test="title !=null">title = #{title}</if><if test="author != null">author = #{author}</if>
    </sql>
    
  2. 在需要的地方使用include标签引用即可

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

注意事项:

  1. 最好基于单表来定义SQL片段
  2. 不要存在where标签

Foreach

select * from mybatis.user where 1 =1 and
<foreach item = "id" collection = "ids" open = "(" sperator = "or" close = ")">#{id}
</foreach>
(id = 1 or id =2 or id = 3)
<!--我们现在传递一个万能的map,这个map中可以存在一个集合-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">select *from mybatis.blog<where><foreach collection="ids" item = "id" open="(" close=")" separator="or">id = #{id}</foreach></where>
</select>
public void queryBlogForEach(){SqlSession sqlSession = MybatisUtils.getSqlSession();BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);HashMap map = new HashMap<>();ArrayList<Integer> ids = new ArrayList<Integer>();ids.add(1);ids.add(2);map.put("ids",ids);List<Blog> blogs = mapper.queryBlogForeach(map);for (Blog blog : blogs) {System.out.println(blog);}mapper.queryBlogForeach(map);sqlSession.close();
}

动态sql就是在拼接sql语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了

建议:

  • 先在Mysql中写出完整的SQL,在对一个去修改成为我们的动态SQL实现通用即可

13.缓存

简介

查询:连接数据,耗资源!
一次查询的结果,给他暂存在一个可以取到的地方! ---> 内存 : 缓存我们再次查询相同数据的时候,直接走缓存,就不用走数据库了。

查询

1.什么是缓存[Cache]?

  • 存在内中的临时数据
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题

2.为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率

3.什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据【可以使用缓存】

Mybatis缓存

Mybatis包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存。缓存可以极大的提升查询效率。

Mybatis系统默认定义了两级缓存:一级缓存和二级缓存

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

一级缓存

一级缓存也叫本地缓存:SqlSession

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

测试步骤:

  1. 开启日志

  2. 测试在一个Session中查询两次相同记录!

    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    import xiaoqi.dao.UserMapper;
    import xiaoqi.pojo.User;
    import xiaoqi.utils.MybatisUtils;public class MyTest {@Testpublic void test(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = mapper.queryUser(1);System.out.println(user);System.out.println("=============");User user1 = mapper.queryUser(1);System.out.println(user1);System.out.println(user == user1);sqlSession.close();}
    }
    
  3. 查看日志输出

缓存失效的情况:

  1. 查询不同的东西

    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    import xiaoqi.dao.UserMapper;
    import xiaoqi.pojo.User;
    import xiaoqi.utils.MybatisUtils;public class MyTest {@Testpublic void test(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = mapper.queryUser(1);System.out.println(user);System.out.println("=============");User user1 = mapper.queryUser(2);System.out.println(user1);System.out.println(user == user1);sqlSession.close();}
    }
    

  2. 增删改操作,可能会改变原来的数据,所以必定会刷新缓存

    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    import xiaoqi.dao.UserMapper;
    import xiaoqi.pojo.User;
    import xiaoqi.utils.MybatisUtils;public class MyTest {@Testpublic void test(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = mapper.queryUser(1);System.out.println(user);mapper.UpdateUser(new User(2,"asas","12345678"));System.out.println("=============");User user1 = mapper.queryUser(1);System.out.println(user1);System.out.println(user == user1);sqlSession.close();}
    }
    

  3. 查询不同的Mapper

  4. 手动清理缓存

    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    import xiaoqi.dao.UserMapper;
    import xiaoqi.pojo.User;
    import xiaoqi.utils.MybatisUtils;public class MyTest {@Testpublic void test(){SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = mapper.queryUser(1);System.out.println(user);sqlSession.clearCache();System.out.println("=============");User user1 = mapper.queryUser(1);System.out.println(user1);System.out.println(user == user1);sqlSession.close();}
    }
    

小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个阶段有效

一级缓存就是一个map

二级缓存

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

步骤:

  1. 开启全局缓存

    <!--开启全局缓存-->
    <setting name="cacheEnable" value="true"/>
    
    cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
  2. 在要使用二级缓存的Mapper中开启

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

    也可以自定义参数

  3. 测试

    1. 我们需要将实体类序列化!否则就会报错 Cause: java.io.NotSerializableException: xiaoqi.pojo.User
    public class MyTest {@Testpublic void test(){SqlSession sqlSession = MybatisUtils.getSqlSession();SqlSession sqlSession1 = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = mapper.queryUser(1);System.out.println(user);sqlSession.close();//关掉第一个sqlsession才会将一级缓存中的数据保存到二级缓存中UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);User user1 = mapper1.queryUser(1);System.out.println(user1);System.out.println(user == user1);sqlSession1.close();}
    }
    

小结:

  1. 只要开启了二级缓存,只在同一个Mapper下有效
  2. 所有的数据都会先放在一级缓存中;只有当会话提交或者关闭的时候,才会提交到二级缓存中

缓存原理

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import xiaoqi.dao.UserMapper;
import xiaoqi.pojo.User;
import xiaoqi.utils.MybatisUtils;public class MyTest {@Testpublic void test(){SqlSession sqlSession = MybatisUtils.getSqlSession();SqlSession sqlSession1 = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = mapper.queryUser(1);System.out.println(user);sqlSession.close();System.out.println("==============");UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);User user1 = mapper1.queryUser(1);User user2 = mapper1.queryUser(2);System.out.println(user1);System.out.println(user2);System.out.println("=======");User user3 = mapper1.queryUser(2);System.out.println(user3);sqlSession1.close();}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UDfhOJNj-1635337754523)(C:/Users/77/AppData/Roaming/Typora/typora-user-images/image-20211027194122230.png)]

自定义缓存-ehcache

Ehcache是一种广泛使用的开源Java分布式缓存,主要面向通用缓存

要在程序中使用ehcache:

  • 导入包
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-ehcache</artifactId><version>1.1.0</version>
</dependency>
  • ehcache的配置文件
<?xml version="1.0" encoding="UTF8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"updateCheck="false">
<!--diskStore :为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置,参数解释如下:user.hoeme - 用户主目录user.dir -  用户当前工作目录javaio.tmpdir - 默认临时文件
--><diskStore path="./tmpdir/Tmp_EhCache/"><defaultCacheeternal="false"maxElementsInMemory="10000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="1800"timeToLiveSeconds="259200"memoryStoreEvictionPolicy="LRU"/><cachename="cloud_user"eternal="false"maxElementsInMemory="5000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="1800"timeToLiveSeconds="1800"memoryStoreEvictionPolicy="LRU"/></ehcache><!--defaultCache:默认缓存策略,当cache找不到定义的缓存时,则使用这个缓存策略,只能定义一个--><!--name:缓存名称。maxElementsInMemory:缓存最大个数。eternal:对象是否永久有效,一但设置了,timeout将不起作用。timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。maxElementsOnDisk:硬盘最大缓存个数。diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。clearOnFlush:内存数量最大时是否清除。-->
  • 使用ehchache
<!--自定义缓存,用第三方缓存覆盖-->
<cache type = "org.mybatis.caches.ehcache.EhcacheCache"/>

redis做缓存

https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->

org.mybatis.caches
mybatis-ehcache
1.1.0


- ehcache的配置文件```xml
<?xml version="1.0" encoding="UTF8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"updateCheck="false">
<!--diskStore :为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置,参数解释如下:user.hoeme - 用户主目录user.dir -  用户当前工作目录javaio.tmpdir - 默认临时文件
--><diskStore path="./tmpdir/Tmp_EhCache/"><defaultCacheeternal="false"maxElementsInMemory="10000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="1800"timeToLiveSeconds="259200"memoryStoreEvictionPolicy="LRU"/><cachename="cloud_user"eternal="false"maxElementsInMemory="5000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="1800"timeToLiveSeconds="1800"memoryStoreEvictionPolicy="LRU"/></ehcache><!--defaultCache:默认缓存策略,当cache找不到定义的缓存时,则使用这个缓存策略,只能定义一个--><!--name:缓存名称。maxElementsInMemory:缓存最大个数。eternal:对象是否永久有效,一但设置了,timeout将不起作用。timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。maxElementsOnDisk:硬盘最大缓存个数。diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。clearOnFlush:内存数量最大时是否清除。-->
  • 使用ehchache
<!--自定义缓存,用第三方缓存覆盖-->
<cache type = "org.mybatis.caches.ehcache.EhcacheCache"/>

redis做缓存

狂神说 | Mybatis完整版笔记相关推荐

  1. Mybatis完整版(二)

    Mybatis完整版(二) 四.[配置解析(优化)](https://mybatis.org/mybatis-3/zh/configuration.html) 1.核心配置文件 2.[环境配置(env ...

  2. 李宏毅《机器学习》完整版笔记发布

    大数据文摘出品 编译:宁静 提到李宏毅老师,熟悉机器学习的读者应该都知道,他的机器学习视频被奉为领域内的经典视频之一,对于想要入门机器学习又想看中文讲解的人来说绝对是非常推荐的.而现在我们有更加完备的 ...

  3. 在线笔记存代码_李宏毅《机器学习》完整版笔记发布

    大数据文摘出品 编译:宁静 提到李宏毅老师,熟悉机器学习的读者应该都知道,他的机器学习视频被奉为领域内的经典视频之一,对于想要入门机器学习又想看中文讲解的人来说绝对是非常推荐的.而现在我们有更加完备的 ...

  4. 2020周阳SpringCloud完整版笔记--一

    微服务架构入门 微服务 的概念最早产生于Martin Fowler在2014年的一篇论文中. 微服务架构是一种架构模式,他提倡将单一应用程序划分成一组小的服务,服务与服务之间互相协调.相互配合,为用户 ...

  5. java笔记从基础到高级完整版

    Java概述 java完整版笔记 ​ Java是跨平台的编程语言,一次编写,到处运行.Java程序是 运行在jvm虚拟机上,不同的操作系统安装jvm就可以运行Java程序. Java开发环境配置 1. ...

  6. 野火洋桃STM32开发版学习指导完整版

    该文章是我历时一个月整理总结而成,专门针对想要通过野火&洋桃STM32开发板入门stm32的读者.由于csdn编辑限制,该学习指导只包含文字信息.如需查看含图片的完整版可进入我的博客下载页.完 ...

  7. CSS学习笔记(狂神-完整版)

    CSS学习笔记(狂神-完整版) HTML + CSS + JavaScript 结构 + 表现 + 动作 1.什么是CSS 如何学习 CSS是什么 CSS怎么用(快速入门) CSS选择器(重点+难点) ...

  8. 狂神Redis学习记录 完整版

    [狂神说Java]Redis最新超详细版教程通俗易懂_哔哩哔哩_bilibili笔记资料交流都在我们的平台:www.kuangstudy.com秦疆老师Java全栈系列课程之Redis讲解从Nosql ...

  9. 狂神说-Mybatis笔记(总)

    环境: JDK1.8 MySQL 8.0.23 maven 3.6.1 IDEA2020.3 框架:需要配置文件 官方中文文档:https://mybatis.org/mybatis-3/zh/ind ...

最新文章

  1. python 模板匹配
  2. android studio 导入一个已有的android studio project作为lib使用
  3. 2016-05-12 SAP UI5事件注册的问题研究
  4. linux 权限提示信息,命令行快速提示:权限进阶 | Linux 中国
  5. Fiddler快速入门
  6. React-router 4 按需加载的实现方式及原理(Code Splitting)
  7. oracle12C 创建用户学习
  8. L2-006 树的遍历 (25 point(s))
  9. java通过JNI调用DLL文件
  10. 2009年计算机一级考试真题及答案
  11. centos/linux 服务器的内存不够了怎么办?centos用虚拟内存扩展内存
  12. 计算机和用户账户名一样,求计算机账户与用户账户的区别与联系?
  13. 你知道啥是ROC吗?
  14. 一杯清茶!细品人生!
  15. linux编译内核卡住,kernel启动卡住
  16. Pytorch - Illegal instruction
  17. 韩国社交软件Kakao Talk要开网络银行,社交软件+银行的模式会怎么转?
  18. android twrp_如何将TWRP恢复环境刷新到Android手机
  19. zynq IRQ_P2F ----ps中断pl
  20. 中级职称软考设计师笔记之【多媒体基础】

热门文章

  1. 根据年份和月份计算当前月有多少天?
  2. Oracle计算两个日期的月份
  3. 你不知道的3D电影7大危害
  4. 怎么登陆163邮箱?163的邮箱有哪些实用技巧?
  5. 【进阶技术】一篇文章搞掂:RibbitMQ
  6. 推荐10部最有影响力的韩剧,如未上榜的请留言下期推荐
  7. java框体怎么添加背景图,文本框添加背景图片,文本框背景图片,public class
  8. 【STC8H8K64U】开天斧3.1学习笔记——LED闪烁
  9. 银河麒麟V10服务器系统安装教程及注意事项
  10. sigmoid函数温习【函数曲线可视化与导函数曲线可视化】