文章目录
参考文档: mybatis – MyBatis 3
Mybatis框架
MyBatis 是一个开源、轻量级的数据持久化框架,是 JDBC 和 Hibernate 的替代方案。MyBatis 内部封装了 JDBC,简化了加载驱动、创建连接、创建 statement 等繁杂过程,只需关注 SQL 语句即可
数据持久化是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中数据模型的统称。例如,文件的存储、数据的读取以及对数据表的增删改查等都是数据持久化操作
ORM 是一种数据持久化技术,它在对象模型和关系型数据库之间建立起对应关系,解决了实体类和数据库表映射的问题,并且提供了一种机制,通过 JavaBean 对象去操作数据库表中的数据。
Object: java对象
Relation: 关系,库中的表
Mapping: 映射
首次应用Mybatis
应用前提:
- 引入
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
引入依赖 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>
创建 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 +
'}';
}
}
配置 连接文件 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&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 注册映射文件 -->
<mappers>
<mapper resource="com/pojo/Team.xml"/>
</mappers>
</configuration>
配置 映射文件 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>
配置 映射文件的扫描 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>
测试查询
@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 通道进行 批处理 ?
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
//这个方法会返回一个 Configuration 实例,你可以在运行时使用它来检查 MyBatis 的配置
Configuration getConfiguration();
}
方法参数名 | 类型 | 说明 |
---|---|---|
autoCommit | boolean | true事务自动提交,否则关闭 |
connection | Connection | 应用自己提供的连接 |
execType | ExecutorType | 枚举定义定义了三个值 ExecutorType.SIMPLE:为每个语句的执行创建一个新的预处理语句 ExecutorType.REUSE:执行器会复用预处理语句 ExecutorType.BATCH:执行器会批量执行所有更新语句 |
默认的 openSession() 方法没有参数,它会创建具备如下特性的 SqlSession:
- 事务作用域将会开启(不自动提交)
- 将由当前环境配置的 DataSource 实例中获取 Connection 对象
- 事务隔离级别将会使用驱动或数据源的默认设置
- 预处理语句不会被复用,也不会批量处理更新
SqlSessio
org.apache.ibatis.session.SqlSession接口
SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束
SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。 SqlSession 在方法内部创建,使用完毕后关闭
SqlSession 类中有超过 20 个方法,我们常用的几乎都是执行语法相关的方法
以下方法被用来执行定义在 SQL 映射 XML 文件中的 SELECT、INSERT、UPDATE 和 DELETE 语句。可通过名字快速了解它们的作用,每一方法都接受语句的 ID 以及参数对象,参数可以是原始类型(支持自动装箱或包装类)、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构架
- Mybatis.xml文件是mybatis框架的全局配置文件,配置了mybatis框架运行的环境等信息。
- Mapperxx.xml是SQL的映射文件,文件中配置了所有的操作数据库的sql语句,这些文件需要在全局配置文件中加载
- 通过mybatis环境等配置信息构建SqlSessionFactroy ,相当于是产生连接池
- 由会话工厂创建SqlSession即会话(连接),操作数据库需要通过SqlSession进行的
- Mybatis底层自定义了Executor执行器的接口操作数据库,Executor接口有两个实现,一个基本的执行器,一个是缓存的执行器
- Mapped statement 也是mybatis框架一个底层的封装对象,包装了mybatis配置信息以及sql映射信息。Mapper.xml文件中的一个SQL语句对应一个Mapped statement对象,sql的id就是Mapped statement的id
- Mapped statement对SQL执行输入参数的定义,输入参数包括HashMap、基本类型、pojo,Executor通过Mapped statemen在执行SQL语句 前将输入java对象映射到sql语句中,执行完毕SQL之后,输出映射就是JDBC编码中的对preparedStatement 执行结果的定义
配置日志
实现步骤:
- 添加依赖
log4j
- 创建日志配置文件
log4j.properties
- 在
mybatis.xml
配置文件添加日志配置
依赖
<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>
测试 : 运行时会提示出运行明细的日志
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
| | └── pojo
| | └── Team.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().commit();
System.out.println("i : " + i);
}
}
输入映射
传递多个参数
parameterType
parameterType 指定值是接口中方法参数的类型,类型必须是完全限定名 或 别名。该属性非必须,因为Mybatis框架能自行判断具 体传入语句的参数
Mybatis 提供以下 3 种方式,实现给映射器传递多个参数:
- 方法直接传递参数
- 使用注解传递参数
- 使用 Map传递参数
应用说明:
- 方法直接传递参数 进行传递 不同mybatis版本传递参数 在sql应用不一样
- mybatis3.3之前:使用 #{0}、#{1}、…
- mybatis3.3之后:使用 #{arg0},#{arg1}、… 或者 #{param1}、#{param2}、…
- sql语句中 的 < 号 不能使用 ,需要 转义符 < :
<
; > :>
- 使用@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 <= ${arg1}
</select>
<!--param 应用-->
<select id="queryByRange2" resultType="com.pojo.Team">
select * from team where teamId>=#{param1} and teamId <= ${param2}
</select>
<!--注解别名 应用-->
<select id="queryByRange3" resultType="com.pojo.Team">
select * from team where teamId>=#{min} and teamId <= ${max}
</select>
<!--Map 应用-->
<!-- 传参 #{} Map集合中的 Key保持一致-->
<select id="queryByRange4" resultType="com.pojo.Team">
select * from team where teamId>=#{min} and teamId <= ${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));
}
}
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&characterEncoding=utf-8&useSSL=false&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>
MyBatis映射关系
在MySQL中,当两表之间存在着主从关系,那么从表有个外键 对应 主表的主键 !
以下是可能出现映射关系的情况:
- 对一 映射关系
- 对多 映射关系
对一 映射关系
对一映射方式有3种:
- 通过关联对象打点调用属性的方式
- 直接引用关联对象的Mapper映射
- 直接引用关联对象的单独查询的方法
实现应用
前提:
- 库中的player表要有数据(注意主从关系)
- 全局配置文件 已注册映射文件
创建新表 mybatis库 player表
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;
创建 com.pojo.Player实体对象
package com.pojo;
public class Player {
private int playerId;
private String playerName;
private int playerNum;
private int teamId;
//多对一的体现 ; 关联对象 多球员 -- 球队
private Team team1;
private Team team3;
private Team team2;
//省略 get 和 set 方法
@Override
public String toString() {
return "Player{" +
"playerId=" + playerId +
", playerName='" + playerName + '\'' +
", playerNum=" + playerNum +
", teamId=" + teamId +
", team1=" + team1 +
", team3=" + team3 +
", team2=" + team2 +
'}';
}
}
创建 com.mapper.PlayerMapper接口
package com.mapper;
import com.pojo.Player;
import org.apache.ibatis.annotations.Param;
public interface PlayerMapper {
Player queryById1(@Param("id") int playerId);
Player queryById2(int playerId);
Player queryById3(int playerId);
Player queryById4(int playerId);
}
创建 PlayerMapper.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.PlayerMapper">
<select id="queryById1" resultType="Player">
SELECT * FROM player WHERE playerId=#{id}
</select>
<select id="queryById2" resultMap="JoinTeamResult1">
SELECT * FROM player p INNER JOIN team t
WHERE p.teamId=t.teamId AND p.playerId=4
</select>
<select id="queryById3" resultMap="JoinTeamResult2">
SELECT * FROM `player` p INNER JOIN team t
WHERE p.teamId=t.teamId AND p.playerId=4
</select>
<select id="queryById4" resultMap="JoinTeamResult3">
SELECT * FROM player WHERE playerId=#{id}
</select>
<resultMap id="baseResultMap" type="com.pojo.Player">
<id column="playerId" property="playerId"/>
<result column="playerName" property="playerName"/>
<result column="playerNum" property="playerNum"/>
<result column="teamId" property="teamId"/>
</resultMap>
<!-- No.1
通过关联对象打点调用属性的方式
要求:连接查询
如果连接查询,一般单独定义resultMap
extends属性值为:继承其他 resultMap的id
前提 team.teamId 与 player.teamId 相同
-->
<resultMap id="JoinTeamResult1" type="player" extends="baseResultMap">
<id column="teamId" property="team1.teamId"/>
<result column="teamName" property="team1.teamName"/>
<result column="location" property="team1.location"/>
<result column="createTime" property="team1.createTime"/>
</resultMap>
<!-- No.2
直接饮用关联对象的Mapper映射
要求:连接查询、关联对象中已经存在被引用的resultMap
property:关联对象的属性名
javaType:关联对象的类型
resultMap:关联对象的命名空间中的resultMap
-->
<resultMap id="JoinTeamResult2" type="Player" extends="baseResultMap">
<association property="team2" javaType="Team" resultMap="com.mapper.TeamMapper.baseResultMap"/>
</resultMap>
<!-- No.3
直接引用关联对象的单独查询的方法
要求:关联对象的Mapper中必须要求有独立的查询方法 如以下:TeamMapper中的queryById方法
property:关联对象的属性名
javaType:关联对象的类型
select:关联对象的单独查询的语句
column:外键列 => 查询从表的主键
-->
<resultMap id="JoinTeamResult3" type="Player" extends="baseResultMap">
<association property="team3" javaType="Team" select="com.mapper.TeamMapper.queryById" column="teamId"/>
</resultMap>
</mapper>
测试
import com.mapper.PlayerMapper;
import com.pojo.Player;
import com.utils.MybatisUtil;
import org.junit.Test;
public class PlayerMapperTest {
private PlayerMapper playerMapper = MybatisUtil.getSqlSession().getMapper(PlayerMapper.class);
@Test
public void queryById1() {
Player player = playerMapper.queryById1(5);
System.out.println("player : " + player);
}
@Test
public void queryById2() {
Player player = playerMapper.queryById2(2);
System.out.println("player : " + player);
}
@Test
public void queryById3() {
Player player = playerMapper.queryById3(5);
System.out.println("player : " + player);
}
@Test
public void queryById4() {
Player player = playerMapper.queryById4(3);
System.out.println("player : " + player);
}
}
对多映射关系
对多映射方式有2种:
- 连接查询
- 引用关联对象的单独查询的方法
实现应用
修改 Team类 添加对多的属性
public class Team {
private Integer teamId;
private String teamName;
private String location;
private Date createTime;
//一对多的体现:一方持有多方的对象
private List<Player> playerList1;
private List<Player> playerList2;
//省略 set 、get、toString 方法
}
修改 TeamMapper接口 添加应用方法
package com.mapper;
import com.pojo.Team;
import com.pojo.Team2;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface TeamMapper {
····
Team queryById1(int temaId);
Team queryById2(int temaId);
}
修改 PlayerMapper接口 添加应用方法(用于实现方法2)
package com.mapper;
import com.pojo.Player;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author Sans
*/
public interface PlayerMapper {
····
List<Player> queryByteamId(int teamId);
}
修改 PlayerMapper.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.PlayerMapper">
·····
<!-- 用于对多映射的应用 -->
<select id="queryByTeamId" resultType="Player">
SELECT * FROM player WHERE teamId=#{id}
</select>
</mapper>
修改 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">
·····
<select id="queryById1" resultMap="joinResult1">
SELECT * FROM team t INNER JOIN player p
WHERE t.teamId = p.teamId AND t.teamId = #{id}
</select>
<select id="queryById2" resultMap="joinResult2">
SELECT * FROM team WHERE teamId=#{id}
</select>
<!-- No.1
对多连接查询:对多使用 collection标签
property:关联对象集合名称
javaType:关联对象集合类型
ofType:关联对象集合泛型
resultMap:引用关联对象的结果映射
-->
<resultMap id="joinResult1" type="Team" extends="baseResultMap">
<collection property="playerList1" javaType="java.util.ArrayList"
ofType="player" resultMap="com.mapper.PlayerMapper.baseResultMap"/>
</resultMap>
<!-- No.2
对多连接查询:对多使用 collection标签
property:关联对象集合名称
javaType:关联对象集合类型
select:引用关联对象的单独查询的方法(前提方法可用)
column:引用关联对象的单独查询的方法的参数 , 一般是外键名!!!
-->
<resultMap id="joinResult2" type="Team" extends="baseResultMap">
<collection property="playerList2" javaType="java.util.ArrayList"
select="com.mapper.PlayerMapper.queryByTeamId" column="teamId"/>
</resultMap>
</mapper>
测试
//对多映射测试
private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
@Test
public void queryById1() {
Team team = teamMapper.queryById1(1019);
System.out.println("team : " + team);
team.getPlayerList1().forEach(player -> System.out.println("\t"+player));
}
@Test
public void queryById2() {
Team team = teamMapper.queryById2(1019);
System.out.println("team : " + team);
team.getPlayerList2().forEach(player -> System.out.println("\t"+player));
}
动态SQL
动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,需要手动拼接 SQL 语句,避免了 在不同条件下拼接 SQL 语句的困难
动态SQL应用到的元素
标签节点 | 作用 |
---|---|
if | 判断语句 |
choose 、when、otherwise | 多条件分支判断语句 |
foreach | 循环语句 |
where、set | 辅助应用(处理SQL瓶装问题) |
应用
应用前提:
- 有库信息
- 映射文件已注册(扫描包)
封装查询条件类 QueryTeamVO
package com.pojo;
import java.util.Date;
public class QueryTeamVO {
/** 封装球队查询信息
* 球队名 & 开始时间节点 & 结束时间节点 & 地点
*/
private String name;
private Date beginTime;
private Date endTime;
private String location;
// 省略 set 、 get 方法
@Override
public String toString() {
return "QueryTeamVO{" +
", name='" + name + '\'' +
", beginTime=" + beginTime +
", endTime=" + endTime +
", location='" + location + '\'' +
'}';
}
}
添加应用方法 TeamMapper接口
package com.mapper;
import com.pojo.QueryTeamVO;
import com.pojo.Team;
import com.pojo.Team2;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface TeamMapper {
····
List<Team> queryByVO(QueryTeamVO vo);
List<Team> queryByVO2(QueryTeamVO vo);
int update2(Team team);
int addList(List list);
int delList(List list);
}
配置映射文件 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">
·····
<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>
····
<!-- 实现多条件查询if
SQL中的 CONCAT函数说明:
CONCAT("aa","bb") => aabb 拼接字符串
-->
<select id="queryByVO" resultMap="baseResultMap">
SELECT * FROM team
<where>
<if test="name!=null">
AND teamName LIKE CONCAT(CONCAT('%',#{name}),'%')
</if>
<if test="beginTime!=null">
AND createTime >= #{beginTime}
</if>
<if test="endTime!=null">
AND createTime <= #{endTime}
</if>
<if test="location!=null">
AND location = #{location}
</if>
</where>
</select>
<!-- choose、when、otherwise 标签应用
标签说明:
choose:指定分支
when:条件分支节点
otherwise:都未满足条件
-->
<select id="queryByVO2" resultMap="baseResultMap">
SELECT * FROM team WHERE 1=1
<choose>
<when test="name!=null">
AND teamName LIKE CONCAT(CONCAT('%',#{name}),'%')
</when>
<when test="location!=null">
AND location LIKE CONCAT(CONCAT('%',#{location}),'%')
</when>
<otherwise>
AND 1=0
</otherwise>
</choose>
</select>
<!-- set 标签应用-->
<update id="update2">
UPDATE team
<set>
<if test="teamName!=null"> teamName=#{teamName} </if>
<if test="location!=null"> location=#{location} </if>
</set>
WHERE teamId=#{teamId}
</update>
<!-- forEach 标签应用
标签属性说明:
collection:指定集合
item:集合中的每个迭代对象
separator:每次迭代之间的分隔符
open:循环语句 以指定字符为 开头
close:循环语句 以指定字符为 结束
-->
<!-- 添加-->
<insert id="addList">
INSERT INTO `mybatis`.`team`(`teamName`, `location`, `createTime`) VALUES
<foreach collection="list" item="team" separator=",">
(#{team.teamName}, #{team.location}, #{team.createTime})
</foreach>
</insert>
<!-- 删除-->
<delete id="delList">
DELETE FROM team WHERE teamId in
<foreach collection="list" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
</mapper>
测试
import com.mapper.TeamMapper;
import com.pojo.QueryTeamVO;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
//动态SQL测试
public class SQLTest {
private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
//if标签节点应用
@Test
public void test01() {
QueryTeamVO vo = new QueryTeamVO();
// vo.setName("哥斯拉");
// vo.setLocation("洛杉矶");
vo.setEndTime(new Date());
List<Team> teamList = teamMapper.queryByVO(vo);
teamList.forEach(team -> System.out.println(team));
}
//choose、when、otherwise 标签应用
@Test
public void test02() {
QueryTeamVO vo = new QueryTeamVO();
//查 指定name的关键字
vo.setName("哥");
//查 指定location的关键字
// vo.setLocation("洛");
List<Team> teamList = teamMapper.queryByVO2(vo);
teamList.forEach(team -> System.out.println(team));
}
//set 标签应用
@Test
public void test03() {
Team team = new Team();
team.setTeamId(1017);
team.setTeamName("老鸽队");
int num = teamMapper.update2(team);
MybatisUtil.getSqlSession().commit();
System.out.println("num : " + num);
}
//forEach 标签应用
//添加
@Test
public void test04() {
List<Team> list = new ArrayList<>();
for (int i = 1 ; i <= 3 ; i++) {
Team team = new Team();
team.setTeamName("forTest"+i);
team.setLocation("jd"+i);
team.setCreateTime(new Date());
list.add(team);
}
int i = teamMapper.addList(list);
MybatisUtil.getSqlSession().commit();
System.out.println("i : " + i);
}
//删除
@Test
public void test05() {
List<Integer> list = new ArrayList<>();
list.add(1022);
list.add(1023);
list.add(1024);
list.add(1025);
int i = teamMapper.delList(list);
MybatisUtil.getSqlSession().commit();
System.out.println("i : " + i);
}
}
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逆向生成
Mybatis 提供逆向生成工具,该工具可以根据 数据库中的表 自动生成单表的 pojo 类、mapper 映射文件和 mapper 接口。大大缩减了开发时间!!
应用实现
库数据引入&展示 (库名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
注意:
classPathEntry
标签。数据库驱动jar:添加自己的jar路径jdbcConnection
标签。 配置数据库连接,应用自己的 url(指定库)、账号、密码javaModelGenerator
标签。指定路径指定包,用于生成实体类sqlMapGenerator
标签。指定路径指定包,用于生成映射接口javaClientGenerator
标签。指定路径指定包,用于生成映射文件,执行SQL语句的table
标签。指定需要生成的表
<?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&characterEncoding=utf-8&useSSL=false&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项目运行
非Maven项目运行
引入依赖:
- org.codehaus.plexus:plexus-classworlds:2.5.2
- org.mybatis.generator:mybatis-generator-core:1.3.5
在根目录下 config/generatorConfig.xml
逆向生成配置文件
启动生成代码:
package com.utils;
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;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class Generator {
public void generator() throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
// 指定配置文件
File configFile = new File("src/config/generatorConfig.xml");
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);
}
// 执行main方法以生成代码
public static void main(String[] args) {
try {
Generator generatorSqlmap = new Generator();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试(应用测试主要部分,多余就不赘述了)
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));
}
}