一、整合
Spring + SpringMVC 是已经配置好了。以下是整合 MyBatis 和 pagehelper分页插件 的部分。
1、导入依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.1</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>x.x.x</version>
</dependency>
2、进行配置
2.1、配置类配置
@Configuration
@MapperScan(basePackages = {"com.exmple.xxx.mapper"})
public class MyBatisConfig {
@Value("${xxx.datasource.url}")
private String url;
@Value("${xxx.datasource.username}")
private String username;
@Value("${xxx.datasource.password}")
private String password;
private static final String CONFIG_LOCATION = "config/xxx/mybatis-config.xml";
private static final String MAPPER_LOCATION = "classpath:mapper/xxx/*.xml";
@Bean(name = "mybatis_dataSource")
public DataSource dataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
@Bean(name = "mybatis_sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
factoryBean.setConfigLocation(new ClassPathResource(CONFIG_LOCATION));
factoryBean.setMapperLocations(resolver.getResources(MAPPER_LOCATION));
PageHelper pageHelper = new PageHelper();
Properties properties = new Properties();
properties.setProperty("dialect", "postgresql");
properties.setProperty("reasonable", "true");
pageHelper.setProperties(properties);
factoryBean.setPlugins(pageHelper);
return factoryBean.getObject();
}
@Bean(name = "mybatis_sqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
return new SqlSessionTemplate(sqlSessionFactory());
}
@Bean(name = "mybatis_transactionManager")
public DataSourceTransactionManager transactionManager(){
return new DataSourceTransactionManager(dataSource());
}
}
2.2、配置拦截器插件另外两种方法
1)pageHelper 分页插件可在 Mybatis 配置文件中配置
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
<property name="offsetAsPageNum" value="true"/>
<property name="rowBoundsWithCount" value="true"/>
<property name="reasonable" value="true"/>
</plugin>
</plugins>
</configuration>
2) 在 Spring 配置文件中配置拦截器插件
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<value>
dialect=postgresql
reasonable=true
</value>
</property>
</bean>
</array>
</property>
</bean>
3、使用测试
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.xxx.mapper.PersonInfoMapper">
<select id="findAllUser" resultType="com.example.xxx.entity.PersonInfoEntity">
select * from person_info_test
</select>
</mapper>
4、自动填充类
@Component
public class MyBatisPlusTimeMetaObjectHandler implements MetaObjectHandler {
private static final Logger LOG = LoggerFactory.getLogger(MyBatisPlusTimeMetaObjectHandler.class);
@Override
public void insertFill(MetaObject metaObject) {
LOG.info("start insert fill....");
Long cur = System.currentTimeMillis();
this.strictInsertFill(metaObject, "createTime", Long.class, cur);
this.strictInsertFill(metaObject, "updateTime", Long.class, cur);
}
@Override
public void updateFill(MetaObject metaObject) {
LOG.info("start update fill....");
Long cur = System.currentTimeMillis();
this.setFieldValByName("updateTime", cur, metaObject);
}
}
二、PageHelper 分页插件
推荐查看文档学习:Mybatis-PageHelper-HowToUse
1、注意事项
? 1)PageHelper.startPage 方法重要提示。只有紧跟在PageHelper.startPage 方法后的第一个Mybatis的 查询(Select) 方法会被分页。
? 2)请不要在系统中配置多个分页插件(使用Spring时,mybatis-config.xml 和Spring<bean> 配置方式,请选择其中一种,不要同时配置多个分页插件)!
? 3)分页插件不支持带有for update 语句的分页。 对于带有for update 的sql,会抛出运行时异常,对于这样的sql建议手动分页,毕竟这样的sql需要重视。
? 4)分页插件不支持嵌套结果映射。 由于嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确。
2、如何在代码中使用
2.1、RowBounds方式的调用
List<User> list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(0, 10));
使用这种调用方式时,你可以使用RowBounds参数进行分页,这种方式侵入性最小,我们可以看到,通过RowBounds方式调用只是使用了这个参数,并没有增加其他任何内容。
分页插件检测到使用了RowBounds参数时,就会对该查询进行物理分页。
注: 不只有命名空间方式可以用RowBounds,使用接口的时候也可以增加RowBounds参数,例如:
List<User> selectAll(RowBounds rowBounds);
注意: 由于默认情况下的 RowBounds 无法获取查询总数,分页插件提供了一个继承自 RowBounds 的 PageRowBounds ,这个对象中增加了 total 属性,执行分页查询后,可以从该属性得到查询总数。
2.2、Mapper接口方式的调用
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectIf(1);
PageHelper.offsetPage(1, 10);
List<User> list = userMapper.selectIf(1);
PageHelper.startPage 静态方法调用
除了 PageHelper.startPage 方法外,还提供了类似用法的 PageHelper.offsetPage 方法。
在你需要进行分页的 MyBatis 查询方法前调用 PageHelper.startPage 静态方法即可,紧跟在这个方法后的第一个MyBatis 查询方法会被进行分页。
例一:
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectIf(1);
assertEquals(2, list.get(0).getId());
assertEquals(10, list.size());
assertEquals(182, ((Page) list).getTotal());
例二:
PageHelper.startPage(request);
List<User> list = userMapper.selectIf(1);
List<User> list2 = userMapper.selectIf(null);
assertEquals(2, list.get(0).getId());
assertEquals(10, list.size());
assertEquals(182, ((Page) list).getTotal());
assertEquals(1, list2.get(0).getId());
assertEquals(182, list2.size());
例三,使用PageInfo 的用法:
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectAll();
PageInfo page = new PageInfo(list);
assertEquals(1, page.getPageNum());
assertEquals(10, page.getPageSize());
assertEquals(1, page.getStartRow());
assertEquals(10, page.getEndRow());
assertEquals(183, page.getTotal());
assertEquals(19, page.getPages());
assertEquals(1, page.getFirstPage());
assertEquals(8, page.getLastPage());
assertEquals(true, page.isFirstPage());
assertEquals(false, page.isLastPage());
assertEquals(false, page.isHasPreviousPage());
assertEquals(true, page.isHasNextPage());
2.3、参数方法调用
public interface CountryMapper {
List<User> selectByPageNumSize(
@Param("user") User user,
@Param("pageNum") int pageNum,
@Param("pageSize") int pageSize);
}
List<User> list = userMapper.selectByPageNumSize(user, 1, 10);
public class User {
private Integer pageNum;
private Integer pageSize;
}
public interface CountryMapper {
List<User> selectByPageNumSize(User user);
}
List<User> list = userMapper.selectByPageNumSize(user);
想要使用参数方式,需要配置 supportMethodsArguments 参数为 true ,同时要配置 params 参数。 例如下面的配置:
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="supportMethodsArguments" value="true"/>
<property name="params" value="pageNum=pageNumKey;pageSize=pageSizeKey;"/>
</plugin>
</plugins>
在 MyBatis 方法中:
List<User> selectByPageNumSize(
@Param("user") User user,
@Param("pageNumKey") int pageNum,
@Param("pageSizeKey") int pageSize);
当调用这个方法时,由于同时发现了 pageNumKey 和 pageSizeKey 参数,这个方法就会被分页。params 提供的几个参数都可以这样使用。
使用 POJO 对象时:
注意: pageNum 和 pageSize 两个属性同时存在才会触发分页操作,在这个前提下,其他的分页参数才会生效。
2.4、jdk8 lambda 用法
Page<User> page = PageHelper.startPage(1, 10).doSelectPage(() -> userMapper.selectGroupBy());
pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(() -> userMapper.selectGroupBy());
total = PageHelper.count(() -> userMapper.selectLike(user));
3、PageHelper 安全调用
1)使用 RowBounds 和 PageRowBounds 参数方式是极其安全的
2)使用参数方式是极其安全的
3)使用 ISelect 接口调用是极其安全的
? ISelect 接口方式除了可以保证安全外,还特别实现了将查询转换为单纯的 count 查询方式,这个方法可以将任意的查询方法,变成一个 select count(*) 的查询方法。
4)什么时候会导致不安全的分页?
PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。
只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal 存储的对象。
如果代码在进入 Executor 前发生异常,就会导致线程不可用,这属于人为的 Bug(例如接口方法和 XML 中的不匹配,导致找不到 MappedStatement 时), 这种情况由于线程不可用,也不会导致 ThreadLocal 参数被错误的使用。
但是如果你写出下面这样的代码,就是不安全的用法:
PageHelper.startPage(1, 10);
List<User> list;
if(param1 != null){
list = userMapper.selectIf(param1);
} else {
list = new ArrayList<User>();
}
这种情况下由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。
上面这个代码,应该写成下面这个样子:
List<User> list;
if(param1 != null){
PageHelper.startPage(1, 10);
list = userMapper.selectIf(param1);
} else {
list = new ArrayList<User>();
}
这种写法就能保证安全。
如果你对此不放心,你可以手动清理 ThreadLocal 存储的分页参数,可以像下面这样使用:
List<User> list;
if(param1 != null){
PageHelper.startPage(1, 10);
try{
list = userMapper.selectAll();
} finally {
PageHelper.clearPage();
}
} else {
list = new ArrayList<User>();
}
这么写很不好看,而且没有必要。
三、MyBatis Mapper
https://mapper.mybatis.io/
|