- 1.MyBatis简介
- 2.MyBatis——入门案例
- 3.MyBatis——全局配置文件常用标签
- (1)properties属性
- (2)settings设置
- (3)typeAliases别名处理器
- (4)typeHandlers类型处理器
- (5)plugins插件
- (6)environments环境
- (7)databaseIdProvider环境
- (7)mapper映射
- 4.MyBatis——映射文件
- (1)简单的增删改查
- (2)获取自增主键的值
- (3)获取非自增主键的值(Oracle)
- (4)参数处理——单个参数
- (5)参数处理——多个参数
- (6)参数处理——命名参数
- (7)参数处理——POJO & Map & TO
- (8)参数处理——参数封装扩展思考
- (9)参数处理——#与&取值的区别
- (10)Select
- ① 返回值类型为List
- ② 记录封装map
- ③ resultMap——自定义结果映射规则
- ④ resultMap——关联查询_环境搭建
- ⑤ resultMap——级联属性封装结果
- ⑥ resultMap——association定义关联对象封装规则
- ⑦ resultMap——association分步查询 & 延迟加载
- ⑧ resultMap——collection定义关联集合封装规则
- ⑨ resultMap——collection分步查询 & 延迟加载
- ⑩ resultMap——discriminator鉴别器
- 5.MyBatis——动态SQL
- (1)简介 & 环境搭建
- (2)动态SQL标签——if
- (3)动态SQL标签——where
- (3)动态SQL标签——trim
- (4)动态SQL标签——choose
- (5)动态SQL标签——set(与if结合的动态更新)
- (6)动态SQL标签——foreach遍历集合
- (7)动态SQL标签——mysql下foreach批量插入的两种方式
- (8)动态SQL标签——内置参数_parameter和_databaseId
- (9)动态SQL标签——bind绑定
- (10)动态SQL标签——抽取可重用的sql片段
- 6.MyBatis——缓存机制
- (1)缓存介绍
- ① 一级缓存
- ② 二级缓存
- (2)一级缓存体验
- (3)一级失效的四种情况
- (4)二级缓存的使用
- (5)缓存有关的设置/属性
- (6)缓存原理图示
- (7)第三方缓存整合——ehcache
- ① 导入ehcache适配包
- ② 在config目录下添加ehcache.xml文件
- ③ 在EmployeeMapper.xml文件中进行配置
- ④ 测试
- ⑤ 扩展——引用缓存
- 7.MyBatis——SSM框架整合
- (1)在IDEA中新建一个Java Web项目
- (2)导入需要的jar包
- (3)进行各种配置以及代码的编写
- (4)测试
- 8.MyBatis——逆向工程
- (1)MyBatis Generator
- (2)编写相应的配置文件及代码
- 9.MyBatis——工作原理
- (1)框架分层架构
- (2)入门案例的运行流程
- 10.MyBatis——插件开发
- (1)插件原理
- (2)编写插件
- (3)多个插件运行流程
- (4)开发插件
- (5)PageHelper分页插件的使用
- 11.Mybatis扩展
- (1)批量操作
- (2)自定义类型处理器——MyBatis中枚举类型的默认处理
- (3)自定义类型处理器——使用自定义的类型处理器处理枚举类型
本文章的笔记整理来自尚硅谷视频https://www.bilibili.com/video/BV1mW411M737
1.MyBatis简介(1)MyBatis历史 原是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation 迁移到了Google Code,随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis ,代码于2013年11月迁移到Github。iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)。 (2)MyBatis特点 ① MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的半自动化持久层框架。 ② MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。 ③ MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。 (3)MyBatis与其它对应框架或组件的对比
MyBatis半自动化的持久化层框架JDBC① SQL夹在Java代码块里,耦合度高导致硬编码内伤。② 维护不易且实际开发需求中sql是有变化,频繁修改的情况多见。Hibernate和JPA① 长难复杂SQL,对于Hibernate而言处理也不容易内部自动生产的SQL,不容易做特殊优化。② 基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难,这会导致数据库性能下降。总之,对开发人员而言,核心sql还是需要自己来优化的。除此之外,如果sql代码和java代码分开,那么它们的功能边界就更加清晰,一个专注业务、一个专注数据。
(4)MyBatis地址为https://github.com/mybatis/mybatis-3/
2.MyBatis——入门案例(1)创建测试表tbl_employee并添加一条测试数据,表结构及数据如下:
(2)在IDEA中创建一个Java项目,并在项目的根目录下创建lib目录(用于存放要导入的 jar 包)和config目录(用于存放配置文件),另外,需要右键点击config目录,找到Mark Directoy as并选择Resources Root。
(3)导入相关的 jar 包并引入到项目中
(4)Mybatis入门案例项目的整体目录结构如下:
各个部分的具体代码如下: ① 日志配置文件log4j.xml
DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
② 与数据库中的表tbl_employee对应的实体类Employee.java
package com.atguigu.mybatis.bean;
import org.apache.ibatis.type.Alias;
//@Alias("别名"):单独为Employee起别名
//@Alias("emp")
public class Employee {
//属性名称应该与对应数据库表(tbl_employee)中的字段一致
private Integer id;
private String last_name;
private String email;
private String gender;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Employee [id=" + id + ", lastName=" + last_name + ", email="
+ email + ", gender=" + gender + "]";
}
}
③ Mapper接口EmployeeMapper.java
package com.atguigu.mybatis.dao;
import com.atguigu.mybatis.bean.Employee;
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
}
④ sql映射文件EmployeeMapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select * from tbl_employee where id = #{id}
⑤ 数据库配置文件dbconfig.properties
jdbc.drive=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
⑥ mybatis的全局配置文件mybatis-config.xml
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
⑦ 测试代码
package com.atguigu.mybatis.test;
import com.atguigu.mybatis.bean.Employee;
import com.atguigu.mybatis.dao.EmployeeMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisTest {
//根据全局配置文件mybatis-config.xml,利用SqlSessionFactoryBuilder创建SqlSessionFactory
public SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
return new SqlSessionFactoryBuilder().build(inputStream);
}
/*
* 1、根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象,其包含数据源等一些运行环境信息
* 2、sql映射文件;配置了每一个sql,以及sql的封装规则等。
* 3、将sql映射文件注册在全局配置文件中
* 4、写代码:
* 1)、根据全局配置文件得到SqlSessionFactory;
* 2)、使用sqlSession工厂,获取到sqlSession对象使用他来执行增删改查
* 一个sqlSession就是代表和数据库的一次会话,用完关闭
* 3)、使用sql的唯一标志来告诉MyBatis执行哪个sql。sql都是保存在sql映射文件中的。
*
* @throws IOException
*/
//旧版本常用方式
@Test
public void test01() throws IOException {
// 获取sqlSession实例,能直接执行已经映射的sql语句
// sql的唯一标识:statement Unique identifier matching the statement to use.
// 执行sql要用的参数:parameter A parameter object to pass to the statement.
//使用SqlSessionFactory获取sqlSession对象,一个SqlSession对象代表和数据库的一次会话
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
Employee employee = openSession.selectOne(
"com.atguigu.mybatis.dao.EmployeeMapper.getEmpById", 1);
System.out.println(employee);
} finally {
openSession.close();
}
}
//新版本常用的方式:接口式编程
/*
* 1、接口式编程
* 原生: Dao ====> DaoImpl
* mybatis: Mapper ====> xxMapper.xml
*
* 2、SqlSession代表和数据库的一次会话;用完必须关闭;
* 3、SqlSession和connection一样她都是非线程安全。每次使用都应该去获取新的对象。
* 4、mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象。
* (将接口和xml进行绑定)
* EmployeeMapper empMapper = sqlSession.getMapper(EmployeeMapper.class);
* 5、两个重要的配置文件:
* mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息等...系统运行环境信息
* sql映射文件:保存了每一个sql语句的映射信息:将sql抽取出来。
*
*/
@Test
public void test02() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
System.out.println(mapper.getClass());
System.out.println(employee);
}finally{
openSession.close();
}
}
}
结果如下:
MyBatis可以使用properties来引入外部properties配置文件的内容。
# 数据库配置文件dbconfig.properties
jdbc.drive=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
(2)settings设置
settings设置是MyBatis 中极为重要的调整设置,它们会改变MyBatis 运行时的行为。 例如开启自动驼峰命名规则映射:
(3)typeAliases别名处理器
typeAliases:别名处理器,它可以为java类型起别名(别名不区分大小写)
除此之外,MyBatis已经为Java中一些常见的数据类型起好了别名,在开发时直接使用即可。
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为。插件通过动态代理机制,可以介入四大对象的任何一个方法的执行。后面会有专门的章节来介绍mybatis运行原理以及插件。 • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) • ParameterHandler (getParameterObject, setParameters) • ResultSetHandler (handleResultSets, handleOutputParameters) • StatementHandler (prepare, parameterize, batch, update, query)
(6)environments环境MyBatis可以配置多种环境,例如开发、测试和生产环境需要有不同的配置。每种环境使用一个environment标签进行配置并指定唯一标识符。可以通过environments标签中的default属性指定一个环境的标识符来快速地切换环境。 ① environment——指定具体的环境:
id指定当前环境的唯一标识transactionManager、dataSource分别表示事务管理、数据源,必须存在于environment中
② transactionManager——事务管理
typeJDBC、MANAGED、自定义JDBC使用了 JDBC 的提交和回滚设置,依赖于从数据源得到的连接来管理事务范围MANAGED不提交或回滚一个连接、让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)自定义实现TransactionFactory接口,type=全类名/别名③ dataSource——数据源
typeUNPOOLED、POOLED、JNDI、自定义UNPOOLED不使用连接池POOLED使用连接池JNDI在EJB 或应用服务器这类容器中查找指定的数据源自定义实现DataSourceFactory接口,定义数据源的获取方式注:在实际开发中,一般使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置。 (7)databaseIdProvider环境MyBatis 可以根据不同的数据库厂商执行不同的语句。
在sql映射文件中编写具体的sql语句时指定需要操作的数据库(当然这需要提前导入相应的jar包以及配置好数据库环境、数据源信息等,这样才能顺利切换需要操作的数据库)。
select * from tbl_employee where id = #{id}
MyBatis匹配规则如下: ① 如果没有配置databaseIdProvider标签,那么databaseId=null; ② 如果配置了databaseIdProvider标签,使用标签配置的name去匹配数据库信息,匹配上设置databaseId=配置指定的值,否则依旧为null; ③ 如果databaseId不为null,他只会找到配置databaseId的sql语句; ④ MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库databaseId 属性的所有语句。如果同时找到带有 databaseId 和不带databaseId 的相同语句,则后者会被舍弃;
(7)mapper映射mappers将写好的sql映射文件注册到全局配置中。
注:当使用mapper中的class属性来注册接口时,不需要映射文件并且sql语句利用注解写在接口上的例子如下:
package com.atguigu.mybatis.dao;
import com.atguigu.mybatis.bean.Employee;
import org.apache.ibatis.annotations.Select;
public interface EmployeeMapperAnnotation {
//直接在注解中编写sql语句,不要sql映射文件
@Select("select * from tbl_employee where id = #{id}")
public Employee getEmpById(Integer id);
}
以上介绍了MyBatis中常用的标签,所有的标签如下图所示。不过在使用这些标签时也需要按照下图中从上到下规定的顺序(某个标签可以没有,但如果有则顺序不能颠倒,否则会报错!!!)
映射文件指导着MyBatis如何进行数据库增删改查,有着非常重要的意义。
(1)简单的增删改查① 在接口EmployeeMapper中编写抽象的增删改查方法
package com.atguigu.mybatis.dao;
import com.atguigu.mybatis.bean.Employee;
public interface EmployeeMapper {
Employee getEmpById(Integer id);
void addEmp(Employee employee);
boolean updateEmp(Employee employee);
void deleteEmpById(Integer id);
}
② 在对应的sql映射文件EmployeeMapper.xml中编写sql语句
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select * from tbl_employee where id = #{id}
insert into tbl_employee(last_name,email,gender)
values (#{last_name},#{email},#{gender})
update tbl_employee
set last_name=#{last_name},email=#{email},gender=#{gender}
where id=#{id}
delete from tbl_employee where id=#{id}
③ 测试
/*
* 测试增删改
* 1、mybatis允许增删改直接定义以下类型返回值
* Integer、Long、Boolean、void
* 2、我们需要手动提交数据
* sqlSessionFactory.openSession();===》手动提交
* sqlSessionFactory.openSession(true);===》自动提交
*/
@Test
public void test04() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//添加数据
Employee employee1 = new Employee(null, "Jerry", "1","Jerry@163.com");
mapper.addEmp(employee1);
//修改数据
Employee employee2 = new Employee(2, "Hi", "1","Hi@163.com");
//若修改影响的行数大于等于1,则返回true,否则返回false
boolean flag = mapper.updateEmp(employee2);
System.out.println(flag);
//删除数据
mapper.deleteEmpById(2);
//手动提交数据
openSession.commit();
}finally{
openSession.close();
}
}
除此之外,insert、update、delete标签中常见的属性如下:
insert into tbl_employee(last_name,email,gender)
values (#{last_name},#{email},#{gender})
//添加数据
Employee employee = new Employee(null, "Jerry", "1","Jerry@163.com");
mapper.addEmp(employee);
//输出得到的自增主键的值
System.out.println(employee.getId());
(3)获取非自增主键的值(Oracle)
select EMPLOYEES_SEQ.nextval from dual
insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL)
values(#{id},#{lastName},#{email})
selectKey标签的相关属性如下:
对于单个参数,mybatis不会做特殊处理,即按照 #{参数名/任意名} 就可以取出参数值。
delete from tbl_employee where id=#{id}
(5)参数处理——多个参数
对于多个参数,mybatis会做特殊处理,即多个参数会被封装成一个map,其中key为param1…paramN(或者参数的索引),value为传入的参数值,#{x}就等于从map中获取 key 为 x 的value的值。 ① 定义含有2个参数的抽象方法:
Employee getEmpByIdAndLast_name(Integer id,String last_name);
② 错误示例:按照单个参数的方式进行取值
select * from tbl_employee where id=#{id} and last_name=#{last_name}
@Test
public void test05() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpByIdAndLast_name(1, "Tom");
System.out.println(employee);
openSession.commit();
}finally{
openSession.close();
}
}
错误提示如下:
### Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [0, 1, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [0, 1, param1, param2]
③ 正确示例
select * from tbl_employee where id=#{param1} and last_name=#{param2}
(6)参数处理——命名参数
对于多个参数的处理还有另外一种方法,即命名参数,使用注解@Param明确指定封装参数时map的key。
Employee getEmpByIdAndLast_name(@Param("id") Integer id,@Param("last_name") String last_name);
select * from tbl_employee where id=#{id} and last_name=#{last_name}
(7)参数处理——POJO & Map & TO
① 如果多个参数正好是我们业务逻辑的数据模型,那么就可以直接传入对应的POJO(Plain Old Java Objects,普通的Java对象),然后按照 #{属性名} 的方式取出传入的POJO的属性值。
boolean updateEmp(Employee employee);
update tbl_employee
set last_name=#{last_name},email=#{email},gender=#{gender}
where id=#{id}
② 如果多个参数不是业务模型中的数据,没有对应的pojo,为了方便,可以传入map。
Employee getEmpByMap(Map map);
select * from tbl_employee where id=#{id} and last_name=#{last_name}
@Test
public void test05() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Map map = new HashMap();
map.put("id",1);
map.put("last_name","Tom");
Employee employee = mapper.getEmpByMap(map);
System.out.println(employee);
openSession.commit();
}finally{
openSession.close();
}
}
③ 如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)来数据传输对象。 例如对于数据的分页处理,我们可以单独定义一个Page类,里面自定义需要的属性,其用法与POJO类似。
(8)参数处理——参数封装扩展思考 public Employee getEmp(@Param(“id”)Integer id,String last_name);取值:id==>#{id/param1} last_name==>#{param2}public Employee getEmp(Integer id, @Param(“e”)Employee emp);取值:id==>#{param1} last_name===>#{param2.last_name/e.last_name}如果是Collection(List、Set)类型或者是数组,也会特殊处理,也是把传入的list或者数组封装在map中key:Collection(collection)如果是List数组使用这个key(list)数组(array)public Employee getEmpById(List ids);取值:取出第一个id的值: #{list[0]} (9)参数处理——#与&取值的区别① #{ } 与 ${ }都可以获取map中的值或者pojo对象属性的值。 ② 在具体的细节上#{ } 与 ${ }有一些不同:
#{ }是以预编译的形式,将参数设置到 sql 语句中,可以防止 sql 注入${ }取出的值直接拼装在 sql 语句中,可能会存在安全问题 ③ 在大多情况下,我们应该使用 #{ } 去取参数的值,但是在一些特殊情况下需要使用 ${ }:
# 在原生JDBC不支持占位符的地方,就可以使用${}进行取值,例如分表、排序等
select * from ${year}_salary where xxx;
select * from ${tbl_name} where id=${id} and last_name=#{last_name};
(10)Select
Select标签用来定义查询操作,常用的属性如下:
id唯一标识符,用来引用这条语句,需要和接口的方法名一致parameterType参数类型,可以不传,MyBatis会根据TypeHandler自动推断resultType返回值类型,别名或者全类名,如果返回的是集合,定义集合中元素的类型,不能和resultMap同时使用其余属性如下:
List getEmpsByLast_NameLike(String last_name);
select * from tbl_employee where last_name like #{last_name}
@Test
public void test06() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//查询姓名中含有字母e的所有员工
List emps = mapper.getEmpsByLast_NameLike("%e%");
System.out.println(emps);
openSession.commit();
}finally{
openSession.close();
}
}
② 记录封装map
1)返回一条记录的map,key为列名,value为对应的值
Map getEmpByIdReturnMap(Integer id);
select * from tbl_employee where id=#{id}
@Test
public void test06() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Map map = mapper.getEmpByIdReturnMap(1);
System.out.println(map);
openSession.commit();
}finally{
openSession.close();
}
}
2)多条记录封装一个map:Map,键是这条记录的主键,值是记录封装后的javaBean
//@MapKey:告诉mybatis封装这个map的时候使用哪个属性作为map的key
@MapKey("id")
Map getEmpByLast_NameLikeReturnMap(String last_name);
select * from tbl_employee where last_name like #{last_name}
@Test
public void test06() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Map map = mapper.getEmpByLast_NameLikeReturnMap("%r%");
System.out.println(map);
openSession.commit();
}finally{
openSession.close();
}
}
③ resultMap——自定义结果映射规则
package com.atguigu.mybatis.dao;
import com.atguigu.mybatis.bean.Employee;
public interface EmployeeMapperPlus{
Employee getEmpById(Integer id);
}
注:记得要在MyBatis全局配置文件(mybatis-config.xml)中注册新的sql映射文件!
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select * from tbl_employee where id=#{id}
④ resultMap——关联查询_环境搭建
1)创建一张新的表 tbl_dept
create table tbl_dept(
id INT(11) primary key AUTO_INCREMENT,
departmentName VARCHAR(255)
)
2)为表 tbl_employee 加上一列,用来存储员工所在的部门id
alter table tbl_employee add COLUMN d_id INT(11);
3)将表 tbl_employee 中的字段d_id(员工所在的部门id)设置为外键
ALTER TABLE tbl_employee add CONSTRAINT fk_emp_dept FOREIGN key(d_id) REFERENCES tbl_dept(id);
4)创建部门类Department
package com.atguigu.mybatis.bean;
public class Department {
private Integer id;
private String departmentName;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
@Override
public String toString() {
return "Department{" +
"id=" + id +
", departmentName='" + departmentName + '\'' +
'}';
}
}
5)在Employee.java中添加新的属性(即员工所在的部门Department),并添加对应的 get/set 方法
private Department dept;
public Department getDept() {
return dept;
}
public void setDept(Department dept) {
this.dept = dept;
}
⑤ resultMap——级联属性封装结果
//EmployeeMapperPlus.java
Employee getEmpAndDept(Integer id);
SELECT e.id id,e.last_name last_name,e.gender gender,e.email email,e.d_id d_id,
d.id did,d.deptName deptName
FROM tbl_employee e,tbl_dept d
WHERE e.d_id=d.id AND e.id=#{id}
@Test
public void test06() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapperPlus mapper = openSession.getMapper(EmployeeMapperPlus.class);
Employee employee = mapper.getEmpAndDept(1);
System.out.println(employee);
System.out.println(employee.getDept());
openSession.commit();
}finally{
openSession.close();
}
}
⑥ resultMap——association定义关联对象封装规则
⑦ resultMap——association分步查询 & 延迟加载
1)新建DepartmentMapper.java,并定义抽象方法getDeptById()。
package com.atguigu.mybatis.dao;
import com.atguigu.mybatis.bean.Department;
public interface DepartmentMapper {
//根据部门id查询部门信息
Department getDeptById(Integer id);
}
2)新建对应的sql映射文件(记得要在MyBatis全局配置文件(mybatis-config.xml)中注册)。
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select id,deptName from tbl_dept where id=#{id}
3)在EmployeeMapperPlus.java中增加抽象方法getEmpByIdStep()。
//EmployeeMapperPlus.java
Employee getEmpByIdStep(Integer id);
4)在EmployeeMapperPlus.xml中编写对应的sql语句。
select * from tbl_employee where id=#{id}
5)测试
@Test
public void test06() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapperPlus mapper = openSession.getMapper(EmployeeMapperPlus.class);
//分步查询
Employee employee = mapper.getEmpByIdStep(1);
System.out.println(employee);
System.out.println(employee.getDept());
openSession.commit();
}finally{
openSession.close();
}
}
6)延迟加载——在MyBatis全局配置文件(mybatis-config.xml)中加上两个设置
⑧ resultMap——collection定义关联集合封装规则
1)在Department.java中添加属于该部门的所有员工的集合List,并添加相应的 get/set 方法。
package com.atguigu.mybatis.bean;
import java.util.List;
public class Department {
private Integer id;
private String deptName;
private List emps;
public List getEmps() {
return emps;
}
public void setEmps(List emps) {
this.emps = emps;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Department{" +
"id=" + id +
", deptName='" + deptName + '\'' +
'}';
}
}
2)在EmployeeMapperPlus.java中添加抽象方法getDeptByIdPlus()。
//EmployeeMapperPlus.java
//通过部门id查询部门信息,以及属于该部门的所有员工的集合List
Department getDeptByIdPlus(Integer id);
3)在DepartmentMapper.xml中编写上述方法对应的sql语句。
select d.id did,d.deptName deptName,
e.id eid,e.last_name last_name,e.email email,e.gender gender
from tbl_dept d
left join tbl_employee e
on d.id=e.d_id
where d.id=#{id};
4)测试
@Test
public void test07() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);
//查询部门id为1的部门信息,以及属于该部门的所有员工信息(在list中)
Department dept = mapper.getDeptByIdPlus(1);
//输出部门信息
System.out.println(dept);
//输出属于该部门的所有员工信息
System.out.println(dept.getEmps());
openSession.commit();
}finally{
openSession.close();
}
}
1)在EmployeeMapperPlus.java中添加抽象方法getEmpsByDeptId()。
//EmployeeMapperPlus.java
//根据部门id查询所有属于该部门的员工信息
List getEmpsByDeptId(Integer deptId);
2)在EmployeeMapperPlus.xml中编写对应的sql语句。
select * from tbl_employee where d_id=#{deptId};
3)在DepartmentMapper.java中添加抽象的分步查询方法getDeptByIdStep()。
//DepartmentMapper.java
//通过部门id查询部门信息,以及属于该部门的所有员工的集合List(分步查询)
Department getDeptByIdStep(Integer id);
4)在DepartmentMapper.xml中编写对应的sql语句。
select id,deptName from tbl_dept where id=#{id}
5)扩展——传递多列值 在collection分步查询时,其中column表示需要传递的参数,当只有一个参数时,直接写进去即可: 当需要传递多个参数时,选需要将多列的值封装在map中进行传递,column=“{key1=column1,key2=column2}”:
6)扩展——延迟加载 fetchType=“lazy”:表示使用延迟加载,fetchType=“eager”:表示使用立即加载。(该设置可以覆盖掉全局配置文件中的延迟加载) 测试代码:
@Test
public void test07() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);
Department dept = mapper.getDeptByIdStep(1);
System.out.println(dept.getDeptName());
System.out.println(dept.getEmps());
openSession.commit();
}finally{
openSession.close();
}
}
测试对比:
在EmployeeMapper.xml中添加鉴别器
5.MyBatis——动态SQL
(1)简介 & 环境搭建
① 动态 SQL是MyBatis强大特性之一,它极大地简化拼装SQL的操作,动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。此外,MyBatis 采用了功能强大的基于 OGNL 的表达式来简化操作。 ② 环境搭建 新建EmployeeMapperDynamicSQL.java
package com.atguigu.mybatis.dao;
public interface EmployeeMapperDynamicSQL {
}
新建EmployeeMapperDynamicSQL.xml(记得要在全局配置文件中注册该sql映射文件!)
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
(2)动态SQL标签——if
① 在EmployeeMapperDynamicSQL.java中添加抽象方法
//携带了哪个字段查询条件就带上这个字段的值
List getEmpsByConditionIf(Employee employee);
② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句
select * from tbl_employee
where 1=1
and id=#{id}
and last_name like #{last_name}
and email=#{email}
and gender=#{gender}
③ 测试
@Test
public void testDynamicSQL() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class);
Employee employee = new Employee(null,"%e%",null,null);
List emps = mapper.getEmpsByConditionIf(employee);
System.out.println(emps);
openSession.commit();
}finally{
openSession.close();
}
}
查询的时候如果某些条件没带可能会出现sql拼装问题,现有以下两种解决方案: ① 给where后面加上1=1,以后的条件都是and xxx(上面(2)中的代码就是这样写的)。 ② MyBatis使用 where 标签来将所有的查询条件包括在内。即在where标签拼装sql语句,并且会去掉多出来的and或者or,但是where只会去掉第一个多出来的and或者or。
select * from tbl_employee
id=#{id}
and last_name like #{last_name}
and email=#{email}
and gender=#{gender}
(3)动态SQL标签——trim
① 在EmployeeMapperDynamicSQL.java中添加抽象方法
List getEmpsByConditionTrim(Employee employee);
② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句
select * from tbl_employee
id=#{id} and
last_name like #{last_name} and
email=#{email} and
gender=#{gender}
(4)动态SQL标签——choose
① 在EmployeeMapperDynamicSQL.java中添加抽象方法
//如果带了id就用id查,如果带了last_name就用last_name查,只会进入其中一个
List getEmpsByConditionChoose(Employee employee);
② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句
select * from tbl_employee
id=#{id}
last_name like #{last_name}
email = #{email}
gender = 0
(5)动态SQL标签——set(与if结合的动态更新)
① 在EmployeeMapperDynamicSQL.java中添加抽象方法
//更新employee中不为空的属性(列),即哪列有值就更新哪列(id主键必须有值)
void updateEmp(Employee employee);
② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句
update tbl_employee
last_name=#{last_name},
email=#{email},
gender=#{gender}
where id=#{id}
(6)动态SQL标签——foreach遍历集合
① 在EmployeeMapperDynamicSQL.java中添加抽象方法
//查询所有id在给定集合中的员工
List getEmpsByConditionForeach(@Param("ids")List ids);
② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句
select * from tbl_employee
#{item_id}
③ 测试
@Test
public void testDynamicSQL() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class);
Employee employee = new Employee(null,null,null,null);
List ids = new ArrayList();
ids.add(1);
ids.add(2);
ids.add(3);
List emps = mapper.getEmpsByConditionForeach(ids);
for (Employee emp : emps) {
System.out.println(emp);
}
openSession.commit();
}finally{
openSession.close();
}
}
① 在EmployeeMapperDynamicSQL.java中添加抽象方法
//批量插入员工信息
void addEmps(@Param("emps")List emps);
② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句
--
insert into tbl_employee(last_name, email, gender,d_id)
values
(#{emp.last_name},#{emp.email},#{emp.gender},#{emp.dept.id})
insert into tbl_employee(last_name,email,gender,d_id)
values(#{emp.last_name},#{emp.email},#{emp.gender},#{emp.dept.id})
③ 测试
@Test
public void testBatchSave() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class);
List emps = new ArrayList();
emps.add(new Employee(null,"Smith","1","Smith@163.com",new Department(1)));
emps.add(new Employee(null,"Smith2","1","Smith2@163.com",new Department(2)));
9 mapper.addEmps(emps);
openSession.commit();
}finally{
openSession.close();
}
}
(8)动态SQL标签——内置参数_parameter和_databaseId
① 在EmployeeMapperDynamicSQL.java中添加抽象方法
List getEmpsTestInnerParameter(Employee employee);
② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句
select * from tbl_employee
where last_name like #{_parameter.last_name}
select * from tbl_employee
(9)动态SQL标签——bind绑定
bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值。以上面的sql语句为例:
select * from tbl_employee
where last_name like #{_lastName}
select * from tbl_employee
测试代码如下:
@Test
public void testBind() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class);
Employee employee = new Employee();
employee.setLast_name("e");
List emps = mapper.getEmpsTestInnerParameter(employee);
System.out.println(emps);
openSession.commit();
}finally{
openSession.close();
}
}
(10)动态SQL标签——抽取可重用的sql片段
抽取可重用的sql片段,方便后面引用 ① sql抽取:将经常要查询的列名,或者插入用的列名抽取出来,方便引用; ② 用include来引用已经抽取的sql; ③ 此外,include还可以自定义一些property,sql标签内部就能使用自定义的属性;include-property:正确的取值方式为${prop}
employee_id,last_name,email
last_name,email,gender,d_id
insert into tbl_employee()
values
(#{emp.last_name},#{emp.email},#{emp.gender},#{emp.dept.id})
6.MyBatis——缓存机制
(1)缓存介绍
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存极大地提升了查询效率。MyBatis中默认定义了两级缓存,即一级缓存和二级缓存。
① 一级缓存一级缓存(local cache),即本地缓存,作用域默认为sqlSession。当 Session flush 或 close 后,该Session 中的所有 Cache 将被清空。除此之外,本地缓存不能被关闭,但是可以调用 clearCache() 来清空本地缓存,或者改变缓存的作用域。
② 二级缓存二级缓存(second level cache),即全局作用域缓存,它是基于namespace级别的缓存,一个namespace对应一个二级缓存。二级缓存在默认情况下不开启,若要开启则需要手动配置。MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口。 二级缓存的工作机制如下: 1)一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中; 2)如果会话关闭,一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容; 3)不同namespace查出的数据会放在自己对应的缓存中(map) 4)查出的数据都会被默认先放在一级缓存中,只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中,那么此时的效果就是数据会从二级缓存中获取。
(2)一级缓存体验同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中。
@Test
public void testFirstLevelCache() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp01 = mapper.getEmpById(1);
System.out.println(emp01);
//xxx
//再次调用getEmpById()方法
Employee emp02 = mapper.getEmpById(1);
System.out.println(emp02);
//判断emp01和emp02是否为同一个对象
System.out.println(emp01==emp02);
openSession.commit();
}finally{
openSession.close();
}
}
① 不同的SqlSession对应不同的一级缓存 ② 同一个SqlSession但是查询条件不同 ③ 两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响) ④ 同一个SqlSession两次查询期间手动清空了缓存(openSession.clearCache())
(4)二级缓存的使用① 在全局配置文件 mybatis-config.xml 中开启全局二级缓存配置:
② 去相应的mapper.xml(以EmployeeMapper.xml为例)中配置使用二级缓存:
③ 相应的POJO(Employee.java、Department.java)需要实现序列化接口
④ 测试
@Test
public void testSecondLevelCache() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
SqlSession openSession2 = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
Employee emp01 = mapper.getEmpById(1);
System.out.println(emp01);
//会话关闭,那么一级缓存中的数据会被保存到二级缓存中,新的会话查询信息,就可以参照二级缓存中的内容
openSession.close();
//第二次查询是从二级缓存中拿到的数据,并没有发送新的sql
Employee emp02 = mapper2.getEmpById(1);
System.out.println(emp02);
System.out.println(emp01==emp02);
openSession.close();
}finally{
}
}
③ 在EmployeeMapper.xml文件中进行配置
④ 测试
@Test
public void testSecondLevelCache() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
SqlSession openSession2 = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
Employee emp01 = mapper.getEmpById(1);
System.out.println(emp01);
openSession.close();
//第二次查询是从二级缓存中拿到的数据,并没有发送新的sql
Employee emp02 = mapper2.getEmpById(1);
System.out.println(emp02);
System.out.println("emp01==emp02的结果为:"+(emp01==emp02));
openSession.close();
}finally{
}
}
在DepartmentMapper.xml中引用缓存
7.MyBatis——SSM框架整合
此处的SSM框架整合未使用Maven等项目管理工具,若想看Maven版本的可以查看这篇文章。
(1)在IDEA中新建一个Java Web项目
右键lib,点击"Add as Library…",再点击OK即可。
整体的项目结构如下: 有需要的可以下载本项目的源代码(提取码:test)
MyBatis Generator简称MBG,是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件、接口、以及bean类等。它支持基本的增删改查,以及QBC风格的条件查询。但是表连接、存储过程等这些复杂sql的定义需要我们手工编写。
(2)编写相应的配置文件及代码① 将之前Mybatis项目复制一份并改名为MyBatis_07_mbg,删除一些不用的文件,并导入Myabtis Generator的包: ② 新建mbg.xml文件,放在src目录下(可以先从官网中复制过来,然后再根据实际情况进行修改)
DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
③ 使用MyBatis逆向工程生成相应的代码及配置,即运行下面官网提供的代码:
@Test
public void testMbg() throws Exception {
List warnings = new ArrayList();
boolean overwrite = true;
File configFile = new File("mbg.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);
}
④ 测试
@Test
public void testMyBatis3() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//xxxExample就是封装查询条件的
//1、查询所有
//List emps = mapper.selectByExample(null);
//2、查询员工名字中有e字母的,和员工性别是1的
//封装员工查询条件的example
EmployeeExample example = new EmployeeExample();
//创建一个Criteria,这个Criteria就是拼装查询条件
//select id, last_name, email, gender, d_id from tbl_employee
//WHERE ( last_name like ? and gender = ? ) or email like "%e%"
Criteria criteria = example.createCriteria();
criteria.andLastNameLike("%e%");
criteria.andGenderEqualTo("1");
Criteria criteria2 = example.createCriteria();
criteria2.andEmailLike("%e%");
example.or(criteria2);
List list = mapper.selectByExample(example);
for (Employee employee : list) {
System.out.println(employee.getId());
}
}finally{
openSession.close();
}
}
9.MyBatis——工作原理
(1)框架分层架构
MyBatis在四大对象Executor、ParameterHandler、ResultSetHandler、StatementHandler的创建过程中,都会有插件进行介入。插件可以利用动态代理机制一层层的包装目标对象,而实现在目标对象执行目标方法之前进行拦截的效果。MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。 ① 每个创建出来的对象不是直接返回的,而是调用方法 interceptorChain.pluginAll(parameterHandler) 后返回。
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
② pluginAll() 方法获取到所有的Interceptor(拦截器,插件需要实现的接口)后,调用interceptor.plugin(target),即返回target包装后的对象。 ③ 插件机制,我们可以使用插件为目标对象创建一个代理对象(即AOP,面向切面编程),我们的插件可以为四大对象创建出代理对象,代理对象就可以拦截到四大对象的每一个执行。
(2)编写插件以入门案例的代码为基础来编写插件,其具体步骤如下: ① 编写Interceptor的实现类 ② 使用@Intercepts注解完成插件签名
package com.atguigu.mybatis.dao;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.util.Properties;
//完成插件签名:告诉MyBatis当前插件用来拦截哪个对象的哪个方法
@Intercepts(
{
@Signature(type= StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MyFirstPlugin implements Interceptor {
//intercept:拦截目标对象的目标方法的执行
@Override
public Object intercept(Invocation invocation) throws Throwable {
//执行目标方法
Object proceed = invocation.proceed();
//返回执行后的返回值
return null;
}
//plugin:包装目标对象的,包装:为目标对象创建一个代理对象
@Override
public Object plugin(Object target) {
//我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象
System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象"+target);
Object wrap = Plugin.wrap(target, this);
//返回为当前target创建的动态代理
return wrap;
}
//setProperties:将插件注册时的property属性设置进来
@Override
public void setProperties(Properties properties) {
System.out.println("插件配置的信息:"+properties);
}
}
③ 将写好的插件注册到全局配置文件mybatis-config中
④ 运行任意一个测试方法
@Test
public void test02() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
System.out.println(mapper.getClass());
System.out.println(employee);
}finally{
openSession.close();
}
}
① 编写第二个插件
package com.atguigu.mybatis.dao;
import java.util.Properties;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
@Intercepts(
{
@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MySecondPlugin implements Interceptor{
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("MySecondPlugin...intercept:"+invocation.getMethod());
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
// TODO Auto-generated method stub
System.out.println("MySecondPlugin...plugin:"+target);
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
}
}
② 注册插件
③ 测试运行
@Override
public Object intercept(Invocation invocation) throws Throwable {
// TODO Auto-generated method stub
System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod());
//动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询3号员工
Object target = invocation.getTarget();
System.out.println("当前拦截到的对象:"+target);
//拿到:StatementHandler==>ParameterHandler===>parameterObject
//拿到target的元数据
MetaObject metaObject = SystemMetaObject.forObject(target);
Object value = metaObject.getValue("parameterHandler.parameterObject");
System.out.println("sql语句用的参数是:"+value);
//修改完sql语句要用的参数
metaObject.setValue("parameterHandler.parameterObject", 3);
//执行目标方法
Object proceed = invocation.proceed();
//返回执行后的返回值
return proceed;
}
(5)PageHelper分页插件的使用
① PageHelper插件官网地址 ② 以入门案例的代码为例,在其基础上使用PageHelper插件。 1)导入需要的 jar 包 2)在全局配置文件mybatis-config中注册pageHelper插件
3)测试使用
@Test
public void testPageHelper01() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//查询第1页的5条记录
Page page = PageHelper.startPage(1, 5);
List emps = mapper.getEmps();
for (Employee emp : emps) {
System.out.println(emp);
}
System.out.println("当前页码:"+page.getPageNum());
System.out.println("总记录数:"+page.getTotal());
System.out.println("每页的记录数:"+page.getPageSize());
}finally{
openSession.close();
}
}
@Test
public void testPageHelper02() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//查询第5页的1条记录
Page page = PageHelper.startPage(5, 1);
List emps = mapper.getEmps();
//传入要连续显示多少页
PageInfo info = new PageInfo(emps, 5);
for (Employee employee : emps) {
System.out.println(employee);
}
System.out.println("当前页码:"+info.getPageNum());
System.out.println("总记录数:"+info.getTotal());
System.out.println("每页的记录数:"+info.getPageSize());
System.out.println("总页码:"+info.getPages());
System.out.println("是否第一页:"+info.isIsFirstPage());
int[] nums = info.getNavigatepageNums();
System.out.println("连续显示的页码:");
//当前页码为第5页,在连续显示时,当前页码居中
for (int i = 0; i
insert into tbl_employee(last_name, gender, email)
values (#{last_name},#{gender},#{email})
③ 测试
@Test
public void testBatch() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//可以执行批量操作的sqlSession
SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
long start = System.currentTimeMillis();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//插入1000条数据
for (int i = 0; i 设置参数===>10000次===>执行(1次))
//Parameters: 616c1(String), 616c1@163.com(String), 1(String)==>4598
//非批量:(预编译sql=设置参数=执行)==》10000 10200
System.out.println("执行时长:"+(end-start)+"毫秒");
}finally{
openSession.close();
}
}
④ 与Spring整合中,推荐额外的配置一个可以专门用来执行批量操作的sqlSession
需要用到批量操作的时候,可以注入配置的这个批量SqlSession,通过它获取到mapper映射器进行操作即可。
(2)自定义类型处理器——MyBatis中枚举类型的默认处理① 创建枚举类EmpStatus,然后在Employee.java中将该类作为其属性并构建 get/set 方法。
package com.atguigu.mybatis.bean;
public enum EmpStatus {
//员工状态
LOGIN,LOGOUT,REMOVE
}
//员工状态
private EmpStatus empStatus=EmpStatus.LOGOUT;
② 在表tbl_employee中新增一列,表示员工状态,同时也需要改变aEmployeeMapper.xml中对应的sql语句
alter table tbl_employee add empStatus varchar(11)
insert into tbl_employee(last_name, gender, email,empStatus)
values (#{last_name},#{gender},#{email},#{empStatus})
③ 测试
/*
* 默认mybatis在处理枚举对象的时候保存的是枚举的名字:EnumTypeHandler
* 改变使用:EnumOrdinalTypeHandler:
*/
@Test
public void testEnum() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = new Employee("test_enum", "enum@163.com","1");
mapper.addEmp(employee);
openSession.commit();
}finally{
openSession.close();
}
}
在全局配置文件mybatis-config.xml添加如下配置之后,再次测试时便保存枚举的索引
① 重新编写枚举EmpStatus类
package com.atguigu.mybatis.bean;
//希望数据库保存的是100,200这些状态码,而不是默认0,1或者枚举的名
public enum EmpStatus {
LOGIN(100,"用户登录"),LOGOUT(200,"用户登出"),REMOVE(300,"用户不存在");
private Integer code;
private String msg;
private EmpStatus(Integer code,String msg){
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
//按照状态码返回枚举对象
public static EmpStatus getEmpStatusByCode(Integer code){
switch (code) {
case 100:
return LOGIN;
case 200:
return LOGOUT;
case 300:
return REMOVE;
default:
return LOGOUT;
}
}
}
② 自定义TypeHandler类
package com.atguigu.mybatis.typehandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import com.atguigu.mybatis.bean.EmpStatus;
//1、实现TypeHandler接口,或者继承BaseTypeHandler
public class MyEnumEmpStatusTypeHandler implements TypeHandler {
//定义当前数据如何保存到数据库中
@Override
public void setParameter(PreparedStatement ps, int i, EmpStatus parameter,
JdbcType jdbcType) throws SQLException {
// TODO Auto-generated method stub
System.out.println("要保存的状态码:"+parameter.getCode());
ps.setString(i, parameter.getCode().toString());
}
@Override
public EmpStatus getResult(ResultSet rs, String columnName)
throws SQLException {
// TODO Auto-generated method stub
//需要根据从数据库中拿到的枚举的状态码返回一个枚举对象
int code = rs.getInt(columnName);
System.out.println("从数据库中获取的状态码:"+code);
EmpStatus status = EmpStatus.getEmpStatusByCode(code);
return status;
}
@Override
public EmpStatus getResult(ResultSet rs, int columnIndex)
throws SQLException {
// TODO Auto-generated method stub
int code = rs.getInt(columnIndex);
System.out.println("从数据库中获取的状态码:"+code);
EmpStatus status = EmpStatus.getEmpStatusByCode(code);
return status;
}
@Override
public EmpStatus getResult(CallableStatement cs, int columnIndex)
throws SQLException {
// TODO Auto-generated method stub
int code = cs.getInt(columnIndex);
System.out.println("从数据库中获取的状态码:"+code);
EmpStatus status = EmpStatus.getEmpStatusByCode(code);
return status;
}
}
③ 在全局配置文件mybatis-config.xml注册