柏竹 柏竹
首页
后端
前端
  • 应用推荐
关于
友链
  • 分类
  • 标签
  • 归档

柏竹

奋斗柏竹
首页
后端
前端
  • 应用推荐
关于
友链
  • 分类
  • 标签
  • 归档
  • Java基础

  • JavaWeb

  • 拓展技术

  • 框架技术

  • 数据库

    • MySQL
    • JDBC
    • Hibernate
    • Mybatis
      • 首次应用
      • Mybatis对象
        • Resources
        • SqlSessionFactoryBuilder
        • SqlSessionFactory
        • SqlSessio
      • MyBatis构架
      • MyBatis日志
      • MyBatis全局配置
        • 配置内容
        • properties
        • settings
        • typeAliases
        • Mappers
      • Mapper动态代理
      • parameterType输入
        • 传递多个参数
        • #{} 和 ${} 区别
      • resultType输出
      • 动态SQL
      • MyBatis映射关系
        • 对一 映射关系
        • 对多映射关系
      • MyBatis延迟加载
      • MyBatis缓存
        • 一级缓存
        • 二级缓存
        • 二级缓存其他设置
      • 反向工程
        • 项目生成
        • Maven项目
      • 分页插件
      • MyBatis问题
        • 线程优化
        • Sql语句字段调配问题
        • 库添加获取id问题
        • 映射问题
        • 数据库列名与实体类属性不匹配问题
        • 分页问题
      • 实用篇
        • 动态SQL
    • Redis
    • Redis原理篇
  • 数据结构

  • Spring

  • SpringMVC

  • SpringBoot

  • SpringClound

  • Ruoyi-Vue-Plus

  • 后端
  • 数据库
柏竹
2021-07-31
目录

Mybatis

# Mybatis

MyBatis 是一个开源、轻量级的数据持久化框架,是 JDBC 和 Hibernate 的替代方案。MyBatis 内部封装了 JDBC,简化了加载驱动、创建连接、创建 statement 等繁杂过程,只需关注 SQL 语句即可

数据持久化是将内存中的 数据模型 转换为 存储模型,以及将 存储模型 转换为 内存中数据模型 的统称。 例如,文件的存储、数据的读取以及对数据表的增删改查等都是数据持久化操作

ORM 是一种数据持久化技术,它在对象模型和关系型数据库之间建立起对应关系,解决了实体类和数据库表映射的问题,并且提供了一种机制,通过 JavaBean 对象去操作数据库表中的数据。

  • Object: java对象
  • Relation: 关系,库中的表
  • Mapping: 映射

参考文档:mybatis – MyBatis 3 (opens new window)

Mybatis 和 Hibernate区别

Mybateis Hibernate
学习门槛 低 高
查询语言 sql hql
ORM架构 半映射 完整映射
资源损耗 低 高
查询自由度 高 低
移植兼容 差 好

总结

  • Hibernate功能强大,数据库无关性好,O/R映射能力强。前提Hibernate要精通,否则用起来很累
  • Hibernate 学习知识点多,精通门槛高。例如 怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate方面需要经验和能力都很强才行

# 首次应用

前提:

  • 引入 mybatis架构依赖 mysql-connector-java底层连接依赖 junit代码测试
  • 数据库表中的列名称 与 实体类属性的名称 相同

项目结构

  .
  |
  ├── src
  |    ├── main
  |	   |	├── java
  |	   |	|	  └── com
  |	   |    |   	   ├── dao
  |    |    |          |	├── TeamDao
  |    |    |          |    └── TeamDaoImpl
  |	   |	|	  	   ├── pojo
  |	   |	|	  	   |	├── Team
  |	   |	|		   |	└── Team.xml
  |	   |    | 		   └── utils		
  |	   |    |    	   		└── MybatisUtil
  |	   |	└──	resources
  |	   |		    ├── log4j.properties
  |	   |			└── mybatis.xml
  |	  test
  |    └── ...
  └── pom.xml
  1. 引入依赖 ==pom.xml==

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.23</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    

    jar包说明:

    • mybatis
    • jdbc
    • 测试环境
  2. 创建 ==Team实体类== 与 ==mybatis库team表数据==

    CREATE TABLE `team` (
    	`teamId` int NOT NULL AUTO_INCREMENT COMMENT '球队ID',
    	`teamName` varchar(50) DEFAULT NULL COMMENT '球队名称',
    	`location` varchar(50) DEFAULT NULL COMMENT '球队位置',
    	`createTime` date DEFAULT NULL COMMENT '球队建立时间',
    	PRIMARY KEY (`teamId`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1003 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
    
    package com.pojo;
    
    import java.util.Date;
    
    public class Team {
        private Integer teamId;
        private String teamName;
        private String location;
        private Date createTme;
        
        /*省略setter和getter方法*/
        
        @Override
        public String toString() {
            return "Team{" +
                    "teamId=" + teamId +
                    ", teamName='" + teamName + '\'' +
                    ", location='" + location + '\'' +
                    ", createTme=" + createTme +
                    '}';
        }
    }
    
    
  3. 配置 连接文件 ==mybatis.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>
        <!--配置 mybatis 环境-->
        <environments default="development">
            <!--id:数据源的名称-->
            <environment id="development">
                <!--事务类型:使用 JDBC 事务,使用 Connection 的提交和回滚-->
                <transactionManager type="JDBC"/>
                <!--数据源 dataSource:创建数据库 Connection 对象
                type: POOLED 使用数据库的连接池
                -->
                <dataSource type="POOLED">
                    <!--连接数据库的四大参数
                    注意 加载驱动是 MySQL8以上,否则 driver和url 都不一样,可参考学过的JDBC-->
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?
    useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=GMT"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    
        <!-- 注册映射文件 -->
        <mappers>
            <mapper resource="com/pojo/Team.xml"/>
        </mappers>
    </configuration>
    

    注意:

    • <dataSource>标签 中主要配置数据库通信,必要的有 加载驱动/连接URL/账号/密码
    • <mappers>.<mapper>.resource属性 必须指定正确的映射文件(否则注册不了
  4. 配置 映射文件 ==Team.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">
    <!-- namespace= "名称必须与映射的类的名字一致,是完全限定名" -->
    <mapper namespace="com.pojo.Team">
        <!-- id="自定义名称,id不能重复;相当于dao中的方法名称"
        resultType="使用的要求:实体类中的属性名与表中的列名一致"
        -->
        <select id="findAll" resultType="com.pojo.Team">
            select * from team
        </select>
    </mapper>
    

    注意:

    • <mapper>.namespace属性 必须映射正确的实体类(完全限定名
    • <select>.resultType属性 返回集中 一列记录实例的 对象/数据类型
    • <select>.id属性 用于Session指定指定SQL的操作(该属性不能存在重复值
  5. 配置 映射文件的扫描 ==pom.xml==

    <build>
        <resources>
            <resource>
                <!--所有目录-->
                <directory>src/main/java</directory>
                <includes>
                    <!--包括目录 .properties, .xml 文件都会扫描到!!-->
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
        <plugins>
            ···
        </plugins>
    </build>
    
  6. 测试查询

    @Test
    public void test() throws IOException {
        // 1. 读取 mybatis配置文件
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        // 2. 创建 SqlSessionFactoryd对象 , 目的 获取 sqlsession
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        // 3. 创建可执行SQL语句的 SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 4. 执行 SQL语句
        List<Team> teamList = sqlSession.selectList("com.pojo.Team.findAll");
        // 5. 遍历结果
        System.out.println("遍历结果:");
        teamList.forEach(System.out::println);
        // 6. 释放资源
        sqlSession.close();
    }
    
    /* 执行结果
    
    遍历结果:
    Team{teamId=1, teamName='张三', location='上海', createTme=null}
    Team{teamId=2, teamName='李四', location='深圳', createTme=null}
    Team{teamId=3, teamName='王五', location='南京', createTme=null} 
    Team{teamId=4, teamName='赵六', location='广州', createTme=null}
    Team{teamId=5, teamName='小七', location='南宁', createTme=null}
    
    */
    

# Mybatis对象

# Resources

==org.apache.ibatis.io.Resources类==

用于读取资源文件。有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象

# SqlSessionFactoryBuilder

==org.apache.ibatis.session.SqlSessionFactoryBuilder类==

SqlSessionFactory 的创建,需要使用 SqlSessionFactoryBuilder对象的build()方法 。事实上使用SqlSessionFactoryBuilder的原因是将SqlSessionFactory这个复杂对象的创建交由Builder来执行,也就是使用了建造者设计模式

建造者模式(又称生成器模式):是一种对象的创建模式。可以将一个产品的 内部表象 与 产品的生成过程 分割开来,从而可以使一个建造过程生成具有 不同的内部表象的产品(将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示),这样用户只需指定需要建造的类型就可以得到具体产品,而不需要了解具体的建造过程和细节。

在建造者模式中,角色分指导者(Director)与建造者(Builder): 用户联系指导者,指导者指挥建造者,最后得到产品,建造者模式可以强制实行 一种分步骤进行的建造过程

# SqlSessionFactory

==org.apache.ibatis.session.SqlSessionFactory接口==

创建 SqlSession 需要使用 SqlSessionFactory接口的 openSession()方法,该方法重载的数量较多,因此只需关注主要应用的以下三个传参数据即可:

  • 事务处理: 在session作用域中 ,是否使用自动提交?(autoCommit)
  • 数据库连接: 在 MyBatis中,是否使用自己提供的连接?(connection)
  • 语句执行: 在 MyBatis中,是否复用 PreparedStatement 通道进行 批处理?
参数名 类型 说明
autoCommit boolean true事务自动提交,否则关闭
connection Connection 应用自己提供的连接
execType ExecutorType ExecutorType枚举定义定义了三个值
SIMPLE:为每个语句的执行创建一个新的预处理语句
REUSE:执行器会复用预处理语句
BATCH:执行器会批量执行所有更新语句

**注意:**如果调用了无参的 openSession()方法,则该Session会默认具备以下特性:

  • 事务作用域将会开启(不自动提交)
  • 将由当前环境配置的 DataSource 实例中获取 Connection 对象 (DataSource指定的是 数据库的配置数据及创建数据库获取的连接)
  • 事务隔离级别将会使用 驱动/数据源 的默认设置
  • 预处理语句不会被复用,也不会批量处理更新

# SqlSessio

==org.apache.ibatis.session.SqlSession接口==

SqlSession接口对象 用于执行持久化操作。一个 SqlSession 对应着一次数据库会话

SqlSessio为 线程不安全的,所以每次数据库会话结束前,立马调用 close()方法 将其关闭。再次需要会话,再次创建

SQL语句写法以及形式有多种,而SqlSessio对象将它们归类封装成了方法 CURD(增删改查) 4中类型(支持自动装箱或包装类)、JavaBean、POJO 或 Map

<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<T> Cursor<T> selectCursor(String statement, Object parameter)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)

说明:

  • insert、update、delete 方法返回值为影响的行数
  • selectOne:返回一个 对象/null ;selectList:返回多个 对象/null
  • 游标(Cursor)与列表(List)返回的结果相同,不同的是,游标借助迭代器实现了数据的惰性加载
  • selectMap()方法 ,它会将返回的对象的其中一个属性作为 key值,将对象作为 value值

# MyBatis构架

  1. Mybatis.xml文件是mybatis框架的全局配置文件,配置了mybatis框架运行的环境等信息
  2. Mapperxx.xml是SQL的映射文件,文件中配置了所有的操作数据库的sql语句,这些文件需要在全局配置文件中加载
  3. 通过mybatis环境等配置信息构建SqlSessionFactroy ,相当于是产生连接池
  4. 由会话工厂创建SqlSession即会 (连接),操作数据库需要通过SqlSession进行的
  5. Mybatis底层自定义了Executor执行器的接口操作数据库,Executor接口有两个实现,一个基本的执行器,一个是缓存的执行器
  6. Mapped statement 也是mybatis框架一个底层的封装对象,包装了mybatis配置信息以及sql映射信息。Mapperxx.xml文件中的一个SQL语句对应一个Mapped statement对象,sql的id就是Mapped statement的id
  7. Mapped statement对SQL执行输入参数的定义,输入参数包括HashMap、基本类型、pojo,Executor通过Mapped statemen在执行SQL语句 前将输入java对象映射到sql语句中,执行完毕SQL之后,输出映射就是JDBC编码中的对preparedStatement 执行结果的定义

# MyBatis日志

日志能够更准确了解运行时的详细信息

实现步骤:

  1. 添加依赖 log4j
  2. 创建日志配置文件 log4j.properties
  3. 在mybatis.xml 配置文件添加日志配置

依赖添加 (jar)

<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.17</version>
</dependency>

创建日志配置文件 log4j.properties

## Global logging configuration info warning error  选择日志呈现种类
log4j.rootLogger=DEBUG,stdout
## Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

在mybatis.xml 配置文件添加日志配置

<configuration>
<!--    日志配置-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    ·····
</configuration>

注意:settings标签 添加在该配置子标签中的第一个!

# MyBatis全局配置

之前应用的 全局配置文件 mybatis.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">

# 配置内容

MyBatis 行为设置和属性信息,全局配置文件的结构如下:

configuration(配置)

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

以上的配置顺序一定一定要遵循顺序进行配置,否则会失效

# properties

properties标签 可在外部进行配置,并可以进行动态替换。properties标签 在 configuration标签里的 下一节点

  • properties子元素配置
  • 外部属性文件配置

以连接 数据库 的四个参数数据为例子

properties子元素配置

<?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>
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=GMT"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </properties>
	····
            <dataSource type="POOLED">
                <!--连接数据库的四大参数
                注意数据库版本使用的是 MySQL8以上,如果是mysql5的话,driver和url都不一样,参考学过的JDBC-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
	····
</configuration>

外部属性文件配置 jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT
jdbc.username=root
jdbc.password=root
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--    引入配置文件-->
    <properties resource="jdbc.properties"/>
	····
            <dataSource type="POOLED">
                <!--连接数据库的四大参数
                注意数据库版本使用的是 MySQL8以上,如果是mysql5的话,driver和url都不一样,参考学过的JDBC-->
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
	····
</configuration>

# settings

MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。例如:日志、等...(以上实例有应用就不赘述了)

<!--配置日志-->
<settings>
	<setting name="logImpl" value="LOG4J"/>
</settings

# typeAliases

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

<?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>
	···
<!--    日志配置-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
<!--    自定义别名-->
    <typeAliases>
        <!--对单个实体类-->
        <typeAlias type="com.pojo.Team" alias="Team"/>
        <!--批量定义别名:类似于扫描(首字母支持大小写)-->
        <package name="com.pojo"/>
    </typeAliases>
    ···
</configuration>

在应用于 映射文件 里 的 parameterType、resultType 、...等属性

<!--查询所有-->
<select id="findAll" resultType="Team">
    select * from team;
</select>
<!--添加-->
<insert id="add" parameterType="Team">
    INSERT INTO `mybatis`.`team`(`teamName`, `location`, `createTime`) VALUES (#{teamName}, #{location}, #{createTime})
</insert>
<!--查询所有2-->
<select id="queryAll3" resultType="Team2">
    select teamId 'team_id',teamName 'team_name',location,createTime from team
</select>
<!--查单列单条数据-->
<select id="queryTotal" resultType="int">
    select count(teamId) from team
</select>
<!--查多列单条数据-->
<select id="queryMap" resultType="map">
    select min(teamId) 'min',max(teamId) 'max' from team
</select>

其他别名

别名 映射的类型
_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

# Mappers

MyBatis 执行 SQL映射语句 前提需要告诉 MyBatis 映射文件路径在哪里,从而实现执行SQL语句

配置形式:

  • 使用 相对于类路径的资源 引用
  • 使用 映射器接口实现类 的完全限定类名
  • 使用 包类的映射器接口实现全部注册为映射器(推荐)
<!-- 注册映射文件 -->
<mappers>
    <!-- 
		相对于类路径的资源 引用 
		使用相对于类路径的资源,从 classpath 路径查找文件
	-->
    <mapper resource="com/mapper/TeamMapper.xml"/>
    <!-- 
		使用的mapper接口的完全限定名 
		要求:接口和映射文件同包同名
	-->
    <mapper class="com.mapper.GameRecordMapper"/>
    <!-- 
		指定包下的 所有Mapper接口 
		注意:此种方法要求 Mapper接口名称和 mapper 映射文件名称相同,且在同一个目录中。
	-->
    <package name="com.mapper"/>
</mappers>

# Mapper动态代理

前面定义的 Dao接口和Dao实现类 没有实质性的工作意义,因此我们弃用Dao,可通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 id 的 SQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的,该形式被称为 ==Mapper接口 的动态代理方式==

要点说明:

  • Mapper 动态代理方式无需实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代理实现的
  • Mapper(映射) 文件里 sql语句中的标签id值 必须对应 接口中的方法名 一致
  • resources配置文件夹,在idea开发工具不能跨级进行创建,必须手动逐级创建路径
  • Mapper(映射) 在resources配置 文件夹中的路径必须 与 接口中的完全限定名 一致
  • SqlSession对象 需要通过 getMapper(Class<T> type)方法 获取代理对象,可获取指定接口的实现类对象
  • Mapper(映射) 文件里 mapper标签的namespace属性值 需要指定 接口的完全限定名
  • 每次添加映射 都需要去 mybatis.xml 的 mappers标签 进行添加注册映射文件
  • 进行 增删改 时需要 MybatisUtil工具类 获取SqlSession对象 进行提交

实例应用

项目结构

  .
  |
  ├── src
  |    ├── main
  |	   |	├── java
  |	   |	|	  └── com
  |	   |    |   	   ├── mapper
  |    |    |          |    └── TeamMapper
  |	   |	|	  	   ├── pojo
  |	   |	|		   |	└── Team
  |	   |    | 		   └── utils		
  |	   |    |    	   		└── MybatisUtil
  |	   |	└──	resources
  |	   |		    ├── log4j.properties
  |	   |			├── mybatis.xml
  |    |			└── com
  |    |				 └── mapper
  |    |				  	   └── TeamMapper.xml
  |	  test	
  |    └── ...
  └── pom.xml

创建 ==TeamMapper接口==

package com.mapper;

import com.pojo.Team;

import java.util.List;

public interface TeamMapper {
    List<Team> findAll();
    Team queryById(int id);
    Team queryByName(String name);
    int add(Team team);
    int update(Team newTeam);
    int delById(int id);
    int delByName(String name);
}

创建 ==TeamMapper.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">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.mapper.TeamMapper">

    <!--
    id="自定义名称,id不能重复;相当于dao中的方法名称"
    resultType="使用的要求:实体类中的属性名与表中的列名一致"
    -->
<!--    查询所有-->
    <select id="findAll" resultType="com.pojo.Team">
        select * from team;
    </select>

<!--    查询指定-->
    <select id="queryById" resultType="com.pojo.Team">
        select * from team where teamId=#{teamId}
    </select>
    <select id="queryByName" resultType="com.pojo.Team">
        select * from team where teamName=#{teamName}
    </select>

    <!--
    parameterType="指定对象作为参数"
    #{对象属性名}
    -->
<!--    添加数据-->
    <insert id="add" parameterType="com.pojo.Team">
        INSERT INTO `mybatis`.`team`(`teamName`, `location`, `createTime`) VALUES (#{teamName}, #{location}, #{createTime})
    </insert>

<!--    修改数据-->
    <update id="update"  parameterType="com.pojo.Team">
        UPDATE `team` SET teamName=#{teamName},location=#{location} WHERE teamId=#{teamId}
    </update>

<!--    删除数据-->
    <delete id="delById" parameterType="com.pojo.Team">
        DELETE FROM `mybatis`.`team` WHERE `teamId` = #{id}
    </delete>
    <delete id="delByName" parameterType="com.pojo.Team">
        DELETE  FROM `mybatis`.`team` WHERE `teamName` = #{name}
    </delete>

</mapper>

在 ==mybatis.xml配置文件== 中注册映射文件

<configuration> 
    ·····
	<!-- 注册映射文件 -->
    <mappers>
		·····
        <mapper resource="com/mapper/TeamMapper.xml"/>
    </mappers>
</configuration>

测试:

import com.mapper.TeamMapper;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;

import java.util.Date;
import java.util.List;

public class TeamMapperTest {
    
    //前提 接口的方法名 与 映射sql 标签的id值 相同!!!
    private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
    
    @Test
    public void findAll(){
        List<Team> all = teamMapper.findAll();
        all.forEach(System.out::println);
    }
    
    @Test
    public void queryById(){
        Team team = teamMapper.queryById(2);
        System.out.println("team : " + team);
    }
    
    @Test
    public void queryByName(){
        Team team = teamMapper.queryByName("火箭");
        System.out.println("team : " + team);
    }
    
    @Test
    public void add(){
        Team team = new Team("公牛","洛杉矶",new Date());
        int add = teamMapper.add(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println("add : " + add);
    }
    
    @Test
    public void update() {
        Team team = teamMapper.queryById(1009);
        team.setTeamName("老鸽");
        team.setLocation("南京");
        int update = teamMapper.update(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println("update : " + update);
    }
    
    @Test
    public void delById() {
        int i = teamMapper.delById(1009);
        MybatisUtil.getSqlSession().commit();
        System.out.println("i : " + i);
    }
    
    @Test
    public void delByName() {
        int i = teamMapper.delByName("公牛");
        MybatisUtil.getSqlSession().comit();
        System.out.println("i : " + i);
    }
    
}

# parameterType输入

# 传递多个参数

parameterType 指定值是接口中方法参数的类型,类型必须是完全限定名 或 别名。该属性非必须,因为Mybatis框架能自行判断具 体传入语句的参数

Mybatis 提供以下 3 种方式,实现给映射器传递多个参数:

  • 方法直接传递参数
  • 使用注解传递参数
  • 使用 Map传递参数

应用说明:

  • 方法直接传递参数 进行传递 不同mybatis版本传递参数 在sql应用不一样

    • mybatis3.3之前:使用 #{0}、#{1}、...
    • mybatis3.3之后:使用 #{arg0},#{arg1}、... 或者 #{param1}、#{param2}、...
  • sql语句中 的 大小于号 不能使用 ,需要 转义符 < : &lt;

    > :&gt;

  • 使用@Param注解后,只能应用 自定的注解名称

多参数应用

修改 ==TeamMapper接口== 添加sql执行的方法

package com.mapper;

import com.pojo.Team;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

public interface TeamMapper {
    ····
    
    List<Team> queryByRange1(int min , int max);
    List<Team> queryByRange2(int min , int max);
    List<Team> queryByRange3(@Param("min") int min ,@Param("max") int max);
    List<Team> queryByRange4(Map<String,Object> map);
}

修改 ==TeamMapper.xml映射文件== 添加sql语句

<!--arg 应用-->
<select id="queryByRange1" resultType="com.pojo.Team">
    select * from team where teamId>=#{arg0} and teamId &lt;= ${arg1}
</select>
<!--param 应用-->
<select id="queryByRange2" resultType="com.pojo.Team">
    select * from team where teamId>=#{param1} and teamId &lt;= ${param2}
</select>
<!--注解别名 应用-->
<select id="queryByRange3" resultType="com.pojo.Team">
    select * from team where teamId>=#{min} and teamId &lt;= ${max}
</select>
<!--Map 应用-->
<!--    传参 #{} Map集合中的 Key保持一致-->
<select id="queryByRange4" resultType="com.pojo.Team">
    select * from team where teamId>=#{min} and teamId &lt;= ${max}
</select>

测试:

import com.mapper.TeamMapper;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

//多参数传递应用
public class multiParameterTest {
    
    private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
    
    //arg 应用
    @Test
    public void test01() {
        List<Team> teamList = teamMapper.queryByRange1(1 , 6);
        teamList.forEach(team -> System.out.println(team));
    }
    
    //param 应用
    @Test
    public void tset02() {
        List<Team> teamList = teamMapper.queryByRange2(1 , 6);
        teamList.forEach(team -> System.out.println(team));
    }
    
    //接口参数名 应用 (需在接口方法中的参数定义名)
    @Test
    public void tset03() {
        List<Team> teamList = teamMapper.queryByRange3(1 , 6);
        teamList.forEach(team -> System.out.println(team));
    }
    
    //Map传递 应用
    @Test
    public void tset04() {
        Map<String,Object> map  = new HashMap<>();
        map.put("min",1);
        map.put("max",6);
        List<Team> teamList = teamMapper.queryByRange4(map);
        teamList.forEach(team -> System.out.println(team));
    }

}

# #{} 和 ${} 区别

#{}:表示一个占位符,通知Mybatis 使用实际的参数值代替

${}:表示字符串原样替换,通知Mybatis 使用 $包含的“字符串”替换所在位置

示例:

配置接口方法

public interface TeamMapper {
 	···
    Team queryByType(
        @Param("type") 
        String type,
        @Param("data") 
        Object data);
}

映射文件

<select id="queryByType" resultType="com.pojo.Team">
    select * from team where ${type}=#{data}
</select>

测试(前提库有相应信息)

@Test
public void queryByType() {
    //teamId、teamName
    Team team1 = teamMapper.queryByType("teamId",1017);
    Team team2 = teamMapper.queryByType("teamName","Sanscan12");
    System.out.println("team1 : " + team1);
    System.out.println("team2 : " + team2);
}

# resultType输出


resultType 执行 sql 得到 ResultSet 转换的类型,使用类型的完全限定名 或 别名。如果返回的是集合,设置的是集合元素的类型,而不是集合本身。resultType 和 resultMap, 不能同时使用。

可通过以下方式进行映射输出:

  • 输出java基本属性类型
  • 输出Map类型
  • 输出pojo类型
  • 输出自定义resultMap类型

应用说明:

  • resultMap 专门用于数据库中的列和实体类不匹配的情况下使用
  • resultMap 是 自己编写表中的列名 与 实体类中的属性 的映射(他们不匹配的前提下需要自行匹配映射)

应用

修改 ==TeamMapper接口== 添加sql执行的方法

package com.mapper;

import com.pojo.Team;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

public interface TeamMapper {
    ····
    
    int queryTotal();
    Map<String,Object> queryMap();
    List<Map<String, Object>> queryMapList();
    List<Team> queryAll2();
}

修改 ==TeamMapper.xml映射文件== 添加sql语句

<!--    查单列单条数据 基本类型输出-->
    <select id="queryTotal" resultType="java.lang.Integer">
        select count(teamId) from team
    </select>
<!--    查多列单条数据 Map类型输出-->
    <select id="queryMap" resultType="java.util.HashMap">
        select min(teamId) 'min',max(teamId) 'max' from team
    </select>
<!--    查多列多条数据 List<Map>类型输出-->
    <select id="queryMapList" resultType="java.util.HashMap">
        select teamName,location from team
    </select>
<!--
	resultMap是 自己编写表中的列名 与 实体类中的属性 的映射(他们不匹配的前提下需要自行匹配映射)
		id: resultMap的名称,要求唯一
		type: 期待要映射为java的类型
	id 主键列 ; result 其余列
		column: 数据库中的列名,不区分大小写
		property: 实体类中的属性名,区分大小写
		javaType: 实体类中对应的属性类型
		jdbcType: 数据库中column类型(一般忽略)
-->
<!--    resultMap数据类型 自定义映射输出-->
    <select id="queryAll2" resultMap="baseResultMap">
        select * from team
    </select>
    <resultMap id="baseResultMap" type="com.pojo.Team">
        <id column="teamId" property="teamId" javaType="java.lang.Integer"/>
        <result column="teamName" property="teamName" javaType="java.lang.String"/>
        <result column="location" property="location" javaType="java.lang.String"/>
<!--        <result column="createTime" property="createTime" javaType="java.util.Date"/>-->
    </resultMap>

测试

import com.mapper.TeamMapper;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

//多参数传递应用
public class multiParameterTest {
    
    private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
    
    //映射输出形式
    
    //查单列单条数据
    @Test
    public void queryTotal() {
        int i = teamMapper.queryTotal();
        System.out.println("i : " + i);
    }
    
    //查多列单条数据
    @Test
    public void queryMap() {
        Map<String, Object> stringObjectMap = teamMapper.queryMap();
        System.out.println(stringObjectMap);
    }
    
    //查多列多条数据
    @Test
    public void queryMapList() {
        List<Map<String, Object>> mapList = teamMapper.queryMapList();
        for (Map<String, Object> stringObjectMap : mapList) {
            System.out.println(stringObjectMap);
        }
    }
    
    //处理自定义类型数据
    @Test
    public void queryAll2() {
        List<Team> teamList = teamMapper.queryAll2();
        teamList.forEach(team -> System.out.println(team));
    }
    
}

# 动态SQL

动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,需要手动拼接 SQL 语句,避免了 在不同条件下拼接 SQL 语句的困难

动态SQL应用到的元素

  • <choose>:多条件分支

    • <when>:判断是否满足 test (boolean):表达式
    • <otherwise>:都不满足条件
  • <foreach>:遍历语句 collection (colection):遍历集合 item (Object):迭代对象 separator (String):迭代分割符 open (String):循环开头 close (String):循环结尾

  • <where>:查询约束

    • <if>:是否添加语句

      test (boolean):表达式

  • <set>:编辑约束

    • <if>:是否添加语句

      test (boolean):表达式

test属性:多个表达式定义的时候 需要使用 and、or

应用

应用前提:

  • 有库信息
  • 映射文件已注册(扫描包)

表结构

字段名 类型
deptno int
dname varchar
loc varchar

创建 ==com.pojo.Dept实体对象==

public class Dept {
    private int deptno;
    private String dname;
    private String loc;
   
    //省略 get、set、toString、构造 方法
}

创建 ==com.mapper.DeptMapper接口==

public interface DeptMapper {
    // CRUD
    int add(@Param("depts") Collection<Dept> depts);
    int del(Dept dept);
    int update(Dept dept);
    List<Dept> findByLike(Dept dept);
}

创建 ==DeptMapper.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">
<!-- namespace= "名称必须与映射的类的名字一致,是完全限定名" -->
<mapper namespace="com.sans.mapper.DeptMapper">

    <!-- CRUD 动态SQL
        列表添加 for
        删除 where=>if
        修改 set=>if
        查 choose=>...
    -->

    <!-- 列表添加 for
      sql示例:INSERT INTO dept(dname, loc) VALUES (?,?) , (?,?) [, ...]
        标签属性说明:
          collection:指定集合
          item:集合中的每个迭代对象
          separator:每次迭代之间的分隔符
          open:循环语句 以指定字符为 开头
          close:循环语句 以指定字符为 结束
    -->
    <insert id="add" parameterType="collection">
        INSERT INTO dept(dname, loc) VALUES
        <foreach collection="depts" item="dept" separator=",">
            (#{dept.dname},#{dept.loc})
        </foreach>
    </insert>

    <!-- 删除 where=>if
        sql示例1:DELETE FROM dept WHERE deptno = ?
        sql示例2:DELETE FROM dept WHERE dname = ?
        sql示例3:DELETE FROM dept WHERE deptno = ? AND dname = ?
     -->
    <delete id="del" parameterType="com.sans.pojo.Dept" >
        DELETE FROM dept
        <where>
            <if test="deptno != 0">
                AND deptno = #{deptno}
            </if>
            <if test="dname != null and dname != ''">
                AND dname = #{dname}
            </if>
        </where>
    </delete>

    <!-- 修改 set=>if
        sql示例1:UPDATE dept SET dname = ? where deptno = ?
        sql示例2:UPDATE dept SET loc = ? where deptno = ?
        sql示例3:UPDATE dept SET dname = ? , loc = ? where deptno = ?
     -->
    <update id="update" parameterType="com.sans.pojo.Dept" >
        UPDATE dept
        <set>
            <if test="dname != null and dname != ''">
                  dname = #{dname}
            </if>
            <if test="loc != null and loc != ''">
                <if test="dname != null and dname != ''">,</if>
                 loc = #{loc}
            </if>
        </set>
        where deptno = #{deptno}
    </update>

    <!-- 查 choose=>...
        sql示例1:select * from dept where 1=1 and dname like '%会%'
        sql示例2:select * from dept where 1=1 and loc like '%总%'
        sql示例3:select * from dept where 1=1 and deptno = ?
        标签说明:
            choose:指定分支
            when:条件分支节点
            otherwise:都未满足条件
    -->
    <select id="findByLike" parameterType="com.sans.pojo.Dept" resultType="com.sans.pojo.Dept">
        select * from dept where 1=1
        <choose>
            <when test="dname != null and dname != ''">
                and dname like '%${dname}%'
            </when>
            <when test="loc != null and loc != ''">
                and loc like '%${loc}%'
            </when>
            <otherwise>
                and deptno = #{deptno}
            </otherwise>
        </choose>
    </select>

    <select id="findAll" resultType="com.sans.pojo.Dept">
        select * from dept
    </select>

</mapper>

测试

package com.sans;

import com.sans.mapper.DeptMapper;
import com.sans.pojo.Dept;
import com.sans.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.*;

public class Demo {
    
    /** SQL动态查询
     *      列表添加 for
     *      删除 where => if
     *      修改 set => if
     *      查 choose => .
     */
    
    SqlSession session = MyBatisUtil.getSession();
    
    @Test
    public void addTest() {
        DeptMapper mapper = session.getMapper(DeptMapper.class);
        
        /** 集合 测试
         *  意图 : foreach标签 指定集合类型遍历区别
         *  - List 兼容
         *  - Set 兼容
         *  - Map 不兼容
         */
        
        // List 测试
        // List<Dept> list = new ArrayList<>();
        // list.add(new Dept("会计部1","汇总"));
        // list.add(new Dept("会计部2","汇总"));
        // list.add(new Dept("会计部3","汇总"));
        
        // set 测试
        Set<Dept> set = new HashSet<>();
        set.add(new Dept("会计部1","汇总"));
        // set.add(new Dept("会计部2","汇总"));
        // set.add(new Dept("会计部3","汇总"));
    
        int add = mapper.add(set);
        // session.commit();
        System.out.println("add : " + add);
    }
    
    @Test
    public void delTest() {
        DeptMapper mapper = session.getMapper(DeptMapper.class);
        Dept dept = new Dept();
        // dept.setDeptno(189);
        dept.setDname("会计部1");
        int del = mapper.del(dept);
        // session.commit();
        System.out.println("del : " + del);
    }
    
    @Test
    public void updateTest() {
        DeptMapper mapper = session.getMapper(DeptMapper.class);
        Dept d = new Dept();
        d.setDeptno(5);
        d.setDname("张三");
        d.setLoc("李四");
        int update = mapper.update(d);
        // session.commit();
        System.out.println("update : " + update);
    
    }
    
    
    @Test
    public void findByTypeTest() {
        DeptMapper mapper = session.getMapper(DeptMapper.class);
        Dept d = new Dept();
        // d.setDname("会");
        // d.setLoc("总");
        d.setDeptno(5);
        mapper.findByLike(d).forEach(System.out::println);
    }
}

# MyBatis映射关系

在MySQL中,当两表之间存在着主从关系,那么从表有个外键 对应 主表的主键 !

以下是可能出现映射关系的情况:

  • 对一 映射关系
  • 对多 映射关系

# 对一 映射关系

对一关系映射形式:

  • 一对一
  • 一对多

一对多映射接收封装方式引用 Mapper映射封装(需要搭配扩展封装 一方对象

实现应用

前提:

  • 分清对象的 主从关系 ,特别是查询的主对象
  • 全局配置文件 已注册 映射文件
  • 反向生成映射配置
  • 确定好数据表

数据库 表

Dept 部门表

字段名 类型 说明 关系键
deptno int id key
dname varchar 部门名称
loc varchar 部门地址

Emp 员工表

字段名 类型 说明 关系键
empno int id key
ename varchar 员工名
job varchar 职位
mgr int
hiredate datetime 入职时间
sal decimal 出售
comm decimal 佣金
deptno int 部门id dept.deptno
jobno int 职位id job.jobno

一对多示例

实体类Emp

public class Emp {
    private Integer empno;
    private String ename;
    private String job;
    private Integer mgr;
    private Date hiredate;
    private double sal;
    private double comm;
    private Integer deptno;
    private Integer jobno;
    
    private Dept dept;
    
    // 省略 get、set、toString、构造 方法
}

实体类Dept

public class Dept {
    private Integer deptno;
    private String dname;
    private String loc;
    
    // 省略 get、set、toString、构造 方法
}

映射接口EmpMapper

public interface EmpMapper {
    // .... 省略 反向生成的 标配CRUD操作
    
    // 内连接多查询 搭配 官方约束容器查询
    List<Emp> selectByExampleJoinDept(EmpExample example);
}

映射文件EmpMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sans.mapper.EmpMapper">
    
	<!-- .... 省略非关键代码 -->
    
    <!-- 反向生成 约束容器的代码 -->
    <sql id="Example_Where_Clause">
        <where>
            <foreach collection="oredCriteria" item="criteria" separator="or">
                <if test="criteria.valid">
                    <trim prefix="(" suffix=")" prefixOverrides="and">
                        <foreach collection="criteria.criteria" item="criterion">
                            <choose>
                                <when test="criterion.noValue">
                                    and ${criterion.condition}
                                </when>
                                <when test="criterion.singleValue">
                                    and ${criterion.condition} #{criterion.value}
                                </when>
                                <when test="criterion.betweenValue">
                                    and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                                </when>
                                <when test="criterion.listValue">
                                    and ${criterion.condition}
                                    <foreach collection="criterion.value" item="listItem" open="(" close=")"
                                             separator=",">
                                        #{listItem}
                                    </foreach>
                                </when>
                            </choose>
                        </foreach>
                    </trim>
                </if>
            </foreach>
        </where>
    </sql>

    <!-- .... 省略非关键代码 -->
    
    <!-- 手配Map映射 扩展对象进行封装 -->
    <!-- <resultMap>标签 属性说明
 		id: 自定义Map映射结果集名
		type:指定查询的主对象
	-->
    <resultMap id="EmpExpandMap" type="com.sans.pojo.Emp">
        <!-- 查询结果及映射 属性/字段 配置
			column:指定数据库查询结果的字段
			property:指定实体类属性对应的字段
			jdbcType:属性/字段 类型 (注意语法
		-->
        <id column="empno" property="empno" jdbcType="INTEGER"/>
        <result column="ename" property="ename" jdbcType="VARCHAR"/>
        <result column="job" property="job" jdbcType="VARCHAR"/>
        <result column="mgr" property="mgr" jdbcType="INTEGER"/>
        <result column="hiredate" property="hiredate" jdbcType="TIMESTAMP"/>
        <result column="sal" property="sal" jdbcType="DECIMAL"/>
        <result column="comm" property="comm" jdbcType="DECIMAL"/>
        <result column="deptno" property="deptno" jdbcType="INTEGER"/>
        <result column="jobno" property="jobno" jdbcType="INTEGER"/>
        <!-- <association>标签 属性说明
 			property:指定查询的从对象 
			javaType:指定从对象的实体对象
 		-->
        <association property="dept" javaType="com.sans.pojo.Dept">
            <id column="deptno" property="deptno" jdbcType="INTEGER"/>
            <result column="dname" property="dname" jdbcType="VARCHAR"/>
            <result column="loc" property="loc" jdbcType="VARCHAR"/>
        </association>
    </resultMap>

    <!--  应用 Emp扩展类
        sql实例1:select emp.* , dept.* from emp, dept where emp.deptno = emp.deptno
        sql实例2:select emp.* , dept.* from emp, dept WHERE ( ename like ? ) and emp.deptno = emp.deptno
    -->
    <select id="selectByExampleJoinDept" resultMap="EmpExpandMap"
            parameterType="com.sans.pojo.EmpExample">
        select emp.* , dept.*
            from emp, dept
        <!-- where 约束 -->
        <if test="_parameter != null">
            <include refid="Example_Where_Clause"/>
           	<!-- 筛选 -->
            and emp.deptno = emp.dept
        </if>
        <!-- 筛选 -->
        <if test="_parameter == null">
            where emp.deptno = emp.deptno
        </if>
    </select>

</mapper>

测试

/** 一对多测试
 *   由于 一对一 较于简单 且应用场景不多,因此不写了
 *
 *   一对多是 主要应用测试
 */

@Test
public void oneToMany() {

    EmpMapper mapper = MyBatisUtil.getSession().getMapper(EmpMapper.class);
    EmpExample example = new EmpExample();
    EmpExample.Criteria criteria = example.createCriteria();
    criteria.andEnameLike("%j%");
    List<Emp> emps = mapper.selectByExampleJoinDept(example);
    
    emps.forEach(o -> {
        System.out.println(o+" => "+o.getDept());
    });
    
}

# 对多映射关系

多对多关系之间的建立需要 第三方表的建立才能进行联系

实现应用

以下的 多对多形式分别用了 中间表查询 和 对多关系查询 两种方式

数据库 表

Dept 部门表

字段名 类型 说明 关系键
deptno int id key
dname varchar 部门名称
loc varchar 部门地址

Emp 员工表

字段名 类型 说明 关系键
empno int id key
ename varchar 员工名
job varchar 职位
mgr int
hiredate datetime 入职时间
sal decimal 出售
comm decimal 佣金
deptno int 部门id dept.deptno
jobno int 职位id job.jobno

Job 职位表

字段名 类型 说明 关系键
jobno int id key
janme varchar 职位名
remark varcahr 备注

这三张表 的关系:可以看似为 多个职位对应多个部门 ,而他们的练习包含有 员工表

实体类Emp

public class Emp {
    private Integer empno;
    private String ename;
    private String job;
    private Integer mgr;
    private Date hiredate;
    private double sal;
    private double comm;
    private Integer deptno;
    private Integer jobno;
    
    // dept 多对一关系
    private Dept beanDept;
    // job 多对一关系
    private Job beanJob;
    
    // 省略 get、set、toString、构造 方法
}

实体类Dept

public class Dept {
    private Integer deptno;
    private String dname;
    private String loc;
    
    private List<Job> jobs;
    
    // 省略 get、set、toString、构造 方法
}

实体类Job

public class Job {
    private Integer jobno;
    private String jname;
    private String remark;
    
    private List<Dept> dpets;
    
    // 省略 get、set、toString、构造 方法
}

EmpMapper接口

public interface EmpMapper {
    // .... 省略 反向生成的 标配CRUD操作
    
    // 通过中间表进行查询 两表信息
    List<Emp> selectByExampleJoinDateAndJob(EmpExample example);
}

EmpMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sans.mapper.EmpMapper">
    <resultMap id="BaseResultMap" type="com.sans.pojo.Emp">
        <id column="empno" property="empno" jdbcType="INTEGER"/>
        <result column="ename" property="ename" jdbcType="VARCHAR"/>
        <result column="job" property="job" jdbcType="VARCHAR"/>
        <result column="mgr" property="mgr" jdbcType="INTEGER"/>
        <result column="hiredate" property="hiredate" jdbcType="TIMESTAMP"/>
        <result column="sal" property="sal" jdbcType="DECIMAL"/>
        <result column="comm" property="comm" jdbcType="DECIMAL"/>
        <result column="deptno" property="deptno" jdbcType="INTEGER"/>
        <result column="jobno" property="jobno" jdbcType="INTEGER"/>
    </resultMap>
    
	<!-- .... 省略非关键代码 -->

    <!-- 通过中间表查 两表的关系 -->
    <resultMap id="EmpAndDeptAndJobMap" type="com.sans.pojo.Emp" extends="BaseResultMap">
        <association property="beanDept" javaType="com.sans.pojo.Dept">
            <id column="deptno" property="deptno" jdbcType="INTEGER"/>
            <result column="dname" property="dname" jdbcType="VARCHAR"/>
            <result column="loc" property="loc" jdbcType="VARCHAR"/>
        </association>
        <association property="beanJob" javaType="com.sans.pojo.Job">
            <id column="jobno" property="jobno" jdbcType="INTEGER"/>
            <result column="jname" property="jname" jdbcType="VARCHAR"/>
            <result column="remark" property="remark" jdbcType="VARCHAR"/>
        </association>
    </resultMap>

    <!-- 多对多 -->
    <select id="selectByExampleJoinDateAndJob" resultMap="EmpAndDeptAndJobMap"
            parameterType="com.sans.pojo.EmpExample">
        SELECT
            emp.*,dname,loc , jname,remark
        FROM
            dept,job,emp
        <if test="_parameter != null">
            <include refid="Example_Where_Clause"/>
            AND dept.deptno = emp.deptno AND job.jobno = emp.jobno
        </if>
        <if test="_parameter == null">
            WHERE dept.deptno = emp.deptno AND job.jobno = emp.jobno
        </if>
    </select>
    
</mapper>

DeptMapper

public interface DeptMapper {
    
    // .... 省略 反向生成的 标配CRUD操作
    
    // 直接多对多 查询
    List<Dept> DeptAndJobByExample(DeptExample example);
}

DeptMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sans.mapper.DeptMapper">
    <resultMap id="BaseResultMap" type="com.sans.pojo.Dept">
        <id column="deptno" property="deptno" jdbcType="INTEGER"/>
        <result column="dname" property="dname" jdbcType="VARCHAR"/>
        <result column="loc" property="loc" jdbcType="VARCHAR"/>
    </resultMap>
    
    <!-- .... 省略非关键代码 -->

    <!-- 
		<BaseResultMap>.extends: 继承已有的属性
		collection标签 属性说明
 			property:实体类中 数据集的属性名
			javaType:实体类中 属性的类型
			ofType:实体类中的 集合泛型类型
		子标签指定的都是 实体对象单条记录中的属性
 	-->
    <resultMap id="DeptAndJobsMap" type="com.sans.pojo.Dept" extends="BaseResultMap">
        <collection property="jobs" javaType="java.util.ArrayList" ofType="com.sans.pojo.Job">
            <id column="jobno" property="jobno" javaType="int"/>
            <result column="jname" property="jname" javaType="String"/>
        </collection>
    </resultMap>

    <!-- 查询部门涉及到的职位有哪些
        sql示例1:SELECT emp.*,dname,loc , jname,remark FROM dept , job , emp WHERE
                    dept.deptno = emp.deptno AND job.jobno = emp.jobno
        sql示例2:SELECT emp.*,dname,loc , jname,remark FROM dept , job , emp WHERE
                    ( dname like ? ) AND dept.deptno = emp.deptno AND job.jobno = emp.jobno
     -->
    <select id="DeptAndJobByExample" resultMap="DeptAndJobsMap"
            parameterType="com.sans.pojo.DeptExample">
        SELECT emp.*,dname,loc , jname,remark FROM
            dept , job , emp
        <if test="_parameter != null">
            <include refid="Example_Where_Clause"/>
            AND dept.deptno = emp.deptno AND job.jobno = emp.jobno
        </if>
        <if test="_parameter == null">
            WHERE dept.deptno = emp.deptno AND job.jobno = emp.jobno
        </if>
    </select>

</mapper>

JobMapper

JobMapper.xml

这两个用于铺垫使用,反向生成后,未更变!

测试

public class AnyToMany {
    
    /** 对多关系
     *   两表的对多关系 需要 第三方 中间表建立关系,因此必须包含有中间表
     *   查询方式:
     *      - 以中间表作为主表 进行对两表 连接查询
     *      - 以对多的任意一个为主表 进行对多 连接查询
     */
    
    // 中间表作为主表 进行连接查询
    @Test
    public void brokerOfFirstTableTest() {
        EmpMapper mapper = MyBatisUtil.getSession().getMapper(EmpMapper.class);
    
        // 配置约束容器
        EmpExample example = new EmpExample();
        EmpExample.Criteria criteria = example.createCriteria();
        criteria.andEnameLike("%j%");
        
        List<Emp> emps = mapper.selectByExampleJoinDateAndJob(example);
        emps.forEach(o -> {
            System.out.println(o+"\n\t"+o.getBeanDept()+"\n\t"+o.getBeanJob());
        });
        
    }
    
    // 任意表为主表 进行连接查询
    @Test
    public void anyOfFirstTableTest() {
        DeptMapper mapper = MyBatisUtil.getSession().getMapper(DeptMapper.class);
    
        // 配置约束容器
        DeptExample example = new DeptExample();
        DeptExample.Criteria criteria = example.createCriteria();
        criteria.andDnameLike("%m%");
    
        List<Dept> depts = mapper.DeptAndJobByExample(example);
        
        depts.forEach( o -> {
            System.out.println(o);
            o.getJobs().forEach(job -> {
                System.out.println("\t"+job);
            });
        });
        
    }
    
}

# MyBatis延迟加载

延迟加载在调用的时候加载的查询数据

配置

<configuration>
    ....
	<settings>
	    <!-- 日志配置 -->
	    <setting name="logImpl" value="LOG4J"/>
	
	    <!-- 打开延迟加载 的开关 -->
	    <setting name="lazyLoadingEnabled" value="true"/>
	    <!-- 将积极加载改为消极加载即按需要加载 -->
	    <setting name="aggressiveLazyLoading" value="false"/>
	    <!-- 启动二级缓存
	    <setting name="cacheEnabled" value="true"/>-->
	</settings>
    ....
</configuration>

根据以上应用 Emp 和 Dept 的关系进行应用懒加载

注意:

  • Emp实体类中 包含 一方Dept对象

SQL映射

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sans.mapper.EmpMapper">
    <resultMap id="BaseResultMap" type="com.sans.pojo.Emp">
        <id column="empno" property="empno" jdbcType="INTEGER"/>
        <result column="ename" property="ename" jdbcType="VARCHAR"/>
        <result column="job" property="job" jdbcType="VARCHAR"/>
        <result column="mgr" property="mgr" jdbcType="INTEGER"/>
        <result column="hiredate" property="hiredate" jdbcType="TIMESTAMP"/>
        <result column="sal" property="sal" jdbcType="DECIMAL"/>
        <result column="comm" property="comm" jdbcType="DECIMAL"/>
        <result column="deptno" property="deptno" jdbcType="INTEGER"/>
        <result column="jobno" property="jobno" jdbcType="INTEGER"/>
    </resultMap>
    
    <!-- ... 省略自动生成的非关键代码 -->

    <!-- 懒加载测试 -->
    <resultMap id="lazyLoadingMap" type="com.sans.pojo.Emp" extends="BaseResultMap">
        <association property="dept" column="deptno" javaType="com.sans.pojo.Dept"
            select="com.sans.mapper.DeptMapper.selectByPrimaryKey">
            <id column="deptno" property="deptno" javaType="int"/>
            <result column="dname" property="dname" javaType="String"/>
            <result column="loc" property="loc" javaType="String"/>
        </association>
    </resultMap>

    <select id="lazyLoadingSelectAll" resultMap="lazyLoadingMap">
        select * from emp
    </select>

</mapper>

以下是测试懒加载的效果实例

无懒加载

DEBUG [main] - ==>  Preparing: select * from emp
DEBUG [main] - ==> Parameters: 
DEBUG [main] - ====>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 20(Integer)
DEBUG [main] - <====      Total: 1
DEBUG [main] - ====>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 30(Integer)
DEBUG [main] - <====      Total: 1
DEBUG [main] - ====>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 47(Integer)
DEBUG [main] - <====      Total: 1
DEBUG [main] - ====>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 10(Integer)
DEBUG [main] - <====      Total: 1
DEBUG [main] - ====>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 13(Integer)
DEBUG [main] - <====      Total: 1
DEBUG [main] - ====>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 14(Integer)
DEBUG [main] - <====      Total: 1
DEBUG [main] - ====>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 141(Integer)
DEBUG [main] - <====      Total: 1
DEBUG [main] - ====>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 140(Integer)
DEBUG [main] - <====      Total: 1
DEBUG [main] - ====>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 164(Integer)
DEBUG [main] - <====      Total: 1
DEBUG [main] - ====>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 167(Integer)
DEBUG [main] - <====      Total: 1
DEBUG [main] - ====>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 1(Integer)
DEBUG [main] - <====      Total: 1
DEBUG [main] - <==      Total: 43
张小三 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
张小 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
李四 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
旺财2 ==> Dept{deptno=47, dname='学术部', loc='B区'} => 学术部
zhansan ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
zhansan2 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
wangwu ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
Jason ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
王五 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
旺财 ==> Dept{deptno=47, dname='学术部', loc='B区'} => 学术部
王小二 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
kkkkk ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
j1 ==> Dept{deptno=13, dname='bb', loc='b'} => bb
j3 ==> Dept{deptno=14, dname='cc', loc='b'} => cc
j4 ==> Dept{deptno=141, dname='cqc', loc='b'} => cqc
j5 ==> Dept{deptno=140, dname='cqc', loc='b'} => cqc
j6 ==> Dept{deptno=141, dname='cqc', loc='b'} => cqc
hhh ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
king2 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
mm ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
旺 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
李 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
null ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
jjj ==> Dept{deptno=164, dname='m1', loc='m1'} => m1
mmm ==> Dept{deptno=164, dname='m1', loc='m1'} => m1
jjj2 ==> Dept{deptno=167, dname='m2', loc='m1'} => m2
mmm2 ==> Dept{deptno=167, dname='m2', loc='m1'} => m2
aaa ==> Dept{deptno=47, dname='学术部', loc='B区'} => 学术部
SMITH ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
ALLEN ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
WARD ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
JONES ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
MARTIN ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
BLAKE ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
CLARK ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
SCOTT ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
KING ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
TURNER ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
ADAMS ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
JAMES ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
FORD ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
MILLER ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
赵六 ==> Dept{deptno=1, dname='会计部', loc='海珠区'} => 会计部

懒加载

DEBUG [main] - ==>  Preparing: select * from emp
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 43
DEBUG [main] - ==>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 20(Integer)
DEBUG [main] - <==      Total: 1
张小三 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
张小 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
DEBUG [main] - ==>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 30(Integer)
DEBUG [main] - <==      Total: 1
李四 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
DEBUG [main] - ==>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 47(Integer)
DEBUG [main] - <==      Total: 1
旺财2 ==> Dept{deptno=47, dname='学术部', loc='B区'} => 学术部
zhansan ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
zhansan2 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
wangwu ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
DEBUG [main] - ==>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 10(Integer)
DEBUG [main] - <==      Total: 1
Jason ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
王五 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
旺财 ==> Dept{deptno=47, dname='学术部', loc='B区'} => 学术部
王小二 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
kkkkk ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
DEBUG [main] - ==>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 13(Integer)
DEBUG [main] - <==      Total: 1
j1 ==> Dept{deptno=13, dname='bb', loc='b'} => bb
DEBUG [main] - ==>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 14(Integer)
DEBUG [main] - <==      Total: 1
j3 ==> Dept{deptno=14, dname='cc', loc='b'} => cc
DEBUG [main] - ==>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 141(Integer)
DEBUG [main] - <==      Total: 1
j4 ==> Dept{deptno=141, dname='cqc', loc='b'} => cqc
DEBUG [main] - ==>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 140(Integer)
DEBUG [main] - <==      Total: 1
j5 ==> Dept{deptno=140, dname='cqc', loc='b'} => cqc
j6 ==> Dept{deptno=141, dname='cqc', loc='b'} => cqc
hhh ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
king2 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
mm ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
旺 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
李 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
null ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
DEBUG [main] - ==>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 164(Integer)
DEBUG [main] - <==      Total: 1
jjj ==> Dept{deptno=164, dname='m1', loc='m1'} => m1
mmm ==> Dept{deptno=164, dname='m1', loc='m1'} => m1
DEBUG [main] - ==>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 167(Integer)
DEBUG [main] - <==      Total: 1
jjj2 ==> Dept{deptno=167, dname='m2', loc='m1'} => m2
mmm2 ==> Dept{deptno=167, dname='m2', loc='m1'} => m2
aaa ==> Dept{deptno=47, dname='学术部', loc='B区'} => 学术部
SMITH ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
ALLEN ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
WARD ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
JONES ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
MARTIN ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
BLAKE ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
CLARK ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
SCOTT ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
KING ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
TURNER ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
ADAMS ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
JAMES ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
FORD ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
MILLER ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
DEBUG [main] - ==>  Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
赵六 ==> Dept{deptno=1, dname='会计部', loc='海珠区'} => 会计部

# MyBatis缓存

MyBatis缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力。

将经常查询的数据存在缓存(内存)中,用户查询该数据的时候不需要从库上 查询,而是直接从缓存中查询,提高查询效率,解决高并发问题。 MyBatis 也有一级缓存 和 二级缓存,并且预留了集成 第三方缓存的接口

# 一级缓存

Mybatis自带的 缓存 ,在构造sqlSession对象时内部有个 HashMap 结构存储缓存数据,它的作用域范围是 sqlSession 。如果两次查询用同一个sqlSession进行查询语句,则第一次会通过数据库获取到数据库 ,二次会缓存中获取数据!

缓存清除条件:

  • session.clearCache()
  • session.close()
  • 执行 增删改
  • 事务回滚
  • 事务提交

应用实例

import com.pojo.Team;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class CacheTest {
    
    private SqlSession sqlSession = MybatisUtil.getSqlSession();
    
    @Test
    public void test01() {
        Team t1 = sqlSession.selectOne("com.mapper.TeamMapper.queryById",1003);
        System.out.println("t1 : " + t1);
        Team t2 = sqlSession.selectOne("com.mapper.TeamMapper.queryById",1003);
        System.out.println("t2 : " + t2);
        
        MybatisUtil.close();
        
        //换 sqlSession ,刷新缓存
        sqlSession = MybatisUtil.getSqlSession();
        Team t3 = sqlSession.selectOne("com.mapper.TeamMapper.queryById",1003);
        System.out.println("t3 : " + t3);
        
        int num = sqlSession.delete("com.mapper.TeamMapper.delById",1000);
        sqlSession.commit();
        System.out.println("num : " + num);
        
        Team t4 = sqlSession.selectOne("com.mapper.TeamMapper.queryById",1003);
        sqlSession.close();
    }
    
}

/* 运行结果

DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - ==>  Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1003(Integer)
DEBUG [main] - <==      Total: 1
t1 : Team{teamId=1003, teamName='老八', location='温州', createTime=Wed Feb 17 00:00:00 CST 2021}
t2 : Team{teamId=1003, teamName='老八', location='温州', createTime=Wed Feb 17 00:00:00 CST 2021}
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - Returned connection 527829831 to pool.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 527829831 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - ==>  Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1003(Integer)
DEBUG [main] - <==      Total: 1
t3 : Team{teamId=1003, teamName='老八', location='温州', createTime=Wed Feb 17 00:00:00 CST 2021}
DEBUG [main] - ==>  Preparing: DELETE FROM `mybatis`.`team` WHERE `teamId` = ?
DEBUG [main] - ==> Parameters: 1000(Integer)
DEBUG [main] - <==    Updates: 0
DEBUG [main] - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
num : 0
DEBUG [main] - ==>  Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1003(Integer)
DEBUG [main] - <==      Total: 1
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - Returned connection 527829831 to pool.

*/

# 二级缓存

MyBatis 二级缓存是全局缓存,作用域超出 SqlSession 范围之外,其作用域是 mapper 的同一命名空间!

两个不同的sqlSession在同一 命名空间 下,执行的sql语句参数相同 ,最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据,从而提高性能!

应用实例

修改配置文件 ==mybatis.xml==

···
	<settings>
	    <!--    日志配置-->
	    <setting name="logImpl" value="LOG4J"/>
	    <!--    是否启动二级缓存-->
	    <setting name="cacheEnabled" value="true"/>
	</settings>
···

修改映射文件 ==TeamMapper.xml==

<mapper namespace="com.mapper.TeamMapper">
<!--   二级缓存标签-->
    <cache></cache>
	····    
<mapper/>

实体类实现 ==Serializable接口==

package com.pojo;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class Team implements Serializable {
    
    private Integer teamId;
    private String teamName;
    private String location;
    private Date createTime;
    
    //一对多的体现:一方持有多方的对象
    private List<Player> playerList1;
    private List<Player> playerList2;
  	
    //省略 set、get、toString、构造 方法
    
}

测试

import com.mapper.TeamMapper;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class CacheTest {
    
    @Test
    public void test02() {
        //查 数据 保留缓存
        SqlSession sqlSession1 = MybatisUtil.getSqlSession();
        Team t1 = sqlSession1.selectOne("com.mapper.TeamMapper.queryById" , 1019);
        System.out.println("t1 : " + t1);
        //清空一级缓存,保留二级缓存
        MybatisUtil.close();
    
        //测试 是否保留二级缓存
        SqlSession sqlSession2 = MybatisUtil.getSqlSession();
        Team t2 = sqlSession2.selectOne("com.mapper.TeamMapper.queryById" , 1019);
        System.out.println("t2 : " + t2);
        MybatisUtil.close();
    
        //测试 删除数据 清空二级缓存
        SqlSession sqlSession3 = MybatisUtil.getSqlSession();
        //删除不存在的数据(假删除)
        int i = sqlSession3.delete("com.mapper.TeamMapper.delById" , 10000);
        System.out.println("i : " + i);
        sqlSession3.commit();
        MybatisUtil.close();
    
        //测试 是否保留二级缓存
        SqlSession sqlSession4 = MybatisUtil.getSqlSession();
        Team t4 = sqlSession4.selectOne("com.mapper.TeamMapper.queryById" , 1019);
        System.out.println("t4 : " + t4);
        MybatisUtil.close();
        
    }
    
}

/*运行结果
    
DEBUG [main] - Created connection 292138977.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - ==>  Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1019(Integer)
DEBUG [main] - <==      Total: 1
t1 : Team{teamId=1019, teamName='哥斯拉', location='华盛顿', createTime=Sun Jul 25 00:00:00 CST 2021}
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Returned connection 292138977 to pool.
 WARN [main] - 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
DEBUG [main] - Cache Hit Ratio [com.mapper.TeamMapper]: 0.5
t2 : Team{teamId=1019, teamName='哥斯拉', location='华盛顿', createTime=Sun Jul 25 00:00:00 CST 2021}
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 292138977 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - ==>  Preparing: DELETE FROM `mybatis`.`team` WHERE `teamId` = ?
DEBUG [main] - ==> Parameters: 10000(Integer)
DEBUG [main] - <==    Updates: 0
i : 0
DEBUG [main] - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Returned connection 292138977 to pool.
DEBUG [main] - Cache Hit Ratio [com.mapper.TeamMapper]: 0.3333333333333333
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 292138977 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - ==>  Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1019(Integer)
DEBUG [main] - <==      Total: 1
t4 : Team{teamId=1019, teamName='哥斯拉', location='华盛顿', createTime=Sun Jul 25 00:00:00 CST 2021}
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Returned connection 292138977 to pool.
    
*/

# 二级缓存其他设置

映射文件中的cache标签

···	
	<!--二级缓存默认配置-->
	<cache>
		<property name="eviction" value="LRU"/><!--回收策略为LRU-->
		<property name="flushInterval" value="60000"/><!--自动刷新时间间隔为60S-->
		<property name="size" value="1024"/><!--最多缓存1024个引用对象-->
		<property name="readOnly" value="true"/><!--只读-->
	</cache>
···
属性 说明
eviction 代表的是缓存回收策略,目前 MyBatis 提供以下策略。
LRU:使用较少,移除最长时间不用的对象;
FIFO:先进先出,按对象进入缓存的顺序来移除它们;
SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象;
WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象
flushInterval 刷新间隔时间,单位为毫秒,这里配置的是 100 秒刷新,如果省略该配置,那么只有当 SQL 被执行的时候才会刷新缓存
size 引用数目,正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。这里配置的是 1024 个对象
readOnly 只读,默认值为 false,意味着缓存数据只能读取而不能修改,这样设置的好处是可以快速读取缓存,缺点是没有办法修改缓存

重用cache标签配置

在命名空间中共享相同的缓存配置和实例,可以使用cache-ref 元素来引用另外一个缓存。引用实例:

···
	<!--需要指定 映射文件的位置-->
	<cache-ref namespace="com.mapper.TeamMapper" />
···

# 反向工程

Mybatis 提供逆向生成工具,该工具可以根据 数据库中的表 自动生成单表的 pojo 类、mapper 映射文件和 mapper 接口。大大缩减了开发时间!!(应用前提,不同库表名不建议写相同!)

MyBatis反向生成方式有多种,本次引用了:项目生成 和 Maven项目工具生成

# 项目生成

项目到手直接配置 generatorConfig.xml 该文件即可,我们只需要关注以下的配置信息

  • <jdbcConnection>:JDBC 库连接
    • **driverClass:**驱动加载
    • **connectionURL:**连接URL
    • **userId:**账号
    • **password:**密码
  • <javaModelGenerator>.targetPackage:指定生成 实体类路径
  • <sqlMapGenerator>.targetPackage:指定生成 映射文件路径
  • <javaClientGenerator>.targetPackage:指定生成 mapper接口路径
  • <table>.tableName:指定 反向生成 的数据库 表

对以上标签详细了解以及配置的信息,可在一下jar包路径了解详细: ==mybatis-generator-core-1.3.2.jar\org\mybatis\generator\config\xml\mybatis-generator-config_1_0.dtd==

依赖包

  • log4j-1.2.16.jar(日志
  • mybatis-3.5.6.jar(mybatis
  • mybatis-generator-core-1.3.2.jar (反向工程
  • mysql-connector-java-8.0.16.jar(jdbc

项目生成问题:

  • 反向生成项目 不能通过子项目进行应用该项目,否则 反向生成找不到 src 根路径
  • 反向生成项目,配置 指定的包名路径 必须与应用的项目一致 (实例和映射的路径

示例:

generatorConfig.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>
   <context id="testTables" targetRuntime="MyBatis3">
      <commentGenerator>
         <!-- 是否去除自动生成的注释 true:是 : false:否 -->
         <property name="suppressAllComments" value="true" />
      </commentGenerator>
      <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
       
      <jdbcConnection
            driverClass="com.mysql.cj.jdbc.Driver"
         connectionURL="jdbc:mysql://127.0.0.1:3306/test?
useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=GMT"
         userId="root"
         password="root">
      </jdbcConnection>
     
      <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 
         NUMERIC 类型解析为java.math.BigDecimal -->
      <javaTypeResolver>
         <property name="forceBigDecimals" value="false" />
      </javaTypeResolver>

      <!-- targetProject:生成POjO类的位置 -->
      <javaModelGenerator targetPackage="com.sans.pojo"
         targetProject=".\src">
         <!-- enableSubPackages:是否让schema作为包的后缀 -->
         <property name="enableSubPackages" value="false" />
         <!-- 从数据库返回的值被清理前后的空格 -->
         <property name="trimStrings" value="true" />
      </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
      <sqlMapGenerator targetPackage="com.sans.mapper"
         targetProject=".\src">
         <!-- enableSubPackages:是否让schema作为包的后缀 -->
         <property name="enableSubPackages" value="false" />
      </sqlMapGenerator>
      <!-- targetPackage:mapper接口生成的位置 -->
      <javaClientGenerator type="XMLMAPPER"
         targetPackage="com.sans.mapper"
         targetProject=".\src">
         <!-- enableSubPackages:是否让schema作为包的后缀 -->
         <property name="enableSubPackages" value="false" />
      </javaClientGenerator>

      <!-- 指定数据库表    -->
      <table tableName="emp">
         <!-- 大小写也一起搬 -->
         <property name="useActualColumnNames" value="true"/>
      </table>
      <table tableName="dept">
         <property name="useActualColumnNames" value="true"/>
      </table>
       
      <!-- 有些表的字段需要指定java类型
       <table schema="" tableName="">
         <columnOverride column="" javaType="" />
      </table> -->
   </context>
</generatorConfiguration>

GeneratorSqlmap类 执行入口

import java.io.File;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

public class GeneratorSqlmap {
   
   // 指定 逆向工程配置文件
   private static String configName = "generatorConfig.xml";
   
   public void generator() throws Exception{

      List<String> warnings = new ArrayList<>();
      boolean overwrite = true;
      
      // 通过类加载的路径进入查找文件
      // 中文路径转义
      String decode = URLDecoder.decode(this.getClass().getClassLoader().getResource(configName).getFile() , "utf-8");
      System.out.println("decode : " + decode);
      File configFile = new File(decode);
      
      ConfigurationParser cp = new ConfigurationParser(warnings);
      Configuration config = cp.parseConfiguration(configFile);
      DefaultShellCallback callback = new DefaultShellCallback(overwrite);
      MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
            callback, warnings);
      myBatisGenerator.generate(null);

   } 
   public static void main(String[] args) {
      try {
         // 直接指定路径可能会有问题
         new GeneratorSqlmap().generator();
      } catch (Exception e) {
         e.printStackTrace();
      }
      
   }

}

执行成功会在指定的路径生成出文件

反向工程应用

直接上测试代码,就不多bb

package com.sans;

import com.sans.mapper.DeptMapper;
import com.sans.pojo.Dept;
import com.sans.pojo.DeptExample;
import com.sans.utils.MyBatisUtil;
import org.junit.Test;

import java.util.List;

public class Demo {
    
    /** 反向工程 单表应用测试
     *  已 Dept对象 示例
     *  反向生成的接口方法
     *     // 查询所有条数 (带约束
     *     int countByExample(DeptExample example);
     *     // 删除指定数据 (带约束
     *     int deleteByExample(DeptExample example);
     *     // 删除指定id
     *     int deleteByPrimaryKey(Integer deptno);
     *     // 添加数据 (完全字段添加
     *     int insert(Dept record);
     *     // 添加数据 (选择添加字段
     *     int insertSelective(Dept record);
     *     // 查询 约束
     *     List<Dept> selectByExample(DeptExample example);
     *     // 查询 指定id
     *     Dept selectByPrimaryKey(Integer deptno);
     *     // 修改 约束 选择性
     *     int updateByExampleSelective(@Param("record") Dept record, @Param("example") DeptExample example);
     *     // 修改 约束
     *     int updateByExample(@Param("record") Dept record, @Param("example") DeptExample example);
     *     // 修改 指定id 选择性
     *     int updateByPrimaryKeySelective(Dept record);
     *     // 修改 指定id
     *     int updateByPrimaryKey(Dept record);
     *
     *     以上方法 共同信息: id、约束、选择性
     *
     *     本次测试围绕:实体约束对象的使用 和 对象属性选择的操作 和 基本的CRUD
     */

    DeptMapper mapper = MyBatisUtil.getSession().getMapper(DeptMapper.class);
    
    // 查询 条数
    // sql示例:select count(*) from dept
    @Test
    public void countTest() {
        int i = mapper.countByExample(null);
        System.out.println("i : " + i);
    }
    
    // 查询 普通
    // sql示例:select deptno, dname, loc from dept
    @Test
    public void findTest() {
        List<Dept> depts = mapper.selectByExample(null);
        depts.forEach(System.out::println);
    }
    
    // 查询 约束
    // sql示例:select deptno, dname, loc from dept WHERE ( deptno = 44 ) or( loc like '天河' )
    @Test
    public void findByExampleTest() {
        DeptExample example = new DeptExample();
        // 约束容器
        DeptExample.Criteria criteria = example.createCriteria();
        DeptExample.Criteria criteria2 = example.or();
        criteria.andDeptnoEqualTo(44);
        criteria2.andLocLike("天河");
        
        List<Dept> depts = mapper.selectByExample(example);
        depts.forEach(System.out::println);
    }
    
    // 添加
    // sql示例:insert into dept (deptno, dname, loc ) values (null, '会计部1', '汇总' )
    @Test
    public void addTest() {
        Dept dept = new Dept();
        dept.setDname("会计部1");
        dept.setLoc("汇总");
        int i = mapper.insert(dept);
        System.out.println(" ["+i+"] ");
    }
    
    // 添加 选择
    // sql示例:insert into dept ( loc ) values ( '汇总' )
    @Test
    public void addSSelectiveTest() {
        Dept dept = new Dept();
        dept.setLoc("汇总");
        int i = mapper.insertSelective(dept);
        System.out.println(" ["+i+"] ");
    }
    
    // 修改 约束 选择
    // sql示例:update dept SET dname = '张三', loc = '张三2' WHERE ( deptno = 50 )
    @Test
    public void updateTest() {
        Dept dept = new Dept();
        dept.setDname("张三");
        dept.setLoc("张三2");
        DeptExample example = new DeptExample();
        DeptExample.Criteria criteria = example.createCriteria();
        criteria.andDeptnoEqualTo(50);
        int i = mapper.updateByExampleSelective(dept , example);
        System.out.println(" ["+i+"] ");
    }
    
}

# Maven项目

Maven反向生成 需要在 ==pom.xml== 中配置 ==org.mybatis.generator== 插件工具进行生成

应用实现

库数据引入&展示 (库名mybatis)

CREATE TABLE `team` (
  `teamId` int NOT NULL AUTO_INCREMENT COMMENT '球队ID',
  `teamName` varchar(50) DEFAULT NULL COMMENT '球队名称',
  `location` varchar(50) DEFAULT NULL COMMENT '球队位置',
  `createTime` date DEFAULT NULL COMMENT '球队建立时间',
  PRIMARY KEY (`teamId`)
) ENGINE=InnoDB AUTO_INCREMENT=1026 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;


CREATE TABLE `player` (
  `playerId` int NOT NULL,
  `playerName` varchar(100) DEFAULT NULL,
  `playerNum` int DEFAULT NULL,
  `teamId` int DEFAULT NULL,
  PRIMARY KEY (`playerId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `gamerecord` (
  `recordId` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `homeTeamId` int DEFAULT NULL COMMENT ' 主队id',
  `gameDate` datetime DEFAULT NULL COMMENT '比赛时间',
  `score` int DEFAULT NULL COMMENT '得分',
  `visitingTeamId` int DEFAULT NULL COMMENT '客队id',
  PRIMARY KEY (`recordId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

配置文件 ==pom.xml==

<?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">
	
    ·····
    <dependencies>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.23</version>
        </dependency>
        <!--测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!--mybatis日志 依赖-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    	<!--xml解析 依赖-->
        <dependency>
            <groupId>org.codehaus.plexus</groupId>
            <artifactId>plexus-classworlds</artifactId>
            <version>2.5.2</version>
        </dependency>
        <!--xml解析 依赖-->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <!--反向生成插件-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>
                <configuration>
                    <!--配置文件的路径-->
                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                    <overwrite>true</overwrite>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.5</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

创建逆向生成配置文件 ==resources/generatorConfig.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>
    <!--1、数据库驱动jar:添加自己的jar路径 -->
    <classPathEntry
            location="D:\Maven\repository\mysql\mysql-connector-java\8.0.23\mysql-connector-java-8.0.23.jar" />
    <context id="MyBatis" targetRuntime="MyBatis3">
        <!--去除注释 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--2、数据库连接 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=GMT"
                        userId="root"
                        password="root">
        </jdbcConnection>
        <!--
        默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer;
        为 true时把JDBC DECIMAL和NUMERIC类型解析为java.math.BigDecimal
            false: Integer
            true: BigDecimal (双精度浮点型变量double可以处理16位有效数)
         -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
        <!--3、生成实体类 指定包名 以及生成的地址 (可以自定义地址,但是路径不存在不会自动创建
        使用Maven生成在target目录下,会自动创建) -->
        <javaModelGenerator targetPackage="com.pojo"
                            targetProject="src\main\java">
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!--4、生成SQLmapper.xml映射文件 -->
        <sqlMapGenerator targetPackage="com.mapper"
                         targetProject="src\main\resources">
        </sqlMapGenerator>
        <!--5、生成Dao(Mapper)接口文件,-->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.mapper"
                             targetProject="src\main\java">
        </javaClientGenerator>
        <!--6、要生成哪些表(更改tableName和domainObjectName就可以) -->
        <!-- tableName:要生成的表名
        enableCountByExample:Count语句中加入where条件查询,默认为true开启
        enableUpdateByExample:Update语句中加入where条件查询,默认为true开启
        enableDeleteByExample:Delete语句中加入where条件查询,默认为true开启
        enableSelectByExample:Select多条语句中加入where条件查询,默认为true开启
        selectByExampleQueryId:Select单个对象语句中加入where条件查询,默认为true开启
        -->
<!--        <table tableName="Team"-->
<!--               enableCountByExample="false"-->
<!--               enableUpdateByExample="false"-->
<!--               enableUpdateByPrimaryKey="false"-->
<!--               enableDeleteByExample="false"-->
<!--               enableDeleteByPrimaryKey="false"-->
<!--               enableSelectByExample="false"-->
<!--               selectByExampleQueryId="false">-->
<!--            <property name="useActualColumnNames" value="true"/>-->
<!--        </table>-->
        <table tableName="team">
            <!--应用 库中对应的名称 (大小写也搬过去)-->
            <property name="useActualColumnNames" value="true"/>
        </table>
        <table tableName="player">
            <property name="useActualColumnNames" value="true"/>
        </table>
        <table tableName="gamerecord">
            <property name="useActualColumnNames" value="true"/>
        </table>
    </context>
</generatorConfiguration>

Maven插件 反向生成 插件命令行:==mybatis-generator:generate==

测试(应用测试主要部分,多余就不赘述了)

import com.mapper.TeamMapper;
import com.pojo.Team;
import com.pojo.TeamExample;
import com.utils.MybatisUtil;
import org.junit.Test;

import java.util.Date;
import java.util.List;

public class TeamTest {

    private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
    
    //查 主键
    @Test
    public void test01() {
        Team team = teamMapper.selectByPrimaryKey(1019);
        System.out.println("team : " + team);
    }
    
    //查总数 无约束
    @Test
    public void test02() {
        
        TeamExample example = new TeamExample();
        //查总数
        long l = teamMapper.countByExample(example);
        System.out.println("l : " + l);
    
    }
    
    //添加
    @Test
    public void test03() {
    
        Team team = new Team();
        team.setTeamName("bozhu-test");
        int insert = teamMapper.insert(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println("insert : " + insert);
    }
    
    //动态添加
    @Test
    public void test04() {
        Team team = new Team();
        team.setTeamName("bozhu-test2");
        int insert = teamMapper.insertSelective(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println("insert : " + insert);
    }
    
    //修改 指定key
    @Test
    public void test05() {
        Team team = teamMapper.selectByPrimaryKey(1026);
        team.setTeamName("老哥");
        team.setLocation("bj");
        team.setCreateTime(new Date());
        int i = teamMapper.updateByPrimaryKey(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println("i : " + i);
    }
    
    //Example约束 应用
    
    /**
     * Example 约束服务
     *      criteria1容器 约束方法名
     *          add+约束列+约束方式
     *          ····
     *      OR:criteria2容器 约束方法名
     *          add+约束列+约束方式
     *          ····
     */
    @Test
    public void test06() {
        //设置多条件 服务
        TeamExample example = new TeamExample();
        // criteria 多条件 容器
        TeamExample.Criteria criteria = example.createCriteria();
        // or 添加’或‘运算符 约束
        TeamExample.Criteria criteria2 = example.or();
        //为容器添加条件
        criteria.andTeamIdBetween(1001,1100);
        criteria2.andTeamIdBetween(1,3);
        List<Team> teamList = teamMapper.selectByExample(example);
        teamList.forEach(team -> System.out.println(team));
    }
}

# 分页插件

Mybatis分页查询,可直接 通过插件进行 快速实现分页功能!

在配置中添加插件一定一定要遵循标签的顺序 查看顺序

依赖包:

  • jsqlparser-0.9.5.jar
  • pagehelper-4.2.1.jar

Mybatis全局配置插件

<configuration>
	...
    <plugins>
        <!-- com.github.pagehelper为PageHelper类所在包名 -->
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql" />
            <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
            <!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
            <!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
            <property name="reasonable" value="true" />
        </plugin>
    </plugins>
    ...
</configuration>

测试

@Test
public void test() {
    EmpMapper mapper = MyBatisUtil.getSession().getMapper(EmpMapper.class);
    
    int pageNum = 2;
    int pageSize = 5;
    
    // 以下分页代码必须定义在 sql查询前的语句,否则分页查询会失效
    PageHelper.startPage(pageNum , pageSize);
    List<Emp> emps = mapper.selectByExample(null);
    
    emps.forEach(System.out::println);
    
    PageInfo<Emp> page = new PageInfo<>(emps);
    System.out.println("总记录数:" + page.getTotal());
    System.out.println("总页数:" + page.getPages());
    System.out.println("上页:" + page.getPrePage());
    System.out.println("下页:" + page.getNextPage());
}

# MyBatis问题

# 线程优化

ThreadLocal

ThreadLocal是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据

SqlSession线程是不安全的线程,利用ThreadLocal实现多线程也能独立完成业务,防止SqlSession同时访问并发问题

ThreadLocal可看作集合容器 ,里面存放的都是唯一且都是相同的副本,里面存储的对象都是独立的

ThreadLocal不是一个线程的本地实现版本,也不是一个Thread。ThreadLocal是为每一个使用该变量的线程都提供一个变量值的副本, 是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突

实现应用

项目结构(在原有的基础上优化)

  .
  |
  ├── src
  |    ├── main
  |	   |	├── java
  |	   |	|	  └── com
  |	   |    |   	   ├── dao
  |    |    |          |	├── TeamDao
  |    |    |          |    └── TeamDaoImpl
  |	   |	|	  	   ├── pojo
  |	   |	|	  	   |	├── Team
  |	   |	|		   |	└── Team.xml
  |	   |    | 		   └── utils		
  |	   |    |    	   		└── MybatisUtil
  |	   |	└──	resources
  |	   |		    ├── log4j.properties
  |	   |			└── mybatis.xml
  |	  test
  |    └── ...
  └── pom.xml

添加映射SQL ==Team.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">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.pojo.Team">

    <!--
    id="自定义名称,id不能重复;相当于dao中的方法名称"
    resultType="使用的要求:实体类中的属性名与表中的列名一致"
    -->
<!--    查询所有-->
    <select id="findAll" resultType="com.pojo.Team">
        select * from team;
    </select>

<!--    查询指定-->
    <select id="findId" resultType="com.pojo.Team">
        select * from team where teamId=#{teamId}
    </select>
    <select id="findName" resultType="com.pojo.Team">
        select * from team where teamName=#{teamName}
    </select>

    <!--
    parameterType="指定对象作为参数"
    #{对象属性名}
    -->
<!--    添加数据-->
    <insert id="add" parameterType="com.pojo.Team">
        INSERT INTO `mybatis`.`team`(`teamName`, `location`, `createTime`) VALUES (#{teamName}, #{location}, #{createTime})
    </insert>

<!--    修改数据-->
    <update id="updateById"  parameterType="com.pojo.Team">
        UPDATE `team` SET teamName=#{teamName},location=#{location} WHERE teamId=#{teamId}
    </update>

<!--    删除数据-->
    <delete id="deleteById" parameterType="com.pojo.Team">
        DELETE FROM `mybatis`.`team` WHERE `teamId` = #{id}
    </delete>
    <delete id="deleteByName" parameterType="com.pojo.Team">
        DELETE  FROM `mybatis`.`team` WHERE `teamName` = #{name}
    </delete>

</mapper>

工具类实现 ==MybatisUtil== (应用主要)

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

public class MybatisUtil {
    
    private static SqlSessionFactory sqlSessionFactory;
    private static ThreadLocal<SqlSession> sqlSessionThreadLocal = new ThreadLocal<>();
    
    static {
        try {
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = sqlSessionThreadLocal.get();
        if (sqlSession == null){
            sqlSession = sqlSessionFactory.openSession();
            sqlSessionThreadLocal.set(sqlSession);
        }
        return sqlSession;
    }
    
    public static void close() {
        SqlSession sqlSession = sqlSessionThreadLocal.get();
        if (sqlSession != null) {
            sqlSession.close();
            sqlSessionThreadLocal.remove();
        }
    }
    
}

实现dao,创建 ==TeamDaoImpl接口==

package com.dao;
import com.pojo.Team;
import java.util.List;

public interface TeamDaoImpl {
    public List<Team> findAll();
    public Team queryById(int id);
    public Team queryByName(String name);
    public int add(Team team);
    public int updateById(int id , Team newTeam);
    public int updateByName(String name , Team newTeam);
    public int delById(int id);
    public int delByName(String name);
}

创建 ==TeamDao实现类== (业务实现)

package com.dao;

import com.pojo.Team;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class TeamDao implements TeamDaoImpl{
    
    @Override
    public List<Team> findAll() {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        List<Team> teams = sqlSession.selectList("com.pojo.Team.findAll");
        sqlSession.commit();
        return teams;
    }
    
    @Override
    public Team queryById(int id) {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        return sqlSession.selectOne("com.pojo.Team.queryById" , id);
    }
    
    @Override
    public Team queryByName(String name) {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        return sqlSession.selectOne("com.pojo.Team.queryByName" , name);
    }
    
    @Override
    public int add(Team team) {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        int num = sqlSession.insert("com.pojo.Team.add",team);
        sqlSession.commit();
        return num;
    }
    
    @Override
    public int updateById(int id ,Team newTeam) {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        Team team = sqlSession.selectOne("com.pojo.Team.queryById" , id);
        team.setTeamName(newTeam.getTeamName());
        team.setLocation(newTeam.getLocation());
        int num = sqlSession.update("com.pojo.Team.updateById" , team);
        sqlSession.commit();
        return num;
    }
    
    @Override
    public int updateByName(String name , Team newTeam) {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        Team team = sqlSession.selectOne("com.pojo.Team.queryByName" , name);
        team.setTeamName(newTeam.getTeamName());
        team.setLocation(newTeam.getLocation());
        int num = sqlSession.update("com.pojo.Team.updateById" , team);
        sqlSession.commit();
        return num;
    }
    
    @Override
    public int delById(int id) {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        int num = sqlSession.delete("com.pojo.Team.deleteById" , id);
        sqlSession.commit();
        return num;
    }
    
    @Override
    public int delByName(String name) {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        int num = sqlSession.delete("com.pojo.Team.deleteByName" , name);
        sqlSession.commit();
        return num;
    }
    
}

测试类 (前提数据库中要有相应数据)

import com.dao.TeamDao;
import com.dao.TeamDaoImpl;
import com.pojo.Team;
import org.junit.Test;

import java.util.Date;
import java.util.List;

public class DaoApp {
    
    TeamDaoImpl teamDao = new TeamDao();
    
    @Test
    public void testFindAll() {
        List<Team> all = teamDao.findAll();
        all.forEach(team -> System.out.println(team));
    }
    
    @Test
    public void testQueryById() {
        Team team = teamDao.queryById(1009);
        System.out.println("team : " + team);
    }
    
    @Test
    public void testQueryByName() {
        Team team = teamDao.queryByName("老八");
        System.out.println("team : " + team);
    }
    
    @Test
    public void testAdd() {
        Team team = new Team("火箭","洛杉矶",new Date());
        int add = teamDao.add(team);
        System.out.println("add : " + add);
    }
    
    @Test
    public void testUpdateById() {
        Team newTeam = new Team("老鸽","上海");
        int i = teamDao.updateById(1004 , newTeam);
        System.out.println("i : " + i);
    }
    
    @Test
    public void testUpdateByName() {
        Team newTeam = new Team("法外狂徒","南京");
        int i = teamDao.updateByName("老鸽" , newTeam);
        System.out.println("i : " + i);
    }
    
    @Test
    public void testDelById() {
        int i = teamDao.delById(1004);
        System.out.println("i : " + i);
    }
    
    @Test
    public void testDelByName() {
        int num = teamDao.delByName("火箭");
        System.out.println("num : " + num);
    }
}

# Sql语句字段调配问题

# 库添加获取id问题

数据库 在添加对象 的时候 序列 都是也默认形式在 数据库 中进行自增的 ,并且自增后 后端是获取不到该对象的自增数据,因此可通过以下方式进行 自定义添加并获取

主要涉及点:

  • 配置映射 文件 ,insert标签 中 添加 selectKey标签 进行 对指定列 的值 自定义添加并获取
  • selectKey标签属性说明:
    • keyProperty:表示自增的id值 赋值 到哪个实体类的属性
    • order:AFTER(之后)、BEFORE(之前)两值,表示在sql语句执行 之前 或 之后
      • 一般情况 BEFORE 用于 指定id并获取;AFTER 用于 获取自增后的id值
    • resultType:表示返回值类型

实现应用

项目结构(在原有的基础上优化)

  .
  |
  ├── src
  |    ├── main
  |	   |	├── java
  |	   |	|	  └── com
  |	   |    |   	   ├── ···
  |	   |	|	  	   ├── pojo
  |	   |	|	  	   |	└── GameRecord
  |	   |    | 		   └── utils		
  |	   |    |    	   		└── MybatisUtil
  |	   |	└──	resources
  |	   |            ├── com
  |	   |            |	 └── mapper
  |	   |            |		   ├──GameRecordMapper.xml
  |	   |            |		   └──TeamMapper.xml
  |	   |		    ├── log4j.properties
  |	   |			└── mybatis.xml
  |	  test
  |    └── ...
  └── pom.xml

数据库添加表(球队比赛信息)

CREATE TABLE `gamerecord` (
  `recordId` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `homeTeamId` int DEFAULT NULL COMMENT ' 主队id',
  `gameDate` datetime DEFAULT NULL COMMENT '比赛时间',
  `score` int DEFAULT NULL COMMENT '得分',
  `visitingTeamId` int DEFAULT NULL COMMENT '客队id',
  PRIMARY KEY (`recordId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

实体类对象 ==GameRecord==

package com.pojo;

import java.util.Date;

/**
* ClassName: GameRecord
* 球队记录实体类
* @author wanglina
* @version 1.0
*/
public class GameRecord {
    private String recordId;
    private Integer homeTeamId;
    private Date gameDate;
    private Integer score;
    private Integer visitingTeamId;
    
	//省略 set 和 get 方法
    
    @Override
    public String toString() {
        return "GameRecord{" +
                "recordId='" + recordId + '\'' +
                ", homeTeamId=" + homeTeamId +
                ", gameDate=" + gameDate +
                ", score=" + score +
                ", visitingTeamId=" + visitingTeamId +
                '}';
    }
}

创建接口 ==GameRecordMapper==

package com.mapper;

import com.pojo.GameRecord;

public interface GameRecordMapper {
    int add(GameRecord gameRecord);
}

添加 ==resources/com/mapper/GameRecordMapper.xml== 配置文件 (order="BEFORE"应用)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.mapper.GameRecordMapper">

    <!--
    新增成功后将自增的ID赋值给参数属性 的 TeamId
    selectKey标签属性说明:
        keyProperty:表示自增的id值 赋值 到哪个实体类的属性
        order:AFTER(之后)、BEFORE(之前)两值,表示在sql语句执行 之前 或 之后
			一般情况 BEFORE 用于 指定id并获取;AFTER 用于 获取自增后的id值
        resultType:表示返回值类型
-->
    <insert id="add" parameterType="com.pojo.GameRecord">
        <selectKey keyProperty="recordId" order="BEFORE" resultType="java.lang.String">
            select uuid();
        </selectKey>
        INSERT INTO `mybatis`.`gamerecord`(`recordId`, `homeTeamId`, `gameDate`, `score`, `visitingTeamId`)
            VALUES (#{recordId}, #{homeTeamId}, #{gameDate}, #{score}, #{visitingTeamId})
    </insert>

</mapper>

修改==TeamMapper.xml==文件中的insert标签

<insert id="add" parameterType="com.pojo.Team">
    <selectKey keyProperty="teamId" order="AFTER" resultType="java.lang.Integer">
        SELECT LAST_INSERT_ID()
    </selectKey>
    INSERT INTO `mybatis`.`team`(`teamName`, `location`, `createTime`) VALUES (#{teamName}, #{location}, #{createTime})
</insert>

在 ==mybatis.xml配置文件== 中注册映射文件

<!-- 注册映射文件 -->
<mappers>
	···
    <mapper resource="com/mapper/TeamMapper.xml"/>
    <mapper resource="com/mapper/GameRecordMapper.xml"/>
</mappers>

测试:

import com.mapper.GameRecordMapper;
import com.mapper.TeamMapper;
import com.pojo.GameRecord;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;

import java.util.Date;

//id自增测试
public class id_incrementTest {
    
    private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
    private GameRecordMapper gameRecordMapper = MybatisUtil.getSqlSession().getMapper(GameRecordMapper.class);
    
    @Test
    public void teamMapper_order_AFTER() {
        Team team = new Team("哥斯拉","东京",new Date());
        int add = teamMapper.add(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println("add : " + add);
        System.out.println("team : " + team);
    }
    
    @Test
    public void gameRecordMapper_order_BEFORE() {
        GameRecord gameRecord = new GameRecord();
        gameRecord.setHomeTeamId(1002);
        gameRecord.setGameDate(new Date());
        gameRecord.setVisitingTeamId(1001);
        gameRecord.setScore(90);
        int add = gameRecordMapper.add(gameRecord);
        MybatisUtil.getSqlSession().commit();
        System.out.println("add : " + add);
        System.out.println("gameRecord : " + gameRecord);
    }
    
}

# 映射问题

# 数据库列名与实体类属性不匹配问题

一般情况 数据库列名 和 实体类属性名 一样,在以上条件为前提下 MyBatis 会自动匹配数据映射问题

解决方案:

  1. sql 查询的别名 与 实体类属性名 一致
  2. 通过resultMap自行映射

数据库 表 列名

teamId、teamName、location、createTime

创建 ==Team2实体类==

package com.pojo;

import java.util.Date;

/**
 * @author Sans
 */
//用于映射测试
public class Team2 {
    private Integer team_id;
    private String team_name;
    private String location;
    private Date createTime;
    
	//set和get 省略
    
    @Override
    public String toString() {
        return "Team2{" +
                "team_id=" + team_id +
                ", team_name='" + team_name + '\'' +
                ", location='" + location + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

==TeamMapper接口方法==

public interface TeamMapper {
    List<Team2> queryAll3();
	List<Team2> queryAll4();
}

修改 ==TeamMapper.xml映射文件== 添加sql语句

<!--    库列名 与 类属性名 不一致问题-->
<!--    解决方案1 (别名形式匹配)-->
<select id="queryAll3" resultType="com.pojo.Team2">
    select teamId 'team_id',teamName 'team_name',location,createTime from team
</select>
<!--    解决方案2(自行配置映射)-->
<select id="queryAll4" resultMap="baseMap">
    select * from team
</select>
<resultMap id="baseMap" type="com.pojo.Team2">
    <id column="teamId" property="team_id" javaType="java.lang.Integer"/>
    <result column="teamName" property="team_name" javaType="java.lang.String"/>
    <result column="location" property="location" javaType="java.lang.String"/>
    <result column="createTime" property="createTime" javaType="java.util.Date"/>
</resultMap>

测试:

//处理自定义类型数据
@Test
public void queryAll3() {
    List<Team2> teamList = teamMapper.queryAll3();
    teamList.forEach(team -> System.out.println(team));
}

@Test
public void queryAll4() {
    List<Team2> teamList = teamMapper.queryAll4();
    teamList.forEach(team -> System.out.println(team));
}

# 分页问题

查询 处理出来的大量数据需要分页功能浏览大量数据,缓解查询结果带来的压力

注意:

  • SQL语句末尾不能添加 分号 ;

应用

添加依赖

<!--        分页应用-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.1</version>
</dependency>

设置配置文件 ==mybatis.xml== (plugins标签节点 在 environments标签节点 之前)

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

测试(在以往的基础上执行)

private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);


//分页插件应用
@Test
public void pageTest() {
    PageHelper.startPage(1,3);
    List<Team> all = teamMapper.findAll();
    all.forEach(team -> System.out.println(team));
    System.out.println("其他分页信息:");
    PageInfo<Team> info = new PageInfo<>(all);
    System.out.println("当前页:"+info.getPageNum());
    System.out.println("总页数:"+info.getPages());
    System.out.println("前一页:"+info.getPrePage());
    System.out.println("后一页:"+info.getNextPage());
    System.out.println("所有导航页号:");
    for (int navigatepageNum : info.getNavigatepageNums()) {
        System.out.println("\t" + navigatepageNum);
    }
}

/* 运行结果

Team{teamId=1, teamName='张三', location='上海', createTime=Thu Jul 15 00:00:00 CST 2021}
Team{teamId=2, teamName='李四', location='深圳', createTime=Wed Jun 02 00:00:00 CST 2021}
Team{teamId=3, teamName='王五', location='南京', createTime=Sun Aug 01 00:00:00 CST 2021}
其他分页信息:
当前页:1
总页数:3
前一页:0
后一页:2
所有导航页号:
	1
	2
	3

*/

# 实用篇

# 动态SQL

实用标签说明 :

主标签

  • <select>
  • <insert>
  • <update>
  • <delete>

逻辑标签

  • <where>
  • <if>

功能标签

  • <trim> 管理子标签 (首尾括号 , 逗号分隔 , ...)
#MyBatis#数据库
上次更新: 2023/03/12, 00:43:49

← Hibernate Redis→

最近更新
01
HTTPS自动续签
10-21
02
博客搭建-简化版(脚本)
10-20
03
ruoyi-vue-plus-部署篇
07-13
更多文章>
Theme by Vdoing | Copyright © 2019-2024 | 桂ICP备2022009417号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式