JavaEE MyBatis学习记录

86 阅读31分钟

Mybatis框架

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

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

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

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

参考文档:mybatis – MyBatis 3

Mybatis 和 Hibernate区别

MybateisHibernate
学习门槛
查询语言sqlhql
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 通道进行 批处理?
参数名类型说明
autoCommitbooleantrue事务自动提交,否则关闭
connectionConnection应用自己提供的连接
execTypeExecutorTypeExecutorType枚举定义定义了三个值
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)

说明:

  • insertupdatedelete 方法返回值为影响的行数
  • 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>

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

<!--查询所有-->
<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>

其他别名

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

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属性:多个表达式定义的时候 需要使用 andor

应用

应用前提:

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

表结构

字段名类型
deptnoint
dnamevarchar
locvarchar

创建 ==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 部门表

字段名类型说明关系键
deptnointidkey
dnamevarchar部门名称
locvarchar部门地址

Emp 员工表

字段名类型说明关系键
empnointidkey
enamevarchar员工名
jobvarchar职位
mgrint
hiredatedatetime入职时间
saldecimal出售
commdecimal佣金
deptnoint部门iddept.deptno
jobnoint职位idjob.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 部门表

字段名类型说明关系键
deptnointidkey
dnamevarchar部门名称
locvarchar部门地址

Emp 员工表

字段名类型说明关系键
empnointidkey
enamevarchar员工名
jobvarchar职位
mgrint
hiredatedatetime入职时间
saldecimal出售
commdecimal佣金
deptnoint部门iddept.deptno
jobnoint职位idjob.jobno

Job 职位表

字段名类型说明关系键
jobnointidkey
janmevarchar职位名
remarkvarcahr备注

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

实体类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());
}