分页插件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 对象

最新文章

  1. Azure PowerShell (6) 设置单个Virtual Machine Endpoint
  2. Thinkphp源码分析系列(九)–视图view类
  3. Fedora 24 install MySQL
  4. PHP中怎样创建一个空对象?
  5. 招聘:web前端开发(中级、高级均可)
  6. Hadoop入门进阶课程3--Hadoop2.X64位环境搭建
  7. webform 文件上传(头像上传) 隐藏FileUpload
  8. 给图像添加logo
  9. MATLAB做主成分分析(PCA)
  10. ZOJ3944People Counting&lt;暴力/枚举&gt;
  11. bzoj1150 [CTSC2007]数据备份Backup 双向链表+堆
  12. win10安装wmi报错问题
  13. 自动化测试基础篇--Selenium单选框(Radio)复选框(CheckBox)
  14. IAR8.11.1安装与破解教程
  15. hadoop yarn组件介绍
  16. centos7执行 wget命令: command not found的两种解决方法
  17. 华为交换机VRRP配置实例收集(转)
  18. MySQL基础练习(二)
  19. eclipse文本编码格式修改为UTF-8
  20. dom4j之selectSingleNode方法

热门文章

  1. mysql 数据传输报错 MySQL server has gone away With statement:
  2. Makefile.am文件的实例讲解
  3. H265 Profile &amp; Level &amp; Tier 介绍
  4. 看一遍就懂,详解java多线程——volatile
  5. BeautifulSoup的安装和使用
  6. 使用json path设置关联
  7. stack组件03
  8. MacDown语法
  9. tcp流式套接字和udp数据报套接字编程区别
  10. AngularX 路由总结