mybatis学习系列五--插件及类型处理器
2 插件编写(80-81)
单个插件编写
2.1实现interceptor接口(ibatis)
invocation.proceed()方法执行必须要有,否则不会无法实现拦截作用
2.2 使用@intercepts注解完成插件签名
2.3 将插件注册到全局配置文件中<plugins>标签
全局配置文件注册plugin时报错:
The content of element type "configuration" must match
"(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
原因:configuration 中的元素不仅有类型限制,还有顺序限制
https://blog.csdn.net/u011199063/article/details/78971527
按照提示的顺序即可
多个插件
插件动态代理时,按插件配置顺序配置层层代理对象,执行目标方法时,按逆向顺序执行
配置的插件顺序
<!-- 自定义插件 -->
<plugins>
<plugin interceptor="com.mybatis.bean.MyPlugin">
<!-- 配置插件属性,在插件setProperties方法中使用 -->
<property name="username1" value="root1"/>
<property name="password1" value="1"/>
</plugin>
<plugin interceptor="com.mybatis.bean.MySecondPlugin">
<property name="username2" value="root2"/>
<property name="password2" value="2"/>
</plugin>
</plugins>
执行打印日志:
22:29:05,207 INFO Class:50 - myfirstplugin plugin:org.apache.ibatis.executor.SimpleExecutor@24b1d79b
22:29:05,208 INFO Class:50 - mysecondplugin plugin:org.apache.ibatis.executor.SimpleExecutor@24b1d79b
22:29:05,239 INFO Class:50 - myfirstplugin plugin:org.apache.ibatis.scripting.defaults.DefaultParameterHandler@70beb599
22:29:05,239 INFO Class:50 - mysecondplugin plugin:org.apache.ibatis.scripting.defaults.DefaultParameterHandler@70beb599
22:29:05,242 INFO Class:50 - myfirstplugin plugin:org.apache.ibatis.executor.resultset.DefaultResultSetHandler@365c30cc
22:29:05,242 INFO Class:50 - mysecondplugin plugin:org.apache.ibatis.executor.resultset.DefaultResultSetHandler@365c30cc
22:29:05,243 INFO Class:50 - myfirstplugin plugin:org.apache.ibatis.executor.statement.RoutingStatementHandler@4148db48
22:29:05,243 INFO Class:50 - mysecondplugin plugin:org.apache.ibatis.executor.statement.RoutingStatementHandler@4148db48
显示包装顺序按配置顺序
再看执行intercept方法执行顺序:
22:29:05,272 INFO Class:40 - myfirstplugin intercept:null
22:29:05,272 INFO Class:40 - mysecondplugin intercept:null
也是按顺序执行,并不是如图所示的包装按正序,执行按逆序
3 插件开发(82)
只有再插件类中intercept方法中调用invocation.proceed()方法才能执行拦截的目标方法
简单开发:修改sql参数
//获取拦截的对象
Object target=invocation.getTarget();
log.info(target);
//获取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();
log.info("myfirstplugin intercept:"+proceed);
执行后打印信息:
sql语句的参数:2
22:00:27,942 DEBUG getMyDeptById:145 - ==> Parameters: 3(Integer)
可见,sql参数从2修改为3了
4 pagehelper分页插件(83)
引入:
1pom配置
<!-- pagehelper分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.4</version>
</dependency>
2全局配置文件中配置
mybatis-config.xml
<plugins>
<!-- 分页插件 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
<property name="offsetAsPageNum" value="false"/>
<property name="rowBoundsWithCount" value="false"/>
<property name="pageSizeZero" value="true"/>
<property name="reasonable" value="false"/>
<property name="supportMethodsArguments" value="false"/>
<property name="returnPageInfo" value="none"/>
</plugin>
</plugins>
3 测试类
EmployeeMapper mapper=session.getMapper(EmployeeMapper.class);
//获取页码信息方式1 (startPage方式)
Page<Object> startPage = PageHelper.startPage(3, 3);
List<Employee> emps=mapper.selectEmployeeByInnerParameter(null);
// System.out.println("当前页码:"+startPage.getPageNum());
// System.out.println("总记录数:"+startPage.getTotal());
// System.out.println("每页记录数:"+startPage.getPageSize());
// System.out.println("总页码:"+startPage.getPages());
// System.out.println("分页后当前页码数据条数:"+emps.size());
//获取页码信息方式2 (pageInfo信息更丰富)
// PageInfo<Employee> pageInfo = new PageInfo<>(emps);
//2个参数,后面参数表示分页导航,连续显示多少页码(即每次显示5页,如2,3,4,5,6)
PageInfo<Employee> pageInfo = new PageInfo<>(emps,2);
System.out.println("当前页码:"+pageInfo.getPageNum());
System.out.println("总记录数:"+pageInfo.getTotal());
System.out.println("每页记录数:"+pageInfo.getPageSize());
System.out.println("总页码:"+pageInfo.getPages());
System.out.println("分页后当前页码数据条数:"+emps.size());
System.out.println("是否第一页:"+pageInfo.isIsFirstPage());
System.out.println("连续显示页码:"+pageInfo.getNavigatePages());
int[] navigatepageNums = pageInfo.getNavigatepageNums();
for ( int i=0;i<navigatepageNums.length;i++) {
System.out.println(navigatepageNums[i]);
}
输出结果:
当前页码:3
总记录数:11
每页记录数:3
总页码:4
分页后当前页码数据条数:3
是否第一页:false
连续显示页码:2
2
3
5 mybatis批量操作(84)
1采用batch方式进行批量插入操作:
//batch类型的session
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
EmployeeMapper mapper=session.getMapper(EmployeeMapper.class);
long start=System.currentTimeMillis();
for(int i=0;i<1000;i++) {
mapper.addEmp(new Employee(null,"name_"+i, "email_"+i+"@com.cn", (i%2)+"", (i%2)));
}
session.commit();
long end=System.currentTimeMillis();
System.out.println("执行时间:"+(end-start));
} finally {
session.close();
}
执行效果:
21:49:06,354 DEBUG addEmp:145 - ==> Parameters: name_304(String), email_304@com.cn(String), 0(String), 0(Integer)
21:49:06,355 DEBUG addEmp:145 - ==> Parameters: name_305(String), email_305@com.cn(String), 1(String), 1(Integer)
21:49:06,355 DEBUG addEmp:145 - ==> Parameters: name_306(String), email_306@com.cn(String), 0(String), 0(Integer)
21:49:06,355 DEBUG addEmp:145 - ==> Parameters: name_307(String), email_307@com.cn(String), 1(String), 1(Integer)
21:49:06,356 DEBUG addEmp:145 - ==> Parameters: name_308(String), email_308@com.cn(String), 0(String), 0(Integer)
执行时间:
21:49:07,208 DEBUG JdbcTransaction:69 - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6ebc05a6]
执行时间:1198
2非batch方式批量操作:
//非batch批量操作session
SqlSession session = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper=session.getMapper(EmployeeMapper.class);
long start=System.currentTimeMillis();
for(int i=0;i<1000;i++) {
mapper.addEmp(new Employee(null,"name_"+i, "email_"+i+"@com.cn", (i%2)+"", (i%2)));
}
session.commit();
long end=System.currentTimeMillis();
System.out.println("执行时间:"+(end-start));
} finally {
session.close();
}
执行情况:
21:52:44,356 DEBUG addEmp:145 - ==> Parameters: name_987(String), email_987@com.cn(String), 1(String), 1(Integer)
21:52:44,356 DEBUG addEmp:145 - <== Updates: 1
21:52:44,356 DEBUG addEmp:145 - ==> Preparing: insert into myemployeee(last_name,email,gender,dept_id) values (?,?, ?,?)
21:52:44,356 DEBUG addEmp:145 - ==> Parameters: name_988(String), email_988@com.cn(String), 0(String), 0(Integer)
21:52:44,357 DEBUG addEmp:145 - <== Updates: 1
执行时间:
执行时间:1376
原因:
批量batch方式:(预编译sql一次==》设置参数==》1000次==)
非批量:(预编译sql==》设置参数==》执行)==》1000次
执行次数 |
非批量执行时间(ms) |
批量执行时间(ms) |
1000 |
1376 |
1198 |
10000 |
11649 |
3607 |
50000 |
37831 |
10593 |
100000 |
62868 |
20195 |
性能在3倍左右
spring整合时,如果获取bath的sqlsession
配置带batch的sqlsession并使用
使用时引入:
6mybatis存储过程(85-86)
可以理解为sql集合,复杂sql情况下
Oracle中分页借助伪列(rowid)
先<=end,再>=start (注意如果先>=start再<=end会导致数据不确定而错误)
1创建分页类:
/**
* 分页类
* @author admin
*
*/
public class Page {
private int start;
private int end;
private int count;
private List<Employee> emps;
public int getStart() {
return start;
}
分页查询:先查询总记录数,再进行分页查询
2/**创建oracle存储过程*/
create or replace procedure
hello_test(//存储过程名
p_start in int,//p_start-->参数开始行;in-->表示输入;int表示参数类型
p_end in int,//p_end-->参数结束行;in-->表示输入;int表示参数类型
p_count out int,//输出
p_emps out sys_refcursor//输出,游标
) as
begin
select count(*) into p_count from employee;//将查询的总记录数交给p_count
open p_emps for //打开游标
select * from
(select rownum rn,e.* from employee e where rownum<=p_end)
where rn>=p_start;
end hello_test;
3使用存储过程:
3.1Select * from user_source;
查看存储过程
3.2mybatis调用存储过程
1)定义查询接口
//调用存储过程分页(参数为定义的page)
void getPageByProcedure(Page page);
2)mapper中定义查询存储过程的sql
<!-- statementType="CALLABLE" 表示调用存储过程 -->
<select id="getPageByProcedure" statementType="CALLABLE" databaseId="oracle">
<!-- 固定用法 { call 存储过程名(参数1,参数2,...) } -->
{call
hello_test(
#{start,mode=IN,jdbcType=INTEGER},
#{end,mode=IN,jdbcType=INTEGER},
#{end,mode=IN,jdbcType=INTEGER},
#{count,mode=OUT,jdbcType=INTEGER},
#{emps,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=resultMap}
<!-- 游标处理,使用ResultSet封装结果集 -->
) }
</select>
3)调用测试
//存储过程查询
EmployeeMapper mapper=session.getMapper(EmployeeMapper.class);
Page page = new Page();
page.setStart(1);
page.setEnd(10);
mapper.getPageByProcedure(page);
System.out.println("总记录数:"+page.getCount());
System.out.println("查出的数据:"+page.getEmps().size());
7 mybatis存自定义类型处理器(87)
自定义typehandler处理枚举类型
设置参数和处理结果时调用typehandler处理参数,将java类型和数据库类型映射。
查看typehandler接口:
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
初步使用枚举
/**
* 枚举工具类
* @author admin
*
*/
public class EnumUtils {
//雇员状态
public enum EmployeeStaus{
LOGIN,//登陆
LONGOUT,//推出
REMOVE//移除
}
//枚举测试
public static void main(String[] args) {
EmployeeStaus status=EmployeeStaus.LOGIN;
System.out.println("枚举的索引:"+status.ordinal());
System.out.println("枚举的名字:"+status.name());
}
}
输出:
枚举的索引:0
枚举的名字:LOGIN
即枚举有索引和名字,接下来测试mybatis默认存放的是什么
1)表myemployeee新增字段status
-- 新增状态字段
alter table myemployeee add column status varchar(11);
2) bean对象添加对应属性
private EmployeeStaus status=EmployeeStaus.LONGOUT;//状态枚举类型,默认退出状态
并提供构造方法:
public Employee(String lastName, String email, String gender, Integer deptId, MyDept myDept,
EmployeeStaus status) {
super();
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.deptId = deptId;
this.myDept = myDept;
this.status = status;
}
3)mapper新增插入字段
<insert id="addEmp" parameterType="com.mybatis.bean.Employee">
insert into myemployeee(last_name,email,gender,dept_id,status)
values (#{lastName,jdbcType=VARCHAR},#{email,jdbcType=VARCHAR},
#{gender,jdbcType=VARCHAR},#{deptId,jdbcType=INTEGER},#{status})
</insert>
4)测试插入:
EmployeeMapper mapper=session.getMapper(EmployeeMapper.class);
mapper.addEmp(new Employee("test11", "test11@com.cn",
1+"", 1, null, EmployeeStaus.LONGOUT));
5)查看数据库存入数据
select t.* from myemployeee t order by t.id desc limit 1;
可以看到默认存储的是枚举的名字
原因:
默认使用enumTypehandler处理(包含EnumTypeHandler和EnumOrdinalTypeHandler共2种类型)
设置参数时代码如下:
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
if (jdbcType == null) {
ps.setString(i, parameter.name());
} else {
ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
}
}
可见保存的是枚举的名字。
变更处理器
可以改变使用EnumOrdinalTypeHandler,
在全局配置器中设置:
<!-- 处理指定enum类型,不指定则处理所有enum类型 -->
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.mybatis.bean.EnumUtils.EmployeeStaus"/>
</typeHandlers>
执行报错:
## The error may exist in SQL Mapper Configuration
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'com.mybatis.bean.EnumUtils.EmployeeStaus'. Cause: java.lang.ClassNotFoundException: Cannot find class: com.mybatis.bean.EnumUtils.EmployeeStaus
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64)
at com.mybatis.bean.Mytest_eum_test.getSqlSessionFactory(Mytest_eum_test.java:45)
at com.mybatis.bean.Mytest_eum_test.main(Mytest_eum_test.java:29)
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'com.mybatis.bean.EnumUtils.EmployeeStaus'. Cause: java.lang.ClassNotFoundException: Cannot find class: com.mybatis.bean.EnumUtils.EmployeeStaus
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:120)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:98)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:78)
... 3 more
Caused by: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'com.mybatis.bean.EnumUtils.EmployeeStaus'. Cause: java.lang.ClassNotFoundException: Cannot find class: com.mybatis.bean.EnumUtils.EmployeeStaus
at org.apache.ibatis.builder.BaseBuilder.resolveClass(BaseBuilder.java:118)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.typeHandlerElement(XMLConfigBuilder.java:337)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:117)
... 5 more
Caused by: org.apache.ibatis.type.TypeException: Could not resolve type alias 'com.mybatis.bean.EnumUtils.EmployeeStaus'. Cause: java.lang.ClassNotFoundException: Cannot find class: com.mybatis.bean.EnumUtils.EmployeeStaus
at org.apache.ibatis.type.TypeAliasRegistry.resolveAlias(TypeAliasRegistry.java:120)
at org.apache.ibatis.builder.BaseBuilder.resolveAlias(BaseBuilder.java:149)
at org.apache.ibatis.builder.BaseBuilder.resolveClass(BaseBuilder.java:116)
... 7 more
Caused by: java.lang.ClassNotFoundException: Cannot find class: com.mybatis.bean.EnumUtils.EmployeeStaus
at org.apache.ibatis.io.ClassLoaderWrapper.classForName(ClassLoaderWrapper.java:200)
at org.apache.ibatis.io.ClassLoaderWrapper.classForName(ClassLoaderWrapper.java:89)
at org.apache.ibatis.io.Resources.classForName(Resources.java:261)
at org.apache.ibatis.type.TypeAliasRegistry.resolveAlias(TypeAliasRegistry.java:116)
使用另一种方式(不在全局配置中配置,仅在执行处指定)
<insert id="addEmp" parameterType="com.mybatis.bean.Employee">
insert into myemployeee(last_name,email,gender,dept_id,status)
values (#{lastName,jdbcType=VARCHAR},#{email,jdbcType=VARCHAR},
#{gender,jdbcType=VARCHAR},#{deptId,jdbcType=INTEGER},#{status,typeHandler=org.apache.ibatis.type.EnumOrdinalTypeHandler})
</insert>
测试:
EmployeeMapper mapper=session.getMapper(EmployeeMapper.class);
mapper.addEmp(new Employee("test22", "test22@com.cn",
1+"", 1, null, EmployeeStaus.LONGOUT));
session.commit();
查看数据库:
mybatis学习系列五--插件及类型处理器的更多相关文章
- MyBatis学习系列二——增删改查
目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring 数据库的经典操作:增删改查. 在这一章我们主要说明一下简单的查询和增删改, ...
- scrapy爬虫学习系列五:图片的抓取和下载
系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备: http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...
- MyBatis学习系列三——结合Spring
目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring MyBatis在项目中应用一般都要结合Spring,这一章主要把MyBat ...
- MyBatis学习系列一之环境搭建
目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring 学习一个新的知识,首先做一个简单的例子使用一下,然后再逐步深入.MyBat ...
- Mybatis学习系列(五)关联查询
前面几节的示例基本都是一些单表查询,实际项目中,经常用到关联表的查询,比如一对一,一对多等情况.在Java实体对象中,一对一和一对多可是使用包装对象解决,属性使用List或者Set来实现,在mybat ...
- mybatis学习系列一
1引入dtd约束(6) Mybatis git地址:https://github.com/mybatis/mybatis-3/wiki/Maven 指导手册:http://www.mybatis.or ...
- MyBatis学习 之 五、MyBatis配置文件
在定义sqlSessionFactory时需要指定MyBatis主配置文件: <bean id="sqlSessionFactory" class="org.myb ...
- Mybatis学习系列(二)Mapper映射文件
Mapper映射文件,作用是用来配置SQL映射语句,根据不同的SQL语句性质,使用不同的标签,mapper文件中常用的标签有<iselect>.<insert>.<upd ...
- RabbitMQ入门学习系列(五) Exchange的Direct类型
快速阅读 利用Exchange的Direct类型,实现对队列的过滤,消费者启动以后,输入相应的key值,攻取该key值对应的在队列中的消息 . 从一节知道Exchange有四种类型 Direct,To ...
随机推荐
- Java 8 停止维护,Java 9 难产,IDEA 2018 发布,还有……
祝大家五一劳动节快乐,工作顺利! 又到了总结上个月干货的时候了,这个月我们带来了各种Java技术干货,各种送书抽奖福利,各种面试题分享,各种最新动态资讯等. 5.1重磅活动 | 区块链免费送书 &am ...
- 机器学习与Tensorflow(7)——tf.train.Saver()、inception-v3的应用
1. tf.train.Saver() tf.train.Saver()是一个类,提供了变量.模型(也称图Graph)的保存和恢复模型方法. TensorFlow是通过构造Graph的方式进行深度学习 ...
- python3模块: os
简介 os模块主要用于提供系统高级别的操作. 常用方法 os.access(path, mode) # 检验权限模式 os.chdir(path) # 改变当前工作目录 os.chflags(path ...
- 2-4 完整Todolist案例
在2-3 的基础上继续,综合前面的Todolist编写一个完整的案例,实现基本的输入内容,添加内容,点击删除内容 稍微讲解54行代码 splice()的用法
- underscore.js源码解析【'_'对象定义及内部函数】
(function() { // Baseline setup // -------------- // Establish the root object, `window` (`self`) in ...
- python学习之切片
所谓切片,其实是列表的部分元素——Python称之为切片.要创建切片,可指定要使用的第一个元素和最后一个元素的索引 . players = ['charles', 'martina', 'michae ...
- Docker容器绑定外部IP和端口
Docker允许通过外部访问容器或者容器之间互联的方式来提供网络服务. 以下操作通过myfirstapp镜像模拟,如何制作myfirstapp镜像请点击此处. 1.外部访问容器容器启动之后,容器中可以 ...
- C++ 重载运算符简单举例
我们可以重定义或重载大部分 C++ 内置的运算符.这样,就能使用自定义类型的运算符. 重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的.与其他函数一 ...
- C#基础---浅谈XML读取以及简单的ORM实现
背景: 在开发ASP.NETMVC4 项目中,虽然web.config配置满足了大部分需求,不过对于某些特定业务,我们有时候需要添加新的配置文件来记录配置信息,那么XML文件配置无疑是我们选择的一个方 ...
- Shiro学习总结(1)——Apache Shiro简介
1.1 简介 Apache Shiro是Java的一个安全框架.目前,使用Apache Shiro的人越来越多,因为它相当简单,对比springSecurity,可能没有Spring Securit ...