分页插件PageHelper

其实Mybstis内部有实现逻辑分页的功能,但是较为麻烦和难用。这里记录一个分页插件PageHelper的使用,我们可以在它的github地址https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md 中看到使用说明。

步骤一:引入jar包,我们使用Maven引入即可

        <!-- PageHelper分页工具 -->
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.1</version>
</dependency>

步骤二:配置拦截器插件,在Mybatis的全局配置文件的plugins元素中配置这个插件

  <!-- 配置PageHelper -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 可以配置让分页参数合理化(就是不会出现负数和大于最大页数的情况) -->
<property name="reasonable" value="true"/>
</plugin>
</plugins>

我在这里配置了一个resonable属性,并将其值设置为true,这是为了使分页的页码数值合理化,不能出现不合理的页码(复数或大于最大页码的数值出现)

还有许多其他的属性,具体的看使用说明中有详细的参数说明。

步骤三:在代码中使用

        //引入PageHelper分页插件
//传入当前页和每页记录数
PageHelper.startPage(pn, 5); //startPage后面的查询就变成了分页查询
List<Employee> list=emplService.getAllEmps();

之前我们知道了插件使用其实是拦截器,插件都会拦截四大对象,所以会对我们的操作进行修改,所以它后面的查询结果都会自动完成(根据我们传入的值)分页

PageHelper.startPage 的两个参数,第一个表示的是当前页码(就是在界面上显示的页码数,从1开始,而不是数据库中记录的从0开始);第二个参数是每页记录数(需要查几条数据)

Ok,返回给前端的数据就是分页后的数据,如果我们还需要得到一些如:当前页,是否为第一页,是否为最后一页,有没有下一页之类的信息,使用PageInfo对象即可

            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Page<Object> page = PageHelper.startPage(5, 1); List<Employee> emps = mapper.getEmps();
//传入要连续显示多少页
PageInfo<Employee> info = new PageInfo<>(emps);
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());

还有很多的分页信息都可以从PageInfo对象中获得,具体的查看文档就行了

其实 PageHelper.startPage 会返回一个Page对象,这个Page对象也有一些分页信息,但是不如PageInfo的详细

在初始化PageInfo的时候,可以传入第二个参数表示要连续显示多少页

PageInfo<Employee> info = new PageInfo<>(emps, 5);
System.out.println("连续显示的页码:");
int[] nums = info.getNavigatepageNums();
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}

连续显示多少页就是说  当前是5  那么1,2,3页的时候,页码显示的都是12345 ;到第4页时,页码显示的就是23456。页码始终保持5页

如果我们后台不需要处理页码这些分页信息,但是还需要这些信息供前端处理,那么我们直接返回这个PageInfo对象就行

@RequestMapping("/getAllEmps.action")
@ResponseBody
public PageInfo getAllEmpsWithJson(@RequestParam(value="pn",defaultValue="1") Integer pn){
//引入PageHelper分页插件
//传入当前页和每页记录数
PageHelper.startPage(pn, 5);

//startPage后面的查询就变成了分页查询
List<Employee> list=emplService.getAllEmps(); //将结果给PageInfo,并将PageInfo交给页面即可
//PageInfo封装了查出来的数据和页码数据
//这个构造器表示连续显示5页
PageInfo pageInfo=new PageInfo(list, 5);
return pageInfo;
}

在页面上我们使用EL或AJax取出所需数据,我们再来简单看看这个PageInfo的内部属性

批处理

在多条插入语句或多条更新语句的时候,如果一条一条SQL去处理,数据库对于每一条SQL都要编译->设置参数->运行;使用批处理,数据库只把SQL编译一次(因为每个批处理的SQL结构都是一样的),只需要每次设置不同的参数。

所以当涉及到批量更新或批量插入的时候,使用批处理速度要快。在Mybatis中也是支持批处理的,之前使用动态SQL的时候我们用foreach元素拼过一条很长的SQL,但是因为数据库对SQL长度会有限制,所以太长的SQL语句是不能作为批处理的。Mybatis的配置文件settings中有一个defultExecutorType属性,它默认是SIMPLE(简单的执行器),我们可以改为BATCH(批处理执行器),但是这样的配置是全局配置,所有的执行器都会变为批处理执行器,所以不推荐使用。

当然了,我们可以在局部使用批处理,就是在openSession的时候给个参数

//可以执行批量操作的sqlSession
SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);

我们用这个SqlSession执行的SQL就是在批处理

    @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);
for (int i = 0; i < 10000; i++) {
mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0, 5), "b", "1"));
}
openSession.commit();
long end = System.currentTimeMillis();
//批量:(预编译sql一次==>设置参数===>10000次===>执行(1次))
//Parameters: 616c1(String), b(String), 1(String)==>4598
//非批量:(预编译sql=设置参数=执行)==》10000 10200
System.out.println("执行时长:"+(end-start));
}finally{
openSession.close();
} }

在我们Mybatis-Spring整合之后,我们就不直接使用SqlSession了,那么如何与Spring整合呢?

需要在Spring配置文件中配置SqlSessionTemplate 这个bean

<!--配置一个可以进行批量执行的sqlSession  -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
<constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>

然后在Service层配置并注入SqlSession,使用这个SqlSession来操作,就像直接操作原生Mybatis操作一样:

使用批处理需要注意的问题:采用批处理的执行器,默认情况下只有在操作到session.commit()才会被Mybatis发送SQL到数据库执行,所以如果在批处理未提交之前还存在其他操作(例如=再查询这条记录)就会发生空指针异常发生回滚。

SqlSession sqlSession=sqlSessionFactory.openSession(ExecutorType.BATCH);
try{
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
User user=new User("lz",21);
userMapper.insertUser(user);
User user2=userMapper.getUser(1);
System.err.println(user2.getUserName);
sqlSession.commit();
}catch(Exception e{
sqlSession.rollback();
e.printStackTrace();
})finally{
if(sqlSession!=null){
sqlSession.close();
}
}

System.err.println(user2.getUserName);  这里就会报空指向异常,因为我们的commit是在最后才提交的。而我们在getUser方法调用之前并不想提交事务,以为后面可能还会有其他的数据库操作在这个事务中,这个时候我们只需调用SqlSession的flushStatement方法便可以了,它的含义是将当前缓存的SQL发送给数据库执行。

userMapper.insertUser(user);
session.flushStatement();
User user2=userMapper.getUser(1);
System.err.println(user2.getUserName);
sqlSession.commit();

这样就避免了在批处理后面的操作错误,我们在使用批处理的时候要特别注意这一点。

调用存储过程

如果数据库中建有存储过程的话,我们Mybatis程序又该如何调用呢?

首先,我们默认使用的statementHandler是预编译的prepare,所以在处理存储过程的时候需要切换为 “CALLABLE” ,然后我们需要用select元素调用存储过程。调用存储过程的方式为 :

{call procedure_name(params)}
    <!-- public void getPageByProcedure();
1、使用select标签定义调用存储过程
2、statementType="CALLABLE":表示要调用存储过程
3、{call procedure_name(params)}
-->
<select id="getPageByProcedure" statementType="CALLABLE" databaseId="oracle">
{call hello_test(
#{start,mode=IN,jdbcType=INTEGER},
#{end,mode=IN,jdbcType=INTEGER},
#{count,mode=OUT,jdbcType=INTEGER},
#{emps,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=PageEmp}
)}
</select>

我们使用mode设置存储过程的入参(IN)和出参(OUT) ;最后一个emps是调用一个游标,游标的javaType对应的是ResultSet,使用自定义的结果集resultMap封装游标数据

MBG代码生成器

Mybatis官方提供了一个代码生成器用于自动生成JavaBean和Mapper, 地址在https://github.com/mybatis/generator 太详细的使用可以看这个地址的文档说明,这里记录一个简单的使用

首先在工程下导入所需jar包

然后我们在工程目录下建立一个MBG的配置文件,名为 generatorConfig.xml (这个文件存放的路径和名字不是固定的)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration> <context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- MySql数据库链接URL、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssm_crud?characterEncoding=utf8"
userId="root" password="root"> </jdbcConnection> <!-- 默认是false,把JDBC DECIMAL和NUMERIC 类型解析为 Integer 设置为true表示把JDBC DECIMAL和NUMERIC
类型解析为 java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 生成pojo模型的包名和位置 -->
<javaModelGenerator targetPackage="cn.lynu.model"
targetProject=".\src\main\java">
<!-- enableSubPackages是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!--trimStrings从数据库中返回的值将被清理前后空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- 生成的mapper映射文件包名和位置 -->
<sqlMapGenerator targetPackage="mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- 生成mapper接口的包名和位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="cn.lynu.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据库中的表 ,可以使用domainObjectName指定表生成的pojo类名-->
<!--<table tableName="tbl_emp" domainObjectName="Employee"></table>-->
<table tableName="tb_emp" domainObjectName="Employee"/>
<table tableName="tb_dept" domainObjectName="Department"/>
</context>
</generatorConfiguration>

这里使用了一个 targetRuntime 指定MBG的运行环境,这个值为 MyBatis3 表示会生成基本的CRUD和带查询条件的查询

最后我们使用一个java类来运行就会开始代码生成

    @Test
public void test() throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
// 指定generatorConfig.xml配置文件位置
File configFile = new File("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);
}

使用QBC多条件查询

    @Test
public void testMyBatis3() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//xxxExample就是封装查询条件的
//1、查询所有
//List<Employee> 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 = ? )
Criteria criteria = example.createCriteria();
//查询条件需要使用Criteria对象的and..()方法添加
criteria.andLastNameLike("%e%");
criteria.andGenderEqualTo("1"); //select id, last_name, email, gender, d_id from tbl_employee
//WHERE ( last_name like ? and gender = ? ) or email like "%e%"
//or条件需要新生成Criteria
Criteria criteria2 = example.createCriteria();
criteria2.andEmailLike("%e%");
example.or(criteria2); List<Employee> list = mapper.selectByExample(example);
for (Employee employee : list) {
System.out.println(employee.getId());
} }finally{
openSession.close();
}
}

在这里Mybatis使用类似于Hibernate的QBC的方式使用 Criteria 完成多条件的查询,每个and条件都可以用一个Criteria的and...()方法添加条件,但是如果是or的条件就必须新生成一个 Cirteria来设置条件,并将这个新的Criteria添加进 Example 对象

Mybatis扩展的更多相关文章

  1. Java EE开发平台随手记4——Mybatis扩展3

    接着昨天的Mybatis扩展——IDaoTemplate接口. 扩展9:批量执行 1.明确什么是批量执行 首先说明一下,这里的批量执行不是利用<foreach>标签生成一长串的sql字符串 ...

  2. Java EE开发平台随手记6——Mybatis扩展4

    这篇博客中来说一下对Mybatis动态代理接口方式的扩展,对于Mybatis动态代理接口不熟悉的朋友,可以参考前一篇博客,或者研读Mybatis源码. 扩展11:动态代理接口扩展 我们知道,真正在My ...

  3. Java EE开发平台随手记3——Mybatis扩展2

    忙里偷闲,继续上周的话题,记录Mybatis的扩展. 扩展5:设置默认的返回结果类型 大家知道,在Mybatis的sql-mapper配置文件中,我们需要给<select>元素添加resu ...

  4. Java EE开发平台随手记2——Mybatis扩展1

    今天来记录一下对Mybatis的扩展,版本是3.3.0,是和Spring集成使用,mybatis-spring集成包的版本是1.2.3,如果使用maven,如下配置: <properties&g ...

  5. tk.mybatis扩展通用接口

    一.tk.mybatis已经为我们封装好了许多拆箱即用的通用mapper,但在实际的项目开发中想必不少小伙伴在数据库设计中都会采用逻辑删除这种方案,再去使用通用的mapper接口就不行了.这时候就需要 ...

  6. MyBatis - 10.MyBatis扩展

    1.PageHelpler分页插件使用 官方文档:中文 1.1 引入插件 1.1.1 引入的jar pagehelper-5.1.6.jar jsqlparser-1.2.jar 1.1.2 mave ...

  7. Mybatis插件扩展以及与Spring整合原理

    @ 目录 前言 正文 插件扩展 1. Interceptor核心实现原理 2. Mybatis的拦截增强 Mybatis与Spring整合原理 1. SqlSessionFactory的创建 2. 扫 ...

  8. 重构Mybatis与Spring集成的SqlSessionFactoryBean(1)

    一般来说,修改框架的源代码是极其有风险的,除非万不得已,否则不要去修改.但是今天却小心翼翼的重构了Mybatis官方提供的与Spring集成的SqlSessionFactoryBean类,一来是抱着试 ...

  9. Java EE开发平台随手记5——Mybatis动态代理接口方式的原生用法

    为了说明后续的Mybatis扩展,插播一篇广告,先来简要说明一下Mybatis的一种原生用法,不过先声明:下面说的只是Mybatis的其中一种用法,如需要更深入了解Mybatis,请参考官方文档,或者 ...

随机推荐

  1. 各个数据库中top 的表示方法

    Select Top在不同数据库中的使用用法: 1. Oracle数据库 SELECT * FROM TABLE1 WHERE ROWNUM<=N 2. Infomix数据库 SELECT FI ...

  2. spring boot 教程(一) 构建我的第一个Spring boot

    Spring Boot特点 1. 创建独立的Spring应用程序 2. 嵌入的Tomcat,无需部署WAR文件 3. 简化Maven配置 4. 自动配置Spring 5. 提供生产就绪型功能,如指标, ...

  3. JSON Schema(模式)

    JSON Schema指的是数据交换中的一种虚拟的“合同”. JSON验证器负责验证语法错误,JSON Schema负责提供一致性检验. JSON Schema是数据接收方额第一道防线,也是数据发送方 ...

  4. Java之引用类型分析(SoftReference/WeakReference/PhantomReference)

    引言: 即使对于Java的很多老鸟来说,如果忽然问他引用的类型,大概率是一脸茫然,不知所措的-.Java中的引用还分类型,神马情况??? 本文将针对这些类型进行分析,帮助您一文知所有类型. Java的 ...

  5. EasyDSS RTMP流媒体服务器中调用videojs播放rtmp视频显示在左上角问题

    本文转自EasyDarwin团队成员Penggy的博客:http://www.jianshu.com/p/f63f5b7c691b 问题描述: 近期我开发了一款新一代的RTMP/HLS流媒体服务器软件 ...

  6. 使用TR1的智能指针

    作为C++程序员,在没有智能指针,手动管理内存的蛮荒岁月里,可以说是暗无天日,痛苦异常.直到上帝说,还是要有光,于是智能指针进了标准.C++码农的日子总算好起来了. 虽然一直鄙视着没有显式指针的语言, ...

  7. learn go passing variable-length arguments

    package main // 参考文档: // https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/06.3.md im ...

  8. Jmeter简单的操作数据库

    mysql驱动包下载地址: https://dev.mysql.com/downloads/connector/j/ 1.添加驱动配置,把下载下来的驱动配置上去 2.添加‘配置元件-用户定义的变量’, ...

  9. Python面试题(十一)

    1.Python中list.tuple.dict.set有什么区别,主要应用在什么样的场景?并用for语句分别进行遍历 定义: list:链表,有序的项目, 通过索引进行查找,使用方括号”[]”; t ...

  10. nodejs之assert

    assert断言在nodejs中的用法: 1.引入assert模块 2.语法 assert('条件', '条件不成立时显示信息'); 例如:assert.js文件 const assert = req ...