Mybatis扩展

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

原文地址:https://www.cnblogs.com/lz2017/p/8446342.html

时间: 2024-10-15 20:52:29

Mybatis扩展的相关文章

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

这篇博客中来说一下对Mybatis动态代理接口方式的扩展,对于Mybatis动态代理接口不熟悉的朋友,可以参考前一篇博客,或者研读Mybatis源码. 扩展11:动态代理接口扩展 我们知道,真正在Mybatis动态代理接口方式背后起作用的是SqlSession接口,类似地,我们的动态代理接口扩展则是基于IDaoTemplate接口,同样的,也需要解决相同的三个基本问题: 问题1:确定需要执行的sqlId 原生用法是根据包名.接口名.方法名去查找,但我们推荐添加一个sqlId的查找策略接口: pu

关于mybatis扩展的一点想法

本文主要目的是为mybatis的 include 标签添加扩展属性 当使用mybatis进行单表查询时 可以方便的使用 <include> 标签将定义好的<sql> 节点内容包含进来,具体示例如下 <sql id="t_agent_Base_Column_List"> ID, WORKER_ID, <span style="font-family: Arial, Helvetica, sans-serif;">WORK

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

今天来记录一下对Mybatis的扩展,版本是3.3.0,是和Spring集成使用,mybatis-spring集成包的版本是1.2.3,如果使用maven,如下配置: <properties>元素下添加 1 <mybatis.version>3.3.0</mybatis.version> 2 <mybatis.spring.version>1.2.3</mybatis.spring.version> <dependencies>元素下

mybatis扩展之使用PageHelper插件进行分页(是真好用)

首先引入相关的jar包: 然后是在Mybatis全局配置文件中配置: <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> </plugins> 就可以使用了. EmployeeMapper.java中 public List<Employee> getAllEmp(); EmployeeMapper.xml中 <s

mybatis扩展之类型处理器(枚举类型处理)

Employee.java(省略getter和setter方法) private Integer id; private String lastName; private String gender; private String email; private EmpStatus empStatus=EmpStatus.LOGOUT; private Department dept; EmpStatus.java package com.gong.mybatis.bean; public enu

mybatis扩展之自定义类型处理器处理枚举类型

全局配置: <typeHandlers> <typeHandler handler="com.gong.mybatis.typeHandler.MyEnumEmpStatusTypeHandler" javaType="com.gong.mybatis.bean.EmpStatus"/> <!-- <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHa

重构Mybatis与Spring集成的SqlSessionFactoryBean(上)

一般来说,修改框架的源代码是极其有风险的,除非万不得已,否则不要去修改.但是今天却小心翼翼的重构了Mybatis官方提供的与Spring集成的SqlSessionFactoryBean类,一来是抱着试错的心态,二来也的确是有现实需要. 先说明两点: 通常来讲,重构是指不改变功能的情况下优化代码,但本文所说的重构也包括了添加功能 本文使用的主要jar包(版本):spring-*-4.3.3.RELEASE.jar.mybatis-3.4.1.jar.mybatis-spring-1.3.0.jar

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

为了说明后续的Mybatis扩展,插播一篇广告,先来简要说明一下Mybatis的一种原生用法,不过先声明:下面说的只是Mybatis的其中一种用法,如需要更深入了解Mybatis,请参考官方文档,或者研读源码. 我们知道,使用Mybatis的方式有很多种,从是否集成上分,可以单独使用,也可以和Spring集成使用:从使用方式上分,可以编写静态工具类,在静态工具中调用SqlSession,也可以直接注入SqlSession/ SqlSessionTemplate,还可以编写Dao接口,让mybat

Mybatis3

1      Mybatis扩展 1.1     example查询 1.2     分页插件 1.3      注解 1.4     自定义类型转化器 2      Mapper接口 Example:where后的条件 方法    功能说明 int countByExample(XXXExample example) thorws SQLException 按条件计数 int deleteByPrimaryKey(Integer id) SQLException 按主键删除 int delet