• 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=".srcmainjava">
                <!-- enableSubPackages是否让schema作为包的后缀 -->
                <property name="enableSubPackages" value="false" />
                <!--trimStrings从数据库中返回的值将被清理前后空格 -->
                <property name="trimStrings" value="true" />
            </javaModelGenerator>
            <!-- 生成的mapper映射文件包名和位置 -->
            <sqlMapGenerator targetPackage="mapper"
                targetProject=".srcmain
    esources">
                <property name="enableSubPackages" value="false" />
            </sqlMapGenerator>
            <!-- 生成mapper接口的包名和位置 -->
            <javaClientGenerator type="XMLMAPPER"
                targetPackage="cn.lynu.mapper" targetProject=".srcmainjava">
                <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 对象

  • 相关阅读:
    ASP.NET HTTP模块和处理程序(5)(转载)
    充分利用ASP.NET的三种缓存提高站点性能(转载)
    LINQ体验(4)——LINQ简介和LINQ to SQL语句之Where
    理解DataSet的数据缓存机制
    在ASP.NET中实现AJAX(一)
    ASP.NET HTTP模块和处理程序(2)(转载)
    ASP.NET HTTP模块和处理程序(1)(转载)
    aspx页面中文汉字显示为乱码
    贫血还是充血?(转载)
    ASP.NET HTTP模块和处理程序(3)(转载)
  • 原文地址:https://www.cnblogs.com/lz2017/p/8446342.html
Copyright © 2020-2023  润新知