转载

mybatis深入学习

  • 在使用mybatis标签的时候,需要指定类的全类名,比如 resultType=xx.xxx.xx ,但是我们可以为类指定别名,那么就可以直接使用别名,避免全类名冗余【不推荐使用】

  • 别名的配置有两种方式,这里我们讲解简单的配置方式,步骤如下:

    • 在mybatis的全局配置文件下指定别名的包扫描如下:
    <typeAliases>
           <packagename="cn.tedu.domain"/>
       </typeAliases>
    
    • 在domain这个包下的所有类默认的别名是类名首字母小写,但是我们也可以使用注解指定,如下:
    @Alias("author")
    public class Author{
        ...
    }
    

内建的别名【推荐使用】

  • mybatis对java中基本类型和基本的引用类型内嵌了别名,我们可以直接使用别名进行指定,这样利于开发,内建的别名如下:
别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

参数处理

  • mybatis内部会将我们传入的参数封装成一个Map,key就是以 param1、param2....

单个参数

  • 单个参数在sql语句中可以任意指定,比如#{a}…,或者可以使用 #{param1}

多个参数

  • 使用 @Param 指定key,那么就可以在sql语句中直接使用这个key即可,如下:
@Select("select * from patient_info where ipt_num=#{iptNum} and status=#{status}")
   PatientselectByIptNumAndInhos(@Param("iptNum")String iptNum, @Param("status")String status);
  • 也可以直接使用mybatis中默认的key,即是param1…..
@Select("select * from patient_info where ipt_num=#{param1} and status=#{param2}")
  PatientselectByIptNumAndInhos(String iptNum,String status);

参数是Map类型

  • mybatis默认的会将参数转换为map,那么我们直接传入一个map那是再好不过了,此时的key就可以直接使用,如下:
@Select("select * from patient_info where ipt_num=#{iptNum} and status=#{status}")
 PatientselectByIptAndInhosNumMap(Map ipts);

@Test
    public void test3()throws IOException {
        SqlSessionFactory sqlSessionFactory = XmlConfigInit.getSqlSessionFactory();
        PatientMapper mapper = sqlSessionFactory.openSession().getMapper(PatientMapper.class);
        Map<String,String> map=new HashMap<>();
        map.put("iptNum","15627656");
        map.put("status","1");
        Patient patient = mapper.selectByIptAndInhosNumMap(map);
        System.out.println(patient);
    }

POJO【推荐使用】

  • 对于POJO可以直接使用成员属性的名称就可以取值,这个经常使用,不再演示

返回结果封装

  • mybatis对于返回结果如何封装有多种实现方式,可以返回List,POJO,Map等类型的数据

返回POJO

  • 对于从数据库中查询单条数据库的时候,返回一个POJO只需要sql查询的字段和POJO类中的属性相同即可自动映射,当然我们也可以开启 驼峰配置
  • resultType 指定返回的POJO的全类名即可,或者指定别名
  • 此处不演示

返回List

  • 同POJO,此时的resultType指定的仍然是List泛型的全类名或者别名

返回Map

  • mybatis还可以返回Map类型的数据,比如我们查询患者的信息,使用Map接收数据,key是患者的id,value就是POJO,如下:
/**
    * 使用@MapKey注解指定返回Map中的key,这里设定的是Patient中的id属性作为key
    * @return
    */
   @MapKey("id")
   @Select("select * from patient_info")
   Map<Integer,Patient>selectAllReturnMap();
  • 在返回的Map的时候需要指定POJO类的哪个字段作为Map的key,使用 @MapKey 这个注解指定

ResultMap

  • mybatis还支持使用ResultMap自定义结果映射,此时的select语句中需要指定resultMap为当前的定义的id
  • 经常使用,不再演示

分步查询

  • 在mybatis中 collectionassociation 中都是可以使用分步查询

  • 我们需要查询一个科室下的所有患者,那么实体类定义如下:

@Data
@ToString
public class Dept{
    private Integer id;
    private String name;
    private List<Patient> patients;
}

@Data
@ToString
public class Patient{
    private String userId;
    private Integer id;
    private String status;
}
  • 不采用分布查询,此时我们的resultMap应该如下:
<resultMapid="baseResultMap"type="cn.tedu.domain.Dept">
        <idcolumn="id"property="id"/>
        <resultcolumn="name"property="name"/>
        <collectionproperty="patients"ofType="cn.tedu.domain.Patient">
            <idcolumn="pid"property="id"/>
            <resultcolumn="userId"property="userId"/>
            <resultcolumn="pstatus"property="status"/>
        </collection>
    </resultMap>

//sql

 SELECT
        d.id AS id,
        d.NAME AS NAME,
        p.id AS pid,
        p.user_id AS userId,
        p.STATUS AS pstatus
    FROM
        dept_info d
        LEFT JOIN patient_info p ON p.dept_id = d.id
    WHERE
        d.id =#{id,jdbcType=INTEGER}
  • 但是我们也可以采用分布查询的方式,先查询出科室的信息,再根据科室的id查询出对应患者的信息,实现步骤如下:

    • 定义一个方法查找科室,此时的resultMap指定的是分步查询的id
    <select id="selectById" resultMap="ByStepResultMap">
               SELECT
           *
       FROM
           dept_info
       WHERE
           id =#{id,jdbcType=INTEGER}
       </select>
    
  • 定义一个方法根据科室id查询患者信息

    @Select("SELECT * from patient_info where dept_id=#{deptId}")
        List<Patient>selectByDeptId(Integer deptId);
    
  • 在resultMap中指定分步查询

    <!--分步查询科室信息和患者信息-->
       <resultMapid="ByStepResultMap"type="cn.tedu.domain.Dept">
           <idcolumn="id"property="id"/>
           <resultcolumn="name"property="name"/>
      
           <!--ofType:指定级联属性的类型-->
           <!--select:指定分步查询患者信息的方法,全类名+方法名-->
           <!--column:指定查询科室获取的结果的哪一个字段作为查询患者方法的参数,可以指定多个
                       如果指定多个,那么需要将参数封装成map,比如column="{key1=column1,key2=column2}"
           -->
            <!--fetchType:在开启全局延迟加载的时候设置是否延迟加载,默认是延迟加载,可以设置为eager表示不延迟加载-->
           <collectionproperty="patients"ofType="cn.tedu.domain.Patient"
                       select="cn.tedu.mapper.PatientMapper.selectByDeptId"
                       column="id">
           </collection>
       </resultMap>
    

延迟加载

  • mybatis默认是不使用延迟加载的,因此当使用分步查询的时候即使没有用到分步查询的结果仍然会发出sql语句
  • 我们可以在全局配置文件中设置开启延迟加载,如下:
<settings>
        <!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。-->
        <settingname="lazyLoadingEnabled"value="true"/>
        <!--当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。-->
        <settingname="aggressiveLazyLoading"value="false"/>
    </settings>

内置参数

  • mybatis中内置了两个参数,在写sql的时候可以直接拿来使用,如下:
    _parameter
    _databaseId
    
  • 实例:
@Select("SELECT * from patient_info where dept_id=#{_parameter}")
   List<Patient>selectByDeptId(Integer deptId);
  • 如果是一个参数,并且是POJO对象,我们还可以使用 _parameter 判断是否为空,如下:
<iftest="_parameter!=null">
	.....
</if>
  • 如果是多个参数,那么就表示一个Map,此时可以直接使用 _parameter.key1.... 直接获取值即可,当然如果没有指定@Param注解,此时还可以使用 _parameter.param1,_parameter.param2... 直接获取对应的值

批量处理

  • Mybatis针对批量操作有两种常用的方法,第一种就死通过动态sql在sql语句中使用for-each拼写,第二种就是使用Mybaits自带的批量执行器(BatchExecutor),这里主要介绍第二种的方式
  • 如何配置?有如下两种方式
    • 在全局配置文件的settings中配置一个属性 defaultExecutorType =BATCH 即可,不过这种方式将会导致所有的sql操作都会使用批量操作。
    • 我们可以在获取SqlSession的时候指定执行类型,如下:
      • SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); ,此时当前的SqlSession执行的就是批量操作。

Mybaits-Spring执行批量处理

  • Spring执行批量处理很简单,只需要在ioc容器中注入一个 SqlSessionTemplate ,并且设置批量处理的属性即可,如下:
    • 这种改变是全局的,慎用
/**
     * 注入SqlSessionTemplate,替代SqlSessionFactory直接创建SqlSession,并且能够使用Spring的事务管理
     * 如果需要使用批量处理,在构造方法中指定ExecutorType.BATCH即可,那么全部的操作的都会使用
     * 【可以不配置,需要的时候使用】
     */
    @Bean
    public SqlSessionTemplate sqlSessionTemplate()throws Exception {
        SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory().getObject(),ExecutorType.BATCH);
        return sqlSessionTemplate;
    }

类型处理器(TypeHandler)

  • 用于处理Java类型和JDBC类型之前的映射关系,mybaits中内置了许多的类型处理器,一般我们在使用 #{} 中的jdbcType属性的时候,mybaits框架会为我们设置相应的处理器,比如jdbcType=DATE,那么mybatis默认对应的处理器就是 DateOnlyTypeHandler (只有年月日),如果我们设置了jdbcType=TIMESTAMP,那么mybatis对Date类型的类型处理器就是 DateTypeHandler ,关于类型处理器和jdbcType的对应关系,看 官方文档中typeHandler这一节
  • 内置处理器执行的时间:
    getResult
    
  • TypeHandler中的方法如下:
public interface TypeHandler<T>{

  /**
   * 为预编译语句设置参数的时候执行,实际就是调用setxxx方法设置参数
   * @param ps PreparedStatement 对象
   * @param i
   * @param parameter 参数
   * @param jdbcType #{}中自定义的jdbcType
   * @throws SQLException
   */
  void setParameter(PreparedStatement ps,int i, T parameter, JdbcType jdbcType)throws SQLException;

  /**
   * 根据列名获取指定的值
   * @param rs ResultSet结果集
   * @param columnName 列名
   * @return
   * @throws SQLException
   */
  TgetResult(ResultSet rs, String columnName)throws SQLException;

  /**
   * 根据下标获取指定列的值
   * @param rs ResultSet结果集
   * @param columnIndex  下标
   * @return
   * @throws SQLException
   */
  TgetResult(ResultSet rs,int columnIndex)throws SQLException;

  /**
   * 调用存储过程的获取返回值的时候
   * @param cs
   * @param columnIndex
   * @return
   * @throws SQLException
   */
  TgetResult(CallableStatement cs,int columnIndex)throws SQLException;

}
  • 虽然mybatis中内置了许多的类型处理器,但是我们也可以自定义类型处理器,并且作用到指定需要处理的类型中,自定义的方式有两种,如下:
    TypeHandler
    BaseTypeHandler
    

实例

  • 实例:我们需要将一个 List<Auth> 对象存入数据库的时候是以json字符串的形式,获取的是以List集合的形式,此时我们可以自定义一个TypeHandler,如下:
/**
 * 自定义类型转换器,将List<Auth>数据存入数据库的时候是以json字符串存入的,获取返回的结果的时候是List集合
 * @MappedJdbcTypes(value = {JdbcType.VARCHAR}):指定了映射的jdbcType的类型是VARCHAR
 * @MappedTypes(value = {Auth.class}):指定了映射的java类型是Auth
 */
@MappedJdbcTypes(value = {JdbcType.VARCHAR})
@MappedTypes(value = {Auth.class})
public class AuthTypeHandlerextends BaseTypeHandler{
    /**
     * 将参数转换为json数据存入数据库
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps,int i, Object parameter, JdbcType jdbcType)throws SQLException {
        String json = new Gson().toJson(parameter);
        ps.setString(i,json);
    }

    @Override
    public Object getNullableResult(ResultSet rs, String columnName)throws SQLException {
        String string = rs.getString(columnName);
        return new Gson().fromJson(string,new TypeToken<List<Auth>>(){}.getType());
    }

    @Override
    public Object getNullableResult(ResultSet rs,int columnIndex)throws SQLException {
        String string = rs.getString(columnIndex);
        return new Gson().fromJson(string,new TypeToken<List<Auth>>(){}.getType());
    }

    @Override
    public Object getNullableResult(CallableStatement cs,int columnIndex)throws SQLException {
        return null;
    }
}
  • 需要在全局配置文件中设置TypeHandler的包扫描,如下:
<!--设置自动扫描包下的typeHandler-->
    <typeHandlers>
        <packagename="cn.tedu.typehandler"/>
    </typeHandlers>
  • 此时的类型处理器还是不起作用的,需要在insert、update的#{}中设置一下typehandler属性,如下:
    • #{}中有一个typeHandler属性,指定自定义的TypeHandler即可
<insertid="insertAdmin"parameterType="cn.tedu.domain.Admin">
        insert into t_admin(name,birthday,account,password,point,status,auths)
         values (#{name,jdbcType=VARCHAR},#{birthday,jdbcType=DATE},
         #{account,jdbcType=VARCHAR},#{password,jdbcType=VARCHAR},
         #{point,jdbcType=DOUBLE},#{status,jdbcType=VARCHAR},#{auths,jdbcType=VARCHAR,typeHandler=cn.tedu.typehandler.AuthTypeHandler})
    </insert>
  • 对select语句设置类型处理器,只能在resultMap中设置,如下:
    • 在result中需要设置javaType,jdbcType,typeHandler三个属性,这里的auths就是需要映射的 List<AUth>
<resultMapid="BaseResultMap"type="cn.tedu.domain.Admin">
       <idcolumn="id"property="id"/>
       <resultcolumn="name"property="name"/>
       <resultcolumn="auths"property="auths"javaType="cn.tedu.domain.Auth"jdbcType="VARCHAR"typeHandler="cn.tedu.typehandler.AuthTypeHandler"></result>

   </resultMap>

枚举类型处理器

  • 枚举的类型处理器默认是 EnumTypeHandler ,存入和取出都是存储的枚举的名称,也有一个 EnumOrdinalTypeHandler 是按照枚举的索引存储和查询的。
  • 我们也可以自定义类型处理器来处理枚举类型。

插件

  • 插件的设计其实就是一个拦截器,在目标方法执行之前进行拦截,当然这里只是针对Mybatis中的四大对象进行拦截,四大对象如下:
    • Executor
    • StatementHandler
    • ParameterHandler
    • ResultSetHandler
  • Interceptor能够做到对四大对象中的每一个方法进行拦截
  • 实现一个插件很简单,只需要实现 org.apache.ibatis.plugin.Interceptor 接口即可
  • 对应Interceptor这个接口的方法解释如下:
public interface Interceptor{

  /**
   * 拦截器真正执行的方法,其中的invocation.proceed()是用来执行目标方法的,只有执行了这个proceed方法,目标方法才会执行,否则不执行
   * @param invocation
   * @return 返回目标方法执行的结果,return invocation.proceed();
   * @throws Throwable
   */
  Objectintercept(Invocation invocation)throws Throwable;

  /**
   * 四大对象生成代理对象的方法,在四大对象创建的时候,都会调用一个pluginAll方法返回一个代理对象
   * 这个方法不需要做修改,默认就行了
   * @param target 目标对象
   * @return 返回的代理对象(层层包装,表示只有一层)
   */
  default Object plugin(Object target){
    return Plugin.wrap(target, this);
  }

  /**
   * 在全局配置文件中<plugin>标签中设置properties属性,会封装在此方法的properties中
   * @param properties
   */
  default void setProperties(Properties properties){
    // NOP
  }

}

实现一个简单的插件

  • 自定义一个插件,修改指定查询语句的入参。如下:
    • 实现原理其实很简单,因为mybaits的增删改查标签所有的信息都封装在MappedStatement中,我们只需要获取这个对象,然后通过属性判断即可。
/**
 * @Intercepts注解标记这是一个拦截器,其中可以指定多个@Signature
 * @Signature:指定该拦截器拦截的是四大对象中的哪个方法
 *      type:拦截器的四大对象的类型
 *      method:拦截器的方法,方法名
 *      args:入参的类型
 */
@Intercepts(
        {
                @Signature(type = ParameterHandler.class,method ="setParameters",args = {PreparedStatement.class})
        }
)
public class FirstPluginimplements Interceptor{

    @Override
    public Object intercept(Invocation invocation)throws Throwable {
        System.out.println("拦截器执行:"+invocation.getTarget());
        //目标对象
        Object target = invocation.getTarget();
        //获取目标对象中所有属性的值,因为ParameterHandler使用的是DefaultParameterHandler,因此里面的所有的属性都封装在其中
        MetaObject metaObject = SystemMetaObject.forObject(target);
        //使用xxx.xxx.xx的方式可以层层获取属性值,这里获取的是mappedStatement中的id值
        String value = (String) metaObject.getValue("mappedStatement.id");
        //如果是指定的查询方法
        if ("cn.tedu.mapper.AdminMapper.selectById".equals(value)){
            //设置参数的值是2,即是设置id=2,因为这里只有一个参数,可以这么设置,如果有多个需要需要循环
            metaObject.setValue("parameterObject", 2);
        }
        //执行目标方法
        return invocation.proceed();
    }

    //可以省略
    @Override
    public Object plugin(Object target){
        return Plugin.wrap(target, this);
    }
    
    //可以省略
    @Override
    public void setProperties(Properties properties){
        System.out.println(properties);
    }
}
  • 全局文件中配置:
<!--配置插件,其中的property可以设置自己的属性,可以封装到setProperties中的properties中-->
   <plugins>
       <plugininterceptor="cn.tedu.plugin.FirstPlugin">
           <propertyname="id"value="11"></property>
       </plugin>
   </plugins>

重要的方法

SystemMetaObject.forObject(target)
metaObject.getValue("mappedStatement.id")
metaObject.setValue(name, value)

多个插件的执行顺序

  • 全局配置文件中配置插件的顺序,决定插件的执行顺序,是相反的顺序。

  • 如果有多个插件作用在同一个对象的同一个方法上,那么插件的执行顺序是怎样的?我们知道四大对象在创建的时候会调用拦截器中的plugin方法创建代理对象,这种代理实层层包装的,那么在后面的插件创建的代理是包裹在最外层的,因此肯定是先执行最外层的拦截器方法。

Spring整合Mybatis

  • 官方文档
  • 添加依赖:
<dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.2</version>
 </dependency>
  • 配置数据源、事务管理器、SqlSessionFactoryBean
    @MapperScan
    SqlSessionFactoryBean
    SqlSessionTemplate
    
/**
 * 配置类
 * @MapperScan:扫描所有的Mapper接口
 */
@Configuration
@ComponentScan(basePackages = {"cn.tedu.ssm"})
@MapperScan(basePackages = {"cn.tedu.ssm.mapper"})
@EnableAspectJAutoProxy
@EnableAsync
@EnableTransactionManagement
public class MainConfig{
    /**
     * 注册数据源
     */
    @Bean
    public DruidDataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(DataConfig.URL);
        dataSource.setUsername(DataConfig.USER);
        dataSource.setPassword(DataConfig.PWD);
        dataSource.setDriverClassName(DataConfig.DRIVER_NAME);
        return dataSource;
    }


    /**
     * 配置SqlSessionFactoryBean,实际就是SqlSessionFactory
     */
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory()throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        //设置数据源
        sqlSessionFactoryBean.setDataSource(dataSource());
        //配置扫描mapepr.xml文件
        PathMatchingResourcePatternResolver classPathResource = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setMapperLocations(classPathResource.getResources("classpath:mappers/*.xml"));
        //设置全局配置文件的位置
        sqlSessionFactoryBean.setConfigLocation(classPathResource.getResource("classpath:mybatis-config.xml"));
        return sqlSessionFactoryBean;
    }


    /**
     * 注入SqlSessionTemplate,替代SqlSessionFactory直接创建SqlSession,并且能够使用Spring的事务管理
     * 如果需要使用批量处理,在构造方法中指定ExecutorType.BATCH即可,那么全部的操作的都会使用
     * 【可以不配置,需要的时候使用】
     */
    @Bean
    public SqlSessionTemplate sqlSessionTemplate()throws Exception {
        SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory().getObject());
        return sqlSessionTemplate;
    }

    /**
     * 创建事务管理器
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager(){
        return new DataSourceTransactionManager(dataSource());
    }
}

注意

  • 在和Spring整合的时候,原先全局配置中配置的数据源,事务管理器等都会被忽略,默认会加载Spring配置的事务管理器和数据源。
  • 如果想要配置TypeHandler、Plugin、TypeAlias等设置,在SqlSessionFactoryBean中都是可以直接配置的,因此在和Spring整合之后,Mybaits的全局配置文件中需要配置的东西很少,几乎可以不用。

源码

  • 全注解版的SSM整合,包括下面分页插件的整合: https://github.com/chenjiabing666/ssm-demo

分页插件

  • 官方文档https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
  • 使用环境:ssm环境下配置
  • 添加依赖:
<dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.1.6</version>
 </dependency>
  • 配置拦截器,在和Spring整合下就只需要在SqlSessionFactoryBean中配置拦截器即可,如下:
/**
     * 配置SqlSessionFactoryBean,实际就是SqlSessionFactory
     */
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory()throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        //设置数据源
        sqlSessionFactoryBean.setDataSource(dataSource());
        //配置扫描mapepr.xml文件
        PathMatchingResourcePatternResolver classPathResource = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setMapperLocations(classPathResource.getResources("classpath:mappers/*.xml"));
        //设置全局配置文件的位置
        sqlSessionFactoryBean.setConfigLocation(classPathResource.getResource("classpath:mybatis-config.xml"));
        //配置插件
        PageInterceptor pageInterceptor = new PageInterceptor();
        //可以配置PageHelper中的参数映射关系,这里使用默认的,不需配置
// pageInterceptor.setProperties();
        sqlSessionFactoryBean.setPlugins(pageInterceptor);
        return sqlSessionFactoryBean;
    }
  • 官方文档中有多种使用方式,我们使用面向接口的方式,如下:
    • 只需要在doSelect中调用查询全部的sql即可
@Test
   public void test3(){
       SqlSessionTemplate sqlSessionTemplate = applicationContext.getBean(SqlSessionTemplate.class);
       final PatientMapper mapper = sqlSessionTemplate.getMapper(PatientMapper.class);
       PageInfo<Object> pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(new ISelect() {
           @Override
           public void doSelect(){
               mapper.selectAllPatient();
           }
       });
   }

自己整合工具类

  • 在实际使用过程中上述的方式有点繁琐,本人自己整合一个工具类,能够很轻松的完成分页。
  • 定义一个ParamReq类,如果有需要的分页的请求都继承这个类,如下:
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;

/**
 * 所有需要分页的请求要继承的类,其中提供了分页需要的参数
 * 默认的映射关系是:pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
 * 也可在设置拦截器的时候指定映射关系,具体看官方文档
 * https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
 */
@Data
public class PageParam{
    /**
     * 当前第几页
     */
    private Integer pageNum=1;

    /**
     * 每页查询的数量
     */
    private Integer pageSize=10;

    /**
     * 是否进行count查询,默认是true,查询
     * 如果设置为false,那么总数total将会为-1,不进行count查询
     */
    @JsonIgnore
    private Boolean countSql=true;

    /**
     * 分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
     */
    @JsonIgnore
    private Boolean reasonable;

    /**
     * 默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
     */
    @JsonIgnore
    private Boolean pageSizeZero=true;
}
  • 自定义执行接口,其实就是覆盖上面的doSelect方法,实现自己的执行查询的方法,如下:
/**
 * 分页需要实现的接口,在doExecute中只需调用查询全部数据的mapper即可
 */
@FunctionalInterface
public interface ExecutePageHelperextends ISelect{

    /**
     * 实现类应该覆盖的方法
     */
    void doExecute();

    @Override
    default void doSelect(){
        doExecute();
    }
}
  • 分页工具类,如下:
/**
 * 执行分页插件的工具类
 */
public class PageHelperUtils{

    /**
     * 执行PageHelper分页的方法
     * @param req 请求对象,继承PageParam类
     * @param executePageHelper ExecutePageHelper的接口实现类
     * @param <T> 泛型,需要返回结果的类型
     * @return
     */
    public static <T>  PageInfo<T>execute(PageParam req,ExecutePageHelper executePageHelper){
        //这里直接传入req,其实其中的值是有映射关系的,在PageParam中有讲到
        return PageHelper.startPage(req).doSelectPageInfo(executePageHelper);
    }
}
  • 测试:
    • 使用lambda表示可以方便的执行分页查询
@Test
   public void test4(){
       SqlSessionTemplate sqlSessionTemplate = applicationContext.getBean(SqlSessionTemplate.class);
       PatientMapper mapper = sqlSessionTemplate.getMapper(PatientMapper.class);
       //分页的请求类,继承ParamReq
       UserReq req=new UserReq();
       PageInfo<Patient> pageInfo = PageHelperUtils.execute(req, mapper::selectAllPatient);
       System.out.println(pageInfo.getList());
   }
原文  https://chenjiabing666.github.io/2019/08/05/mybatis深入学习/
正文到此结束
Loading...