Mybatis框架全面详解

2023-11-10

MyBatis的基本使用

第一章:MyBatis的概念

MyBtais网址: https://mybatis.net.cn/

什么是MyBatis?

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

ORM(Object Relationship Mapping)对象关系映射的概念介绍:

  • 对象: Java的实体类对象
  • 关系:关系型数据库
  • 映射:二者之间的对应关系
Java概念 数据库概念
属性 字段
对象 记录

在这里插入图片描述

与其它持久化层技术对比

  • JDBC:
    1. SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
    2. 维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
    3. 代码冗长,开发效率低
  • Hibernate 和 JPA
    1. 操作简便,开发效率高
    2. 程序中的长难复杂 SQL 需要绕过框架
    3. 内部自动生产的 SQL,不容易做特殊优化
    4. 基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
    5. 反射操作太多,导致数据库性能下降
  • MyBatis
    1. 轻量级,性能出色
    2. SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
    3. 开发效率稍逊于HIbernate,但是完全能够接收

其他概念:

持久化的概念:对象的瞬时态和持久态相互转化。
瞬时转为持久的操作: 增加 insert;修改 update;删除 delete。
持久转瞬时的操作:查询 select。

序列化和反序列化的概念
序列化把对象的状态转化为可存储的或可传输的状态(二进制字节流)
反序列化把可存储的或可传输的状态转化为对象的状态。

第二章: MyBtais的基本使用

2.1 环境的搭建

2.1.1 物理建模

CREATE TABLE book (
	`bookid` INT (6),
	`bookname` VARCHAR (300),
	`price` FLOAT ,
	`sortname` VARCHAR (150)
); 
INSERT INTO `book` (`bookid`, `bookname`, `price`, `sortname`) VALUES('000001','Java实战',56,'技术开发');
INSERT INTO `book` (`bookid`, `bookname`, `price`, `sortname`) VALUES('000002','JavaWeb入门到提高',66,'开发');
INSERT INTO `book` (`bookid`, `bookname`, `price`, `sortname`) VALUES('000003','MySQL实战',64,'数据库');
INSERT INTO `book` (`bookid`, `bookname`, `price`, `sortname`) VALUES('000004','Mybatis实践开发',54,'框架');

2.1.2 逻辑建模

①:首先创建一个Maven module
在这里插入图片描述
②:创建Java实体类

注意:Java的实体类中,属性的类型不要使用基本数据类型,要使用包装类型。因为包装类型可以赋值为null,表示空,而基本数据类型不可以。

@Data                 // 这个是自动生成get、set等方法
@AllArgsConstructor	  // 创建有参构造器(所有参数)
@NoArgsConstructor    // 创建无参构造器
public class Book {
    private Integer bookId;
    private String bookName;
    private Float price;
    private String sortName;
}

③:创建MyBatis的工具类

public class MyBatisUtil {
    private static SqlSessionFactory sqlSessionFactory;
    // 声明Mybatis全局配置文件的路径
    private final static String conf = "mybatis-conf.xml";
    static {
        try {
            // 创建SqlSessionFactory对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            // 以输入流的形式加载Mybatis配置文件,并且创建创建SqlSessionFactory对象
            InputStream is = Resources.getResourceAsStream(conf);
            sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
	// 使用SqlSessionFactory对象开启一个会话
    public static SqlSession getSqlSession(boolean flag){
        return sqlSessionFactory.openSession();
    }

    // 自动开启事务
    public static SqlSession getSqlSession(){
        return getSqlSession(true);
    }
	// 关闭事务
    public static void close(SqlSession session){
        if(session != null){
        	// 关闭SqlSession
            session.close();
        }
    }
}

2.1.3 搭建框架开发环境

要配置的环境位置如下图所示:
在各位置

① :导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>demo02</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- MySql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>
        <!-- MyBatis核心包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!-- 代码生成器-- 自动生成get和set方法 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
        <!-- junit框架 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
        <!-- 日志框架 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
    </dependencies>
</project>

2.1.3.1 junit框架

导入依赖后就可以直接使用。

常用注解 作用
@Test 此注解可以作为一个测试用例
@Before 在调用每一个测试用例前,先执行此注解标记的方法
@After 在调用完每一个测试用例后,都会执行此注解标记的方法
@BeforeClass 所有测试用例调用前执行一次,在测试类没有实例化之前就已被加载,需用static修饰;
@AfterClass 所有测试用例调用后执行一次,在测试类没有实例化之前就已被加载,需用static修饰;
@lgnore 暂不执行该方法;

2.1.3.2 log4j(日志)框架

首先要导入依赖,然后需要在MyBatis-conf.xml文件中配置。

配置日志的类型有:
SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING

配置格式如下。注意: setting的name是固定的,value只能取上面的几种类型。

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

②:创建连接mysql的文件

url=jdbc:mysql://127.0.0.1:3306/数据库名?serverTimezone=GMT
driver=com.mysql.cj.jdbc.Driver
username=数据库账号名
password=数据库的密码

③:MyBatis的全局配置文件

<?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>
    <!-- 加载db.properties文件 -->
    <properties resource="db.properties"/>
    <settings>
    	<!-- 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
    <environments default="development">
    <!-- environment表示配置Mybatis的一个具体的环境 -->
    <!-- 这样可以配置多个environment的环境,但environments的default值要与其中的一个environment的id值要相对应-->
        <environment id="development">
         <!-- Mybatis的内置的事务管理器 -->
            <transactionManager type="JDBC"/>
            <!-- 配置数据源 -->
            <dataSource type="POOLED">
               <!-- 建立数据库连接的具体信息 -->
               <!-- 这里的value值与db.properties的键相对应-->
               <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注册:指定Mybatis映射文件的具体位置 -->
        <!-- mapper标签:配置一个具体的Mapper映射文件 -->
        <!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
        <!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
        <mapper resource="com/wdzl/mapper/BookMapper.xml"/>
    </mappers>
</configuration>

④:创建BookMapper.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属性:在Mybatis全局范围内找到一个具体的Mapper配置 -->
<!-- 引入接口后,为了方便通过接口全类名来找到Mapper配置文件,所以通常将namespace属性设置为接口全类名 -->
<mapper namespace="com.wdzl.dao.IBookDao">
	<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
    <!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
    <select id="queryAll" resultType="com.wdzl.pojo.Book">
        select bookid,bookname,price,sortname from book
    </select>
</mapper>

注意:BookMapper.xml所在的目录要和mybatis-conf.xml中使用mapper标签配置的一致。

2.1.4 测试

①:编写dao接口

public interface IBookDao {
    /**
     * 查询所有图书
     * @return
     */
    public List<Book> queryAll();
    /**
     * 按条件查询图书
     */
    public Book queryById(Integer bookId);
}

②:编写测试方法进行测试

	// 查询所有图书
	@Test
    public void select(){   	
    	// 这个MyBatisUtil是上面写的的工具类
        SqlSession session = MyBatisUtil.getSqlSession();
        // 根据BookMapper接口的Class对象获取Mapper接口类型的对象
        IBookDao mapper = session.getMapper(IBookDao.class);
        // 调用BookMapper接口的方法完成对数据库的操作
        List<Book> list = mapper.queryAll();
        for (Book book : list) {
            System.out.println(book);
        }
        MyBatisUtil.close(session);
    }
    // 按条件查询图书
    @Test
    public void selectById(){
        SqlSession session = MyBatisUtil.getSqlSession();
        IBookDao mapper = session.getMapper(IBookDao.class);
        Book book = mapper.queryById(1);
        System.out.println(book);
        MyBatisUtil.close(session);
    }

2.1.5 注意

MyBatis框架会在初始化时将XML文件读取进来,封装到对象中,再然后就都是Java代码的执行了,XML中的配置是没法执行的。

2.2 #{}与${}

2.2.1. #{}

Mybatis会在运行过程中,把配置文件中的SQL语句里面的#{}转换为“?”占位符,发送给数据库执行。防止了注入安全或特殊字符问题。

配置文件中的SQL:

 <select id="queryById" resultType="com.wdzl.pojo.Book">
         select bookid,bookname,price,sortname from book where bookid = #{bookid}
 </select>

实际执行的Sql语句:

select bookid,bookname,price,sortname from book where bookid = ?

2.2.2 ${}

将来会根据${}拼字符串

使用场景:在SQL语句中,数据库表的表名不确定,需要外部动态传入,此时不能使用#{},因为数据库不允许表名位置使用问号占位符,此时只能使用${}。

配置文件中的SQL:

 <select id="queryByName" resultType="com.wdzl.pojo.Book">
         select bookid,bookname,price,sortname from book where bookname = '${bookName}'
 </select>

实际执行的Sql语句:

select bookid,bookname,price,sortname from book where bookname = 'Java实战'

注意: 如果使用${}容易发生sql注入问题。因此只要能用#{}就不用 $ {}。

2.3 数据的输入

概念: 指在调用Dao层的接口时,数据传入的形式。

  • 简单类型:只包含一个值的数据类型
    ①:基本数据类型:int、byte、short、double、……
    ②:基本数据类型的包装类型:Integer、Character、Double、……
    ③:字符串类型:String
  • 复杂类型:包含多个值的数据类型
    ①:实体类类型:自定义类(Book)
    ②:集合类型:List、Set、Map、……
    ③:数组类型:int[]、String[]、……

2.3.1 单个简单类型参数

dao层中接口的方法:

public Book queryById(Integer bookId);

注意: 如果是传单个参数不需要指定parameterType,并且#{}中的名字可以任意,也不区分大小写。
SQL 语句:

<select id="queryByName" resultType="com.wdzl.pojo.Book">
         select bookid,bookname,price,sortname from book where bookname = #{bookName}
</select>

2.3.2 实体类型参数

dao层中接口的方法:

public void insertBook(Book book);

注意: 如果是传实体类型参数需要指定parameterType,并且#{}中的名字需要与实体类中的属性保持一致,不然会报错。
SQL 语句:

    <!-- 现在在这条SQL语句中,#{}中的表达式需要被用来从Book book实体类中获取bookid值、bookname、price、sortname的值 -->
    <!-- 而我们从实体类中获取值通常都是调用getXxx()方法 -->
    <!-- 而getXxx()方法、setXxx()方法定义了实体类的属性 -->
    <!-- 定义属性的规则是:把get、set去掉,剩下部分的首字母小写,若后面还有单词则需要大写 -->
    <!-- 所以我们在#{}中使用getXxx()方法、setXxx()方法定义的属性名即可 -->
<insert id="insertBook" parameterType="com.wdzl.pojo.Book" >
        insert into book (bookid,bookname,price,sortname) values (#{bookId},#{bookName},#{price},#{sortName})
</insert>

结论:Mybatis会根据#{}中传入的数据,加工成getXxx()方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到#{}这个位置。

2.3.3 零散的简单类型数据

dao层中接口的方法:

 public List<Book> queryBook(@Param("bookName") String bookName,@Param("price") Float price);

SQL 语句:

<select id="queryBook" resultType="com.wdzl.pojo.Book">
         select bookid,bookname,price,sortname from book where bookname like #{bookName} and price > #{price}
</select>

2.3.4 Map 类型参数

使用场景: 有很多零散的参数需要传递,但是没有对应的实体类类型可以使用。使用@Param注解一个一个传入又太麻烦了。所以都封装到Map中。

dao层中接口的方法:

public void updatePrice(Map<String,Object> paramMap);

SQL 语句:
#{}中写Map的key名。

<update id="updatePrice">
        update book set price = #{price} where bookid = #{bookId}
</update>

2.4 数据的输出

2.4.1 返回单个简单类型数据

dao层中接口的方法:

public Integer totalNum();

SQL语句:

<!-- 注意:必须要写resultType返回的类型 -->
<select id="totalNum" resultType="Integer">
        select count(*) from book
</select>

2.4.2 返回实体类对象

dao层中接口的方法:

public Book queryByName(String bookName);

SQL语句:

<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
<!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
<select id="queryByName" resultType="com.wdzl.pojo.Book">
<!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符 -->
<!-- 给每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->
         select bookid,bookname,price,sortname from book where bookname = '${bookName}'
</select>

2.4.3 返回Map类型

适用于SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中。能够封装成实体类类型的,就不使用Map类型。
dao层中接口的方法:

public Map<String,Object> queryTopPrice();

SQL语句:

<!-- 注意:这里的resultType只能写map,否则会报错 -->
<select id="queryTopPrice" resultType="map">
        select bookname,price from book where price = (select max(price) from book)
</select>

2.4.4 返回List类型

查询结果返回多个实体类对象,希望把多个实体类对象放在List集合中返回。此时不需要任何特殊处理,在resultType属性中还是设置实体类类型即可。

dao层中接口的方法:

public List<Book> queryAll();

SQL语句:

<select id="queryAll" resultType="com.wdzl.pojo.Book">
        select bookid,bookname,price,sortname from book
</select>

2.4.5 返回自增主键

dao层中接口的方法:

public Integer insertBook(Book book);

SQL语句:

<!-- useGeneratedKeys属性字面意思就是“使用生成的主键” -->
<!-- keyProperty属性可以指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性 -->
<!-- 这里parameterType写不写都不影响,但是这里的#{}里面的名字要与实体类的属性保持一致 -->
<insert keyProperty="bookId" useGeneratedKeys="true" id="insertBook" parameterType="com.wdzl.pojo.Book">
        insert into book (bookid,bookname,price,sortname) values (#{bookId},#{bookName},#{price},#{sortName})
</insert>

注意 Mybatis是将自增主键的值设置到实体类对象中,而不是以Mapper接口方法返回值的形式返回。

不支持自增主键的数据库
例如Oracle不支持自增型主键,则就要使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用。

<insert id="insertEmployee" 
        parameterType="com.wdzl.pojo.Book"  
            databaseId="oracle">
        <selectKey order="BEFORE" keyProperty="bookid" 
                                       resultType="Integer">
            select book_seq.nextval from dual 
        </selectKey>    
        insert into orcl_employee(id,last_name,email,gender) values(#{id},#{lastName},#{email},#{gender})
</insert>

2.4.6 数据库表字段和实体类属性对应关系

①:设置别名的方式----将字段的别名设置成和实体类属性一致。

注意:这里并未传入实体类,所以这里数据库字段名和实体类的属性名不区分大小写,我这里只是作为一个演示,如果传入的是实体类,别名必须要与实体类的属性一样,也要注意大小写。

<select id="queryAll" resultType="com.wdzl.pojo.Book">
        select bookid bookId,bookname bookName,price,sortname sortName from book
</select>

②:设置自动识别驼峰式命名规则

在MyBatis.xml中配置,下面一节我会给大家讲解settings中的配置

<!-- 在MyBatis-conf.xml内对Mybatis进行配置 -->
<settings>
    <!-- 具体配置 -->
    <!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则,开启后就不能使用别名的方式了 -->
    <!-- 规则要求数据库表字段命名方式:单词_单词 -->
    <!-- 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名 -->
    <!-- 将xxx_xxx这样的列名自动映射到xxXxx这样驼峰式命名的属性名 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

注意: 设置自动识别驼峰式命名规则后,就不能使用别名了。

<!--  public Book queryById(Integer bookId); -->
<select id="queryAll" resultType="com.wdzl.pojo.Book">
        select book_id,book_name,price,sort_name from book where book_id = #{bookId}
</select>

③:使用resultMap标签

<!-- 专门声明一个resultMap设定column到property之间的对应关系 -->
<!-- type类型是针对哪个实体类做映射-->
<!-- 
autoMapping="true":自动映射
如果出现属性名和字段名一致的情况下,不用显式的使用<result>做映射配置的。
但是如果取值为false,则必须使用<result>做映射,否则为null
 -->
<resultMap id="selectBookByResultMap" type="com.wdzl.pojo.Book" autoMapping="true">
    
    <!-- 使用id标签设置主键列和主键属性之间的对应关系 -->
    <!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 -->
    <id column="bookid" property="bookId"/>
    <!-- 使用result标签设置普通字段和Java实体类属性之间的关系 -->
    <result column="bookname" property="bookName"/>
    <result column="price" property="price"/>
    <result column="sortname " property="sortName"/>
</resultMap>
    
<!-- public Book queryByName(String bookName); -->
<select id="queryByName" resultMap="selectBookByResultMap">
         select bookid,bookname,price,sortname from book where bookname = '${bookName}'
</select>

2.4.7 总结

  • 接口中的方法名和SQL的id一致
  • 方法返回值和resultType一致
  • 方法的参数和SQL的参数一致
  • 接口的全类名和映射配置文件的名称空间一致

2.5 MyBatis-conf.xml的常用全局配置

配置文档的顶层结构:
在这里插入图片描述
注意:配置时需要注意这些的顺序,要不然会报错。

2.5.1 properties(属性)

用来配置参数信息,例如数据库的连接信息。当然,我们可以将这些参数信息单独的放在properties 文件中,然后使用properties的resource属性将properties 文件引入进来,然后在xml 配置文件中用${}引用就可以了,引入的目的是为了放在在xml配置文件中写死参数。
<properties resource="db.properties"/>

2.5.2 settings(设置)

设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true / false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true / false false
useGeneratedKeys 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 true / false false
defaultStatementTimeout 设置超时时间,它决定数据库驱动等待数据库响应的秒数 任意正整数 未设置 (null)
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true / false false
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J / LOG4J / LOG4J2 / JDK_LOGGING / COMMONS_LOGGING / STDOUT_LOGGING / NO_LOGGING 未设置 (null)

经常使用到的如上所示,如果还想使用其他的属性的话,你可以参考官方文档

2.5.3 typeAliases(类型别名)

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

第一种配置方法:

<!-- 会扫描com.wdzl.pojo整个包下的需要的Java Bean-->
<typeAliases>
        <package name="com.wdzl.pojo"/>
</typeAliases>

第二种配置方法

<typeAliases>
        <typeAlias type="com.wdzl.pojo.Book" alias="book"/>
</typeAliases>

第三种配置方法(注解)

@Alias("book")
public class Book{
    ...
}

关于别名的使用:
配置好别名后,resultType、parameterType等可以直接写别名。

<select id="queryAll" resultType="Book">
        select bookid,bookname,price,sortname from book
</select>

2.5.4 typeHandlers(类型处理器)

数据库中的字段类型与Java属性的类型不是对应的,因此我们要把Java对象的属性值转化为数据库中的字段值,数据库中的字段值转化为Java对象的属性值,这时候就需要到类型处理器
后面会给大家详细介绍并且使用类型处理器,这里只需要知道这个概念就行。

2.5.5 plugins(插件)

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可

<plugins>
<!-- 分页插件 -->
       <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

2.5.6 environments(环境配置)

MyBatis 可以配置成适应多种环境,例如开发环境、测试环境、生产环境。可以在不同的环境中使用不同的数据库地址或者类型。

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

2.5.7 mappers(映射器)

< mappers >标签配置的是我们的映射器,也就是Mapper.xml 的路径。这里配置的目的是让MyBatis 在启动的时候去扫描这些映射器,创建映射关系。

经常使用的是第一种,因此对于剩下的三种也没有进行实践操作。

<!-- 我们有四种指定Mapper 文件的方式 -->
<!-- 使用相对于类路径的资源引用 -->
<mappers>
        <mapper resource="com/zhang/pojo/BookMapper.xml"/>
        <mapper resource="com/zhang/pojo/AuthorMapper.xml"/>
</mappers>

<!-- 将包内的映射器接口实现全部注册为映射器 -->
<!-- 使用这种方式注意以下几点 -->
<!-- Dao接口和Mapper配置文件名称一致 -->
<!-- Mapper配置文件放在resources路径下 -->
<mappers>
  <!-- 我这里的Dao层接口与Mapper配置文件名称不一致,所以这个用法是错误的
  这里给大家提醒一下 -->
  <package name="com/zhang/pojo"/>
</mappers>

<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>

第三章: 关联关系

关联关系在数量上的体现: 一对一,一对多,多对多。
关联关系的方向: 双向–>双方都可以访问到对方;单向–>双方中只有一方能够访问到对方。

创建模型:
①:创建数据库表并插入数据

create table author(
	author_id int primary key auto_increment,
	author_name varchar(11),
	author_address varchar(20),
	author_sex int default 1
)

insert into author (author_name,author_address,author_sex) values("老李","北京",1);
insert into author (author_name,author_address,author_sex) values("老张","上海",2);
insert into author (author_name,author_address,author_sex) values("老六","杭州",2);


create table book(
	book_id int primary key auto_increment,
	book_name varchar(11),
	book_price float,
	author_id int,
	foreign key(author_id) references author(author_id)
)

insert into book(book_name,book_price,author_id) values("Java入门",45,2);
insert into book(book_name,book_price,author_id) values("Spring入门",55,1);
insert into book(book_name,book_price,author_id) values("Mybatis入门",75,3);
insert into book(book_name,book_price,author_id) values("C++入门",45,2);
insert into book(book_name,book_price,author_id) values("SpringMVC入门",45,2);

②: 创建实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Author {
    private Integer authorId;
    private String authorName;
    private String authorAddress;
    private Set<Book> authorBooks;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
    private Integer bookId;
    private String bookName;
    private Float bookPrice;
    private String bookSortName;
    private Author author;
}

3.1 对一关系的映射

3.1.1 一对一

dao层中接口的方法

public Book queryOne(Integer bookId);

映射文件中的SQL语句

<!-- 创建resultMap实现“对一”关联关系映射 -->
<!-- id属性:通常设置为这个resultMap所服务的那条SQL语句的id加上“ResultMap” -->
<!-- type属性:要设置为这个resultMap所服务的那条SQL语句最终要返回的类型 -->
<resultMap id="bookMap" type="com.wdzl.pojo.Book" autoMapping="true">
		<!-- 先设置Book自身属性和字段的对应关系 -->
        <id column="book_id" property="bookId"/>
        <result column="book_name" property="bookName"/>
        <result column="book_price" property="bookPrice"/>
        <!-- 使用association标签配置“对一”关联关系 -->
    	<!-- property属性:对应类中的属性 -->
    	<!-- column属性:数据库中的字段 -->
    	<!-- javaType属性:对应类的全类名 -->
    	<!-- 多对一关联关系中,autoMapping默认为false-->
        <association property="author" autoMapping="true">
        <!-- 配置Author类的属性和字段名之间的对应关系 -->
            <id column="author_id" property="authorId"/>
            <result column="author_name" property="authorName"/>
            <result column="author_address" property="authorAddress"/>
            <result column="author_sex" property="authorSex"/>
        </association>
</resultMap>

<select id="queryOne" resultMap="bookMap">
        select book_id,book_name,book_price,b.author_id,author_name,author_address,author_sex from book b,author a
        where book_id = #{bookId} and a.author_id = b.author_id
</select>

3.1.2 多对一

dao层中接口的方法

public List<Book> queryAll();

映射文件中的SQL语句

<resultMap id="bookMap" type="com.wdzl.pojo.Book" autoMapping="true">
        <id column="book_id" property="bookId"/>
        <result column="book_name" property="bookName"/>
        <result column="book_price" property="bookPrice"/>
        <association property="author" autoMapping="true">
            <id column="author_id" property="authorId"/>
            <result column="author_name" property="authorName"/>
            <result column="author_address" property="authorAddress"/>
            <result column="author_sex" property="authorSex"/>
        </association>
</resultMap>

<select id="queryAll" resultMap="bookMap">
        select book_id,book_name,book_price,b.author_id,author_name,author_address,author_sex from book b,author a
        where a.author_id = b.author_id
</select>

3.2 对多关系的映射

dao层中接口的方法

public List<Author> queryAll();

映射文件中的SQL语句

<resultMap id="authorMapper" type="Author" autoMapping="true">
        <id column="author_id" property="authorId"/>
        <result column="author_name" property="authorName"/>
        <result column="author_address" property="authorAddress"/>
        <result column="author_sex" property="authorSex"/>
        <!-- collection标签:映射“对多”的关联关系 -->
    	<!-- property属性:对应类中的属性 -->
    	<!-- ofType属性:集合属性中元素的类型 -->
        <collection property="books" ofType="Book" autoMapping="true">
            <id column="book_id" property="bookId"/>
            <result column="book_name" property="bookName"/>
            <result column="book_price" property="bookPrice"/>
        </collection>
</resultMap>

<select id="queryAll" resultMap="authorMapper">
       select b.book_id,b.book_name,b.book_price,b.author_id,a.author_name,a.author_address,a.author_sex
       from book b,author a
       where a.author_id = b.author_id
</select>

3.3 分步查询

分开查询Book和Author。

例:
查询每本图书的作者

<resultMap id="bookMap" type="com.wdzl.pojo.Book" autoMapping="true">
        <id column="book_id" property="bookId"/>
        <result column="book_name" property="bookName"/>
        <result column="book_price" property="bookPrice"/>
        <association property="author" autoMapping="true" column="author_id" select="queryAuthorId"/>
</resultMap>

<select id="queryAll" resultMap="bookMap">
        select book_id,book_name,book_price,author_id from book
</select>

<!-- resultMap="com.wdzl.dao.IAuthorDao.authorMap 这个resultMap引用了命名空间com.wdzl.dao.IAuthorDao的resultMap(authorMap) -->
<select id="queryAuthorId" resultMap="com.wdzl.dao.IAuthorDao.authorMap">
        select author_id,author_name,author_sex ,author_address from author where author_id=#{authorId}
</select>

例:查询每个作者出版的书

<resultMap id="authorMap" type="Author" autoMapping="true">
        <id column="author_id" property="authorId"/>
        <result column="author_name" property="authorName"/>
        <result column="author_address" property="authorAddress"/>
        <result column="author_sex" property="authorSex"/>
        <!--javaType 属性的类型
          ofType : 集合中元素的类型
        -->

		<!-- books集合属性的映射关系,使用分步查询 -->
		<!-- 在collection标签中使用select属性指定要引用的SQL语句 -->
		<!-- select属性值的格式是:Mapper配置文件的名称空间.SQL语句id,如果在当前的名称空间,则可以直接引用 -->
		<!-- column属性:指定Book和AAuthor之间建立关联关系时所依赖的字段 -->
        <collection property="books" fetchType="lazy"  autoMapping="true" column="author_id" select="queryBookId">
          
        </collection>
</resultMap>

<!-- 作者下的所有图书-->
<select id="queryAll" resultMap="authorMap">
        select author_id,author_name,author_address,author_sex from author
</select>

<!-- 为什么这里还需要配置别名,因为这里并没有与resultMap关联-->
<select id="queryBookId" resultType="Book">
        select book_id bookId,book_name bookName,book_price bookPrice from book where author_id = #{author_id}
</select>

3.4 懒加载(两种办法)

懒加载的概念:对于实体类关联的属性到需要使用时才查询。也叫延迟加载。

在全局配置文件中设置懒加载

<!-- 使用settings对Mybatis全局进行设置 -->
<settings>
    <!-- 开启延迟加载功能:需要配置两个配置项 -->
    <!-- 1、将lazyLoadingEnabled设置为true,开启懒加载功能 -->
    <setting name="lazyLoadingEnabled" value="true"/><!-- 2、将aggressiveLazyLoading设置为false,关闭“积极的懒加载” -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

在映射文件中配置懒加载(此时只有进行分步查询时才可以配置)
在collection 或association上 配置 fetchType=“lazy”

<collection property="books" fetchType="lazy"  autoMapping="true" column="author_id" select="queryBookId">
            <id column="book_id" property="bookId"/>
            <result column="book_name" property="bookName"/>
            <result column="book_price" property="bookPrice"/>
</collection>

第四章: 动态SQL

Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。
注意:这里的数据库模型和Java实体类与第三章的一样。

4.1 if和where标签

dao层中接口的方法:

// 按照书名模糊查询并且大于某个价格的书
public List<Book> queryByNameAndPrice(Book book);

使用where标签自动去掉“标签体内前面、后面多余的and/or” 。

使用if标签,让我们可以有选择的加入SQL语句的片段。这个SQL语句片段是否要加入整个SQL语句,就看if标签判断的结果是否为true。

在if标签的test属性中,可以访问实体类的属性,不可以访问数据库表的字段。

<![CDATA[ ]]>在中括号里面可以写 > 、 < 等特殊符号,否则,写 > 、<等特殊符号需要使用 & gt ;& lt;等表示。

SQL语句:

<select id="queryByNameAndPrice" resultType="com.wdzl.pojo.Book">
        select book_id bookId,book_name bookName,book_price bookPrice from book
        <where>
            <!-- 在if标签内部,需要访问接口的参数时还是正常写#{} -->
            <if test="bookName != null and bookName != ''">
                and book_name like #{bookName}
            </if>
            <if test="bookPrice != null">
                <![CDATA[
                    and book_price > #{bookPrice}
                ]]>
            </if>
            <!--
         第一种情况:所有条件都满足 WHERE book_name=? and book_price>?
         第二种情况:部分条件满足 WHERE book_price>?
         第三种情况:所有条件都不满足 没有where子句
         -->
        </where>
</select>

4.2 set标签

应用场景:更新一部分字段的信息。

使用set标签动态管理set子句,并且动态去掉两端多余的逗号。

dao层中接口的方法:

// 根据需要修改部分书籍信息
public void updateInfo(Book book);

SQL语句:

<update id="updateInfo">
        update book
        <set>
            <if test="bookName != null and bookName != ''">
                book_name = #{bookName},
            </if>
            <if test="bookPrice != null">
                book_price = #{bookPrice}
            </if>
        </set>
        where book_id = #{bookId}
        <!--
         第一种情况:所有条件都满足 SET book_name=?, book_price=?
         第二种情况:部分条件满足 SET book_price =?
         第三种情况:所有条件都不满足 update book where book_id=?
            没有set子句的update语句会导致SQL语法错误
     -->
</update>

4.3 trim标签

使用trim标签控制条件部分两端是否包含某些字符

  • prefix属性:指定要动态添加的前缀
  • suffix属性:指定要动态添加的后缀
  • prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
  • suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值

dao层中接口的方法:

// 根据需要修改部分书籍信息
public List<Book> queryByNameAndPrice(Book book);

SQL语句:

<select id="queryByNameAndPrice" resultType="com.wdzl.pojo.Book">
        select book_id bookId,book_name bookName,book_price bookPrice from book
        <trim prefix="where" prefixOverrides="and|or">
            <if test="bookName != null and bookName != ''">
                and book_name like #{bookName}
            </if>
            <if test="bookPrice != null">
                <![CDATA[
                    and book_price > #{bookPrice}
                ]]>
            </if>
        </trim>
</select>

4.4 choose/when/otherwise标签

在多个分支条件中,仅执行一个。
dao层中接口的方法:

// 根据需要修改部分书籍信息
public List<Book> queryByNameAndPrice(Book book);

SQL语句:

<select id="queryByNameAndPrice" resultType="com.wdzl.pojo.Book">
        select book_id bookId,book_name bookName,book_price bookPrice from book
        <choose>
            <when test="bookName != null and bookName != ''">
                where book_name like #{bookName}
            </when>
            <when test="bookPrice != null">
                <![CDATA[
                    where book_price > #{bookPrice}
                ]]>
            </when>
            <otherwise>
                where book_id = #{bookId}
            </otherwise>
        </choose>
     <!--
     第一种情况:第一个when满足条件 where book_name=?
     第二种情况:第二个when满足条件 where book_price > ?
     第三种情况:两个when都不满足,则执行book_id = ? 
     -->
</select>

4.5 foreach标签

在批量更新中,会报错。

dao层中接口的方法:

// 根据需要修改部分书籍信息
public void updateSomeInfo(List<Book> list);

SQL语句

 <update id="updateSomeInfo">
 <!--
    collection属性:要遍历的集合
    item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象
    separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符
    open属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串
    close属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串
    index属性:这里起一个名字,便于后面引用
        遍历List集合,这里能够得到List集合的索引值
        遍历Map集合,这里能够得到Map集合的key
 -->
        <foreach collection="list" item="book" separator=";">
        <!-- 默认collection 取值可以是 list、arg0、collection;还可以使用@Param注解指定一个具体的名字,当做collection属性的值 -->
        <!-- 在foreach标签内部如果需要引用遍历得到的具体的一个对象,需要使用item属性声明的名称 -->
            update book set book_name = #{book.bookName} where book_id = #{book.bookId}
        </foreach>
    </update>

此时会出现语法错误,但并不是因为语法导致的,解决办法就是需要在数据库连接信息的 url地址 后跟allowMultiQueries=true

4.6 sql 标签

抽取重复的SQL片段

<sql id="mySelectSql">
     select book_id,book_name,book_price from book
</sql>

引用已抽取的SQL片段

<!-- 使用include标签引用声明的SQL片段 -->
<include refid="mySelectSql"/>

第五章: 缓存

5.1 缓存

缓存机制:
在这里插入图片描述

一级缓存和二级缓存的使用顺序
在这里插入图片描述
查询的顺序是:

  • 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
  • 如果二级缓存没有命中,再查询一级缓存
  • 如果一级缓存也没有命中,则查询数据库
  • SqlSession关闭之前,一级缓存中的数据会写入二级缓存

效用范围:
一级缓存: SqlSession级别
二级缓存: SqlSessionFactory级别
在这里插入图片描述
一级缓存和二级缓存的范围大小:
在这里插入图片描述

5.2 一级缓存

MyBatis 默认一级缓存是开启的,一级缓存是在 SqlSession 级别的缓存。即,同一个 SqlSession ,多次调用同一个 Mapper 和同一个方法的同一个参数,只会进行一次数据库查询,然后把数据缓存到缓存中,以后直接先从缓存中取出数据,不会直接去查数据库。但是不同的 SqlSession 对象,是不同享缓存的。
在这里插入图片描述
上面查询中,可以发现查询相同的bookId,只需要查询第一次的Sql语句,第二次就不需要。

		SqlSession session = MyBatisUtil.getSqlSession();
        IBookDao mapper = session.getMapper(IBookDao.class);
        List<Book> books = mapper.queryAll();
        for (Book book : books) {
            System.out.println(book);
        }
        System.out.println(mapper.queryOne(2));
        MyBatisUtil.close(session);

在这里插入图片描述
注意:上面查询中,queryAll()查询出了所有的book信息,然后又进行了一次queryOne() 查询bookId=2的图书的记录,可以发现又进行了一次Sql语句的查询。因为 MyBatis判断是否相同查询方式是 Statement Id + Offset + Limmit + Sql + Params 来判断的。不同方法不同参数会判定为不同的查询。

一级缓存失效的情况

  1. sqlSession.clearCache(); ------ 清空缓存
  2. 在映射文件中的< select >上加flushCache=“true”
  3. 执行了更新操作:update insert delele
  4. .sqlSession.commit(); rollback(); ----- 引起清空一级缓存的
  5. 不同的映射或SQL 都不能相互缓存
  6. 查询条件发生了变化

5.3 二级缓存

一级缓存中,其最大的共享范围就是一个SqlSession内部,如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,具体的工作流程如下所示:
在这里插入图片描述

二级缓存的配置:

①:开启二级缓存功能: 注意:默认情况二级缓存配置是开启的。

<mapper namespace="com.wdzl.dao.IBookDao">
    <!-- 加入cache标签启用二级缓存功能 -->
    <cache/>
</mapper>

注意:在上面配置的二级缓存,只是针对于此命名空间的。如果如果要单独针对某个查询配置二级缓存开启策略时,可以在映射文件中对查询单独配置。如下配置:

 <select id="queryOne" resultType="book" useCache="true">

注意: 如果全局和局部缓存都配置了,则局部配置优先级高

使用< cache-ref> 引用其他命名空间中的缓存。
例:在AuthorMapper.xml中引用BookMapper.xml的缓存

<mapper namespace="com.wdzl.dao.IAuthorDao">
    <!-- 引用其他命名空间的缓存 -->
    <cache-ref namespace="com.wdzl.dao.IBookDao"/>
    <!--上面直接引用下面空间中的缓存配置策略即可,注意下面如果没有配置cache,上面会异常 -->

②:让实体类序列化

public class Book implements Serializable

③:测试

 	@Test
    public void test4(){
        test3();
        System.out.println("==================");
        test3();
    }
    public void test3(){
        SqlSession session = MyBatisUtil.getSqlSession();
        IBookDao mapper = session.getMapper(IBookDao.class);

        Book book = mapper.queryOne(1);
        System.out.println(book);

        MyBatisUtil.close(session);
    }

打印效果:

Cache Hit Ratio [com.wdzl.dao.IBookDao]: 0.0
Opening JDBC Connection
Created connection 13803304.
==>  Preparing: select book_id bookId,book_name bookName,book_price bookPrice from book where book_id = ?
==> Parameters: 1(Integer)
<==    Columns: bookId, bookName, bookPrice
<==        Row: 1, Java入门, 45.0
<==      Total: 1
Book(bookId=1, bookName=Java入门, bookPrice=45.0, author=null)
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@d29f28]
Returned connection 13803304 to pool.
==================
As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
Cache Hit Ratio [com.wdzl.dao.IBookDao]: 0.5
Book(bookId=1, bookName=Java入门, bookPrice=45.0, author=null)

④:缓存命中率
日志中打印的Cache Hit Ratio叫做缓存命中率

Cache Hit Ratio [com.wdzl.dao.IBookDao]: 0.0
Cache Hit Ratio [com.wdzl.dao.IBookDao]: 0.5

缓存命中率=命中缓存的次数/查询的总次数

二级缓存注意点:

  • 默认第一次查出的对象,放入一级缓存。默认不会自动存入二级缓存。
  • 要存入二级缓存,执行sqlsession.close() 或 commit() 存入二级缓存。
  • 如果直接清除缓存,你就会发现没有命中二级缓存

自定义缓存的策略

在Mapper配置文件中添加的cache标签可以设置一些属性:

  • eviction属性:缓存回收策略
    LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
    FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
    SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
    WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
    默认的是 LRU。
  • flushInterval属性:刷新间隔,单位毫秒
    默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
  • size属性:引用数目,正整数
    代表缓存最多可以存储多少个对象,太大容易导致内存溢出
  • readOnly属性:只读,true/false
    true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
    false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
<mapper namespace="com.wdzl.dao.IBookDao">
    <!-- 
    加入cache标签启用二级缓存功能
    size : 缓存存放多少个元素
    size=2 时,当查询三个不同的对象时,第三个无法缓存,从而需要查询sql,前面两个不需要
 	-->
    <cache size="2"/>
</mapper>

测试自定义缓存的策略

	@Test
    public void test5(){
        SqlSession session0 = MyBatisUtil.getSqlSession();
        IBookDao mapper = session0.getMapper(IBookDao.class);

        mapper.queryOne(1);
        mapper.queryOne(2);
        mapper.queryOne(3);
        session0.close();
        SqlSession session1 = MyBatisUtil.getSqlSession();
        IBookDao mapper1 = session1.getMapper(IBookDao.class);
        mapper1.queryOne(1);
        session0.close();
        SqlSession session2 = MyBatisUtil.getSqlSession();
        IBookDao mapper2 = session2.getMapper(IBookDao.class);
        mapper2.queryOne(2); // 发现这里又执行SQL了,因为上面三个对象只能缓存其中两个
        session0.close();
    }

5.4 缓存组件:ehcache

Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。

实现步骤

注意:使用组件不需要实现序列化接口了

①:在pom.xml中配置依赖

<dependency>
	<groupId>org.mybatis.caches</groupId>
	<artifactId>mybatis-ehcache</artifactId>
	<version>1.2.1</version>
</dependency>

②:在映射文件中配置

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

第六章:其他

6.1 类型处理器

概念:MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。下表描述了一些常见的类型处理器。

类型处理器 Java 类型 JDBC 类型
BooleanTypeHandler java.lang.Boolean, boolean 数据库兼容的 BOOLEAN
ByteTypeHandler java.lang.Byte, byte 数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandler java.lang.Short, short 数据库兼容的 NUMERIC 或 SMALLINT
IntegerTypeHandler java.lang.Integer, int 数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandler java.lang.Long, long 数据库兼容的 NUMERIC 或 BIGINT
FloatTypeHandler java.lang.Float, float 数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandler java.lang.Double, double 数据库兼容的 NUMERIC 或 DOUBLE
StringTypeHandler java.lang.String CHAR, VARCHAR
DateOnlyTypeHandler java.util.Date DATE
SqlDateTypeHandler SqlDateTypeHandler DATE
LocalDateTypeHandler java.time.LocalDate DATE

如果想要看其他的类型处理器,请参考官方文档

类型处理器分为内置类型转换器自定义类型转换器

6.1.1 自定义类型转换器实现方式

两种实现方式:

  • 实现TypeHandler
  • 继承 BaseTypeHandler

6.1.1.1 编写自定义类型的转换器

// 此泛型指的是java中要转化对象的类型
// 实现性别类型的转换
// String 男  -----  JDBC  int
public class SexTypeHandler implements TypeHandler<String> {

    /**
     *
     * @param preparedStatement
     * @param i     占位符位置
     * @param s     给占位符赋值的参数
     * @param jdbcType
     * @throws SQLException
     */
    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
        if("男".equals(s)){
            preparedStatement.setInt(i,1);
        }else if("女".equals(s)){
            preparedStatement.setInt(i,2);
        }
    }

    /**
     *
     * @param resultSet
     * @param s     列名
     * @return
     * @throws SQLException
     */
    @Override
    public String getResult(ResultSet resultSet, String s) throws SQLException {
        int sex = resultSet.getInt(s);
        if(sex == 1){
            return "男";
        }
        return "女";
    }

    /**
     *
     * @param resultSet
     * @param i     处于第i个位置
     * @return
     * @throws SQLException
     */
    @Override
    public String getResult(ResultSet resultSet, int i) throws SQLException {
        return null;
    }

    @Override
    public String getResult(CallableStatement callableStatement, int i) throws SQLException {
        return null;
    }
}

6.1.1.2 使用两种配置方式

  • 全局配置
    在全局配置文件中配置,MyBatis-conf.xml中配置。
<!--全局类型转换器
通过 javaType和jdbcType两者来匹配的。
-->
 <typeHandlers>
        <typeHandler handler="com.wdzl.typehandler.SexTypeHandler" javaType="String" jdbcType="INTEGER"/>
</typeHandlers>

在映射文件中配置。

<resultMap id="sexChange" type="com.wdzl.pojo.Author">
        <id column="author_id" property="authorId"/>
        <result column="author_name" property="authorName"/>
        <result column="author_address" property="authorAddress"/>
        <result column="author_sex" property="authorSex" javaType="String" jdbcType="INTEGER"/>
</resultMap>
<!-- 查询时的转换语句 -->
<select id="queryAll" resultMap="sexChange">
        select author_id,author_name,author_address,author_sex from author
</select>
<!-- 插入时的转换语句,更新也类似 -->
<insert id="insertAuthor">
        insert into author(author_id,author_name,author_address,author_sex) values (#{authorId},#{authorName},#{authorAddress},#{authorSex,javaType=String,jdbcType=INTEGER})
</insert>
  • 局部配置

在映射文件中配置。
注意:resultMap标签在2.4.6节中讲过。

<resultMap id="sexChange" type="com.wdzl.pojo.Author">
        <id column="author_id" property="authorId"/>
        <result column="author_name" property="authorName"/>
        <result column="author_address" property="authorAddress"/>
        <result column="author_sex" property="authorSex" typeHandler="com.wdzl.typehandler.SexTypeHandler"/>
</resultMap>

<!-- 查询时的转换语句 -->
<select id="queryAll" resultMap="sexChange">
        select author_id,author_name,author_address,author_sex from author
</select>

<!-- 插入时的转化语句,更新也类似-->
<insert id="insertAuthor">
        insert into author(author_id,author_name,author_address,author_sex) values (#{authorId},#{authorName},#{authorAddress},#{authorSex,typeHandler=com.wdzl.typehandler.SexTypeHandler})
</insert>

6.2 分页插件

①:导入分页插件的依赖

<dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.2.0</version>
</dependency>

②:在全局配置文件中配置插件

<!--分页插件-->
<plugins>
	<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

③:使用
注意:在每次查询之前,都必须要设置起始页和每页记录数即可。

@Test
    public void test(){
        SqlSession session = MyBatisUtil.getSqlSession();
        IBookDao mapper = session.getMapper(IBookDao.class);
        // 第一个参数是起始页,第二个参数是每页记录数
        Page<Book> page = PageHelper.startPage(2, 3);
        mapper.queryAll().forEach(System.out::println);
        System.out.println("当前页号: " + page.getPageNum());       //当前页号
        System.out.println("总页数: " + page.getPages());          // 总页数
        System.out.println("每页记录数: " + page.getPageSize());    //页的记录数
        System.out.println("总记录数: " + page.getTotal());        // 总记录数
        System.out.println("当前页记录列表: " + page.getResult());  //当前页记录列表
        MyBatisUtil.close(session);
    }

6.3 逆向工程

6.3.1 概念

正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的。

逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:

  • Java实体类
  • Dao接口
  • Mapper配置文件

6.3.2 具体实现

准备sql语句

create table t_book(
	book_id int primary key auto_increment,
	book_name varchar(30),
	book_price float,
	boook_sortname varchar(30),
	pubDate date,
	pubcom varchar(30)
)

6.3.2.1 配置pom.xml

	<dependencies>
        <!-- 依赖Mybatis核心包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>
    </dependencies>
    <!-- 控制Maven在构建过程中相关配置 -->
    <build>
        <!-- 构建过程中用到的插件 -->
        <plugins>
            <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.0</version>
                <!-- 插件的依赖 -->
                <dependencies>
                    <!-- 逆向工程的核心依赖 -->
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.2</version>
                    </dependency>
                    <!-- 数据库连接池 -->
                    <dependency>
                        <groupId>com.alibaba</groupId>
                        <artifactId>druid</artifactId>
                        <version>1.2.8</version>
                    </dependency>
                    <!-- MySQL驱动 -->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>8.0.26</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

6.3.2.2 配置逆向工程的xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!--
            targetRuntime: 执行生成的逆向工程的版本
                    MyBatis3Simple: 生成基本的CRUD(清新简洁版)
                    MyBatis3: 生成带条件的CRUD(奢华尊享版)
     -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!-- 数据库的连接信息 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/testdb"
                        userId="root"
                        password="123456">
            <!-- 解决table schema中有多个重名的表生成表结构不一致问题 -->
            <property name="nullCatalogMeansCurrent" value="true"/>
        </jdbcConnection>
        <!-- javaBean的生成策略-->
        <javaModelGenerator targetPackage="com.zhang.pojo" targetProject=".\src\main\java">
            <!--enableSubPackages:是否让schema作为包的后缀  -->
            <property name="enableSubPackages" value="true" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="com.zhang.mapper"  targetProject=".\src\main\resources">
            <!--enableSubPackages:是否让schema作为包的后缀  -->
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.zhang.mapper"  targetProject=".\src\main\java">
            <!--enableSubPackages:是否让schema作为包的后缀  -->
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="t_author" domainObjectName="Author"/>
        <table tableName="t_book" domainObjectName="Book"/>
    </context>
</generatorConfiguration>

6.3.2.3 点击生成逆向工程的文件

在这里插入图片描述
然后会帮你自动生成下述目录。
在这里插入图片描述

6.3.2.4 进行测试

编写db.properties文件

url=jdbc:mysql://127.0.0.1:3306/testdb?serverTimezone=GMT
driver=com.mysql.cj.jdbc.Driver
username=root
password=123456

编写mybatis-conf.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>
    <properties resource="db.properties"></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/zhang/mapper/BookMapper.xml"/>
    </mappers>
</configuration>

编写测试文件

	public static void main(String[] args) {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-conf.xml");
            SqlSessionFactory sessionFactory = builder.build(is);
            SqlSession session = sessionFactory.openSession(true);
            BookMapper mapper = session.getMapper(BookMapper.class);
            mapper.selectByExample(null).forEach(System.out::println);
            is.close();
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

6.4 注解

6.4.1 基本使用

①: DAO接口中对应的方法上添加注解

 	@Select("select book_id bookId,book_name bookName,book_price bookPrice from book where book_id = #{bookId}")
    public Book queryOne(Integer bookId);
    
     @Insert("insert into book(book_id,book_name,book_price) values(#{bookId},#{bookName},#{bookPrice})")
    public void insertBook(Book book);

②: 在配置文件中引入映射注解类

 <mappers>
       <mapper class="com.wdzl.dao.IBookDao"/>
</mappers>

6.4.2 主键获取

这里有两种方式。一种是使用**@Options注解;另一种是使用@SelectKey**注解。

	@Insert("insert into book(book_id,book_name,book_price) values(#{bookId},#{bookName},#{bookPrice})")
    /*@Options(useGeneratedKeys=true, keyProperty="bookId", keyColumn="book_id")*/
    @SelectKey(statement = "select last_insert_id()", keyProperty = "bookId",
            before = false, resultType = Integer.class)
    public Integer insertBook(Book book);

测试:

	@Test
    public void test6(){
        SqlSession session = MyBatisUtil.getSqlSession();
        IBookDao mapper = session.getMapper(IBookDao.class);
        Book book = new Book();
        book.setBookName("Java从入门到入土");
        book.setBookPrice(85f);
        mapper.insertBook(book);
        // 如果不使用@Options注解或@SelectKey注解,你会发现这里获取的id为空。
        System.out.println(book.getBookId());
        MyBatisUtil.close(session);
    }

6.4.3 结果映射

 	@Select("select book_id,book_name,book_price from book")
    @Results({
            @Result(property = "bookId",id = true,column = "book_id"),
            @Result(property = "bookName",column = "book_name"),
            @Result(property = "bookPrice",column = "book_price")
    })
    public List<Book> queryAll();

如果多个查询需要结果映射时,重复编写 @Results 就过于繁琐,可以修改上面代码,给增加 id 属性,下面其他地方通过 id 引用即可重复使用。

两种方式:

方式一:

  • @Results(id=“bookMap”)
  • @ResultMap(“bookMap”)
 	@Select("select book_id,book_name,book_price from book")
    @Results(id = "bookMap",value = {
    		@Result(property = "bookId",id = true,column = "book_id"),
            @Result(property = "bookName",column = "book_name"),
            @Result(property = "bookPrice",column = "book_price")
    })
    public List<Book> queryAll();
    
	@Select("select book_id,book_name,book_price from book where book_id = #{bookId}")
    @ResultMap("bookMap") // 引用上面的结果映射
    public Book queryOne(Integer bookId);

方式二:

先在映射文件中配置,通过xml 方式配置结果映射

<resultMap id="bookMap" type="com.wdzl.pojo.Book">
        <id property="bookId" column="book_id"/>
        <result property="bookName" column="book_name"/>
        <result property="bookPrice" column="book_price"/>
</resultMap>

然后在全局配置文件中,转化为xml资源进行引入

<mappers>
        <mapper class="com.wdzl.dao.IBookDao"/>
        <mapper resource="com/wdzl/mapper/BookMapper.xml"/>
</mappers>

最后在注解 Dao类中直接通过 @ResultMap() 引用即可

 	@Select("select book_id,book_name,book_price from book")
    @ResultMap("bookMap") //引用上面的resultMap中resultMap的id。
    public List<Book> queryAll();

6.4.4 动态SQL

在Dao层接口中添加此方法。

	@Update("<script>" +
            "update book " +
            "<set>" +
            " <if test=\"bookName!=null and bookName!=''\">" +
            " book_name=#{bookName}," +
            " </if>" +
            " <if test=\"bookPrice!=null\">" +
            " book_price=#{bookPrice}," +
            " </if>" +
            "</set>" +
            "<where>" +
            " <if test=\"bookId!=null\">" +
            " book_id=#{bookId}" +
            " </if>" +
            "</where>" +
            "</script>")
    void update(Book book);

6.4.5 Provider方式实现动态SQL

上面的使用xml 标记方式实现动态SQL ,太繁琐,MyBatis 中提供了 Provider 的方式。

.①:编写动态生成 SQL 的provider 类,用来提供动态SQL字符串生成

	/**
     * 根据条件动态生成SQL
     * @param book
     * @return
     */
    public String query(Book book) {
        return new SQL(){
            {
                SELECT("book_id bookId","book_name bookName","book_price bookPrice");
                FROM("book");
                if(book.getBookName() != null){
                    WHERE("book_name = #{bookName}");
                }
                if(book.getBookPrice() != null){
                    WHERE("book_price > #{bookPrice}");
                }
                ORDER_BY("book_price desc");
            }
        }.toString();
    }

②: 引用 provider类

  • @SelectProvider
  • @UpdateProvider
  • @DeleteProvider
  • @InsertProvider
	@SelectProvider(type = BookProvider.class,method = "query")
    public List<Book> search(Book book);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Mybatis框架全面详解 的相关文章

  • 传递自定义类型查询参数

    如何接受自定义类型查询参数 public String detail QueryParam request final MYRequest request 上面的行在启动服务器时出现错误 jersey server model ModelV
  • 如何在正则表达式中编写可选单词?

    我想编写一个识别以下模式的 java 正则表达式 abc def the ghi and abc def ghi 我试过这个 abc def the ghi 但是 它没有识别第二种模式 我哪里出错了 abc def the ghi 删除多余
  • 如何检查单词是否在wordNet中

    我开始了解wordNet直到我知道我找到了synonymous对于一个特定的词 现在我有一个文件 我想使用标记化该文本n gram例如 String s I like to wear tee shirt 使用后n gram这将是 I lik
  • JFrame 在连续运行代码时冻结

    我在使用时遇到问题JFrame 它会冻结 连续运行代码 下面是我的代码 点击时btnRun 我调用了该函数MainLoop ActionListener btnRun Click new ActionListener Override pu
  • 从字符串中删除重音符号

    Android 中有没有什么方法 据我所知 没有 java text Normalizer 可以从字符串中删除任何重音 例如 变成 eau 如果可能的话 我想避免解析字符串来检查每个字符 java text NormalizerAndroi
  • 将 Python 列表(JSON 或其他)插入 MySQL 数据库

    所以我在Python中有一堆数组数据 嗯 相反 我有一个清单 我试图将此数组存储到 MySQL 数据库中的单个单元格中 我尝试使用 JSON 来序列化我的数据 但也许我不明白 JSON 是如何工作的 因此 在连接到我的数据库后 我尝试了上游
  • 如何获取 JDBC 中 UPDATE 查询影响的所有行?

    我有一项任务需要使用更新记录PreparedStatement 一旦记录被更新 我们知道更新查询返回计数 即受影响的行数 但是 我想要的不是计数 而是受更新查询影响的行作为响应 或者至少是受影响的行的 id 值列表 这是我的更新查询 UPD
  • React Native v0.71.8 React-native-vector-icons 你看不到的图标

    我在用react native版本v0 71 8 我安装了react native vector icons库 但图标未显示 似乎链接在最新版本的 React Native 中不再起作用 所以我按照说明进行操作 但它不再编译 出现以下错误
  • Hybris:如何在impex中导入zip文件中的媒体?

    我知道我们可以导入未像这样压缩的图像 siteResource jar com project initialdata constants ProjectInitialDataConstants projectinitialdata imp
  • 如何使用 AffineTransform.quadrantRotate 旋转位图?

    我想旋转一个bitmap关于它的中心点 然后将其绘制成更大的图形上下文 位图是40x40 pixels 图形上下文是500x500 pixels 这就是我正在做的 BufferedImage bi new BufferedImage 500
  • 如何在一次操作中使用 Thymeleaf 检查 null 和空条件?

    有什么方法可以检查 Thymeleaf 中的 null 和empty 条件吗 方法一 1 variable1 variable2 variable3 2 variable null 3 variable 如果我们结合两个条件 例如 vari
  • 选择每组最新的项目[重复]

    这个问题在这里已经有答案了 可能的重复 检索每组中的最后一条记录 https stackoverflow com questions 1313120 retrieving the last record in each group 我有 2
  • 线程数组?

    所以我在理解如何避免线程的顺序执行时遇到了问题 我试图创建一个线程数组并在单独的循环中执行 start 和 join 函数 这是我现在拥有的代码示例 private static int w static class wThreads im
  • Spring 如何在运行时获取有关“强类型集合”的泛型类型信息?

    我在 Spring 3 0 文档中阅读了以下内容 强类型集合 仅限 Java 5 在 Java 5 及更高版本中 您可以使用强类型集合 使用泛型类型 也就是说 可以声明一个 Collection 类型 使其只能包含 String 元素 例如
  • 如何在不使用 -cp 开关的情况下在 Groovy 中自动加载数据库 jar?

    我想简化调用 Oracle 数据库的 Groovy 脚本的执行 如何将 ojdbc jar 添加到默认类路径以便我可以运行 groovy RunScript groovy 代替 groovy cp ojdbc5 jar RunScript
  • Spring MVC:通用 DAO 和服务类

    我正在 Spring MVC 中编写网页 我使用 Generic DAO 编写了所有 DAO 现在我想重写我的服务类 我该如何写 通用服务 我的 DAO 如下 DAO package net example com dao import j
  • 亚马逊 Linux - 安装 openjdk-debuginfo?

    我试图使用jstack在 ec2 实例上amazon linux 所以我安装了openjdk devel包裹 sudo yum install java 1 7 0 openjdk devel x86 64 但是 jstack 引发了异常j
  • MySQL 查询中的窗口函数

    有没有办法在 SELECT 查询本身中动态地使用 MySQL 查询中的窗口函数 我知道在 PostgreSQL 中这是可能的 例如 下面是 PostgreSQL 中的等效查询 SELECT c server ip c client ip s
  • 假布尔值=真?

    我在一本书中找到了这段代码 并在 Netbeans 中执行了它 boolean b false if b true System out println true else System out println false 我只是不明白为什
  • MyBatis 枚举的使用

    我知道以前有人问过这个问题 但我无法根据迄今为止找到的信息实施解决方案 所以也许有人可以向我解释一下 我有一个表 状态 它有两列 id 和 name id是PK 我不想使用 POJO Status 而是使用枚举 我创建了这样一个枚举 如下所

随机推荐

  • matlab对正弦信号作FFT得到频谱图

    转自 https www cnblogs com alexanderkun p 4723577 html https blog csdn net qq 36024066 article details 89491650 一 FFT物理意义如
  • 大小限制_GEE学习笔记 九十五:请求大小限制

    这里说一个导出数据可能会遇到的问题 当然这个问题不仅仅可能会出现在数据导出 在其他涉及到网络通信的操作都有可能会出现这个问题 1 分析错误 首先先看一下报错的内容 如下图 分析一下报错的内容 消息请求 payload 的大小超过了大小限制
  • UE4 GamePlay框架个人整理——游戏信息

    摘自 UE4官方文档 接在UE4框架类关系之后 游戏信息 两个主要类负责处理进行中游戏的相关信息 Game Mode 和 Game State GameMode 任务是定义和实现游戏规则 用于表示一个游戏的玩法逻辑 如 类似英雄联盟中推掉最
  • 2022苹果开发者账号注册流程详解(公司账号)

    公司开发者账号注册流程详解 一 注册公司账号前需要提供的信息 1 邮箱 邮箱密码 2 微信或支付宝或储蓄卡或者信用卡 原来是必须有需要带国际支付能力银行卡 visa银行卡 实际上是转账到国外的苹果银行卡账号 3 一个手机号 4 D U N
  • 如何有效实现应用系统的增删改查功能

    如何有效实现应用系统的增删改查功能 1 前言 针对数据的增加 修改 删除和查询是应用软件系统中最为常用的功能 作为软件开发人员 如何高效的实现上述功能 并且能够保证系统数据的正确性 规范性和有效性是非常重要的 本文结合自己在项目实践中完成的
  • 【1day】复现深信服数据中心管理系统 XXE漏洞——可读取文件

    目录 一 漏洞描述 二 影响版本 三 资产测绘 四 漏洞复现 一 漏洞描述 深信服的数据中心管理系统DC是一种外置的AC 异地扩展备份管理 数据中心 它提供了强大的功能和灵活性 旨在满足日志数据处理的需求 该系统具备多种高级功能 包括多条件
  • 5. 垃圾收集器G1&ZGC详解

    JVM性能调优 1 G1收集器 XX UseG1GC 1 1 G1垃圾收集分类 1 1 1 YoungGC 1 1 2 MixedGC 1 1 3 Full GC 1 2 G1收集器参数设置 1 3 G1垃圾收集器优化建议 1 4 什么场景
  • C++中string类对象占多少个字节?

    不管string的内容多少 或者不赋值 大小都是28个字节 但是G 编译是4个字节 string的实现在各库中可能有所不同 但是在同一库中相同一点是 无论你的string 里放多长的字符串 它的sizeof 都是固定的 字符串所占的空间是从
  • 【Yarn】YARN 核心设计 yarn 核心 service 是如何运行的

    文章目录 1 概述 2 案例 2 1 service 2 2 状态类 2 3 抽象类 2 4 组合service 2 5 组合service 2 6 ResourceManager 1 概述 上一篇文章 Yarn Yarn NodeMana
  • CompletableFuture取多个任务结果

    public class DemoTest Test public void test long start System currentTimeMillis CompletableFuture
  • 网络分层模型及协议简介

    1 OSI七层模型和TCP IP四层模型 从应用程序的角度出发 主要掌握TCP IP模型 OSI七层模型 物数网传会表应 1 物理层 主要定义物理设备标准 如网线的接口类型 光纤的接口类型 各种传输介质的传输速率等 它的主要作用是传输比特流
  • 合并两个有序链表,合并后依然有序 --- 三种方法

    1 合并链表p1 p2到p1上 void CombineList ListNode p1 ListNode p2 合并链表p1 p2 到p1 if p1 NULL p1 p2 return if p2 NULL return ListNod
  • 随机抽人名小程序_年会必备,5分钟学会制作抽奖小程序

    每次年会 都希望人品爆发 中个大奖 可惜这么多年过去了 依然没中过大奖 你是不是跟卢子一样 抽奖的形式有多种 在Excel中也可以制作一个抽奖小程序 现在从A列成员中抽取3名中奖人员 效果如动图 抽奖都是随机生成的 要借助随机数 随机数有两
  • 数据库和AI的一次火花

    欢迎大家前往腾讯云 社区 获取更多腾讯海量技术实践干货哦 本文由宗文 发表于云 社区专栏 导语 通过历史数据 基于时间序列来预测未来 我们生活中很多数据是有时间维度的 比如说天气或者股票价格 对于这种带有时序的数据 有一种基于时间序列的预测
  • linux 下如何配置JDK呢?

    系统Ubuntu 下载jdk 9 0 1 1 切换到root 创建文件夹 xxxx ubuntu sudo su root ubuntu mkdir usr java 2 找到下载的jdk 9 0 1 linux x64 bin tar g
  • 【Shell-Kill】Kill掉Linux进程以及Kill掉Yarn任务

    Shell Kill Kill掉Linux进程以及Kill掉Yarn任务 1 使用 awk 方式 kill Linux 进程 2 使用 Shell 脚本批量处理 Yarn 任务 2 1 使用 Shell 脚本批量查看 Yarn 任务数量 2
  • Hibernate框架详解(三)

    表与表之间的关系 1 一对一 例子 在中国 一个男人只能有一个妻子 一个女人只能有一个丈夫 2 一对多 例子 学生和班级的关系 一个班级可以有多个学生 而一个学生只能属于一个班级 一对多建表 通过外键建立关系 在多的那一方创建字段作为外键
  • Android TV RecyclerView 焦点处理及获取焦点的Item保持在中间

    原生RecyclerView 在Tv中的焦点处理很不好 经常找不到焦点或者焦点丢失 原因是因为当item未显示时即未加载时时不能获取焦点的 所以当我们按上下键时经常丢失焦点或者焦点乱跳 要解决这个问题我们必须要手动控制RecyclerVie
  • JavaScript 输入内容表单规则验证

    1 电子邮箱格式 function emailCheck data let str a zA Z0 9 a zA Z0 9 a zA Z0 9 0 61 a zA Z0 9 a zA Z0 9 a zA Z0 9 0 61 a zA Z0
  • Mybatis框架全面详解

    MyBatis的基本使用 第一章 MyBatis的概念 第二章 MyBtais的基本使用 2 1 环境的搭建 2 1 1 物理建模 2 1 2 逻辑建模 2 1 3 搭建框架开发环境 2 1 3 1 junit框架 2 1 3 2 log4