手把手教mybatis 第三节
动态SQL
查询条件:客户姓名模糊查询 客户性别精确查询
String sql ="select * from tb_customer where cusName like '%"+cusName+"%' and sex="+sex;
String cusName;
Integer sex;
String sql="select * from tb_customer";
String sql ="select * from tb_customer where sex="+sex;
//12个查询条件
String sql="select * from tb_customer where 1=1";
if(cusName!=null){
sql+=" and cusName like"
}
if(sex!=null){
sql+=" and sex="
}
String sql="select * from tb_customer";
if(cusName!=null || sex!=null){
sql+=" where "
};
int num=0;
if(cusName!=null and num==0){
sql+=" cusName="
}
if(cusName!=null){
}
// 判断是否包含有查询条件
根据业务需求的变化,sql语句会发生变化,sql语句不是固定的sql语句
1.什么时候有where
2.第一个查询条件没有and,以后的查询条件都有and 或or
where
要结合if标签或choose标签使用。自动增加where关键字,并去除第一个and关键字
<where>
<if> and
<if> and
<if> and
</where>
<select id="selectUsers" resultType="user">
select id id2,name name2,sex sex2 from tb_user
<where>
<if test="name!=null and name!=''">
and name like #{name}
</if>
<if test="sex!=null">
and sex=#{sex}
</if>
<if test="age!=null">
and age=#{age}
</if>
</where>
</select>
set
修改用户信息,只修改不为空的属性
String sql="update tb_user set "; if(name!=null){ sql+="name="+name+","; } if(age!=null){ sql+=" age="+age+"," }
只能用于update语句,自动增加SET关键字,并去除最后一个,
<update id="updateById">
update tb_user
<set>
<if test="name!=null and name!=''">
name=#{name},
</if>
<if test="age!=null">
age=#{age},
</if>
<if test="sex!=null">
sex=#{sex},
</if>
</set>
where id=#{id}
</update>
if
<c:if test="逻辑表达式"></c:if> 当逻辑表达式成立时,标签内部的内容显示
<if test="逻辑表达式"></if>当逻辑表达式成立时,标签内部的内容显示
逻辑运算符:
==
!=
> > 转义支付
< < 转义字符
>=
<=
and
or
变量:如果输入参数是简单类型 变量_parameter 代表输入的参数的值
输入参数是自定义类型,属性名就代表的输入参数
输入参数是map,key代表的输入参数
id>10查询id>10的所有用户,如果id<=10,查询所有用户
注意
如果输入参数中有自定义类型的属性,在判断时必须先判断自定义类型是否为空,才能够调用自定义类型的属性中的属性名
<select id="selectUsers" resultType="user">
select id id2,name name2,sex sex2 from tb_user
<if test="map.id>10">
where id <#{id}
</if>
</select>
choose
<c:choose>
<c:when test="">
....
<c:otherwise>
<choose>
<when test="">
....
<otherwise>
<select id="selectUsers" resultType="user">
select id id2,name name2,sex sex2 from tb_user
<choose>
<when test="map!=null and map.id>10">
where id>#{id}
</when>
<otherwise>
order by id desc
</otherwise>
</choose>
</select>
foreach
<c:foreach items="需要遍历的集合" var="每次变量的数据" begin="" end="" step="" varstatus="">
<foreach collection="需要遍历的集合" item="var 每次遍历的数据" index="当前遍历的所有" open="循环开始的字符串" close="循环结束后的字符串" seprator="循环分割的字符串">
in子句
where id in(1,2,3,4,5)
数组
<delete id="deleteById" parameterType="int">
delete from tb_user
<foreach collection="array" item="id" open=" where id in(" close=")" separator=",">
#{id}
</foreach>
</delete>
List
<delete id="deleteById" parameterType="int">
delete from tb_user
<foreach collection="list" item="id" open=" where id in(" close=")" separator=",">
#{id}
</foreach>
</delete>
自定义类型
<delete id="deleteById" parameterType="user">
delete from tb_user
<foreach collection="ids" item="id" open=" where id in(" close=")" separator=",">
#{id}
</foreach>
</delete>
注意
foreach遍历时,如果输入参数是数组,collection=“array”
如果输入参数是集合 collection=“list”
如果输入参数是自定义类型 collection=“集合类型的属性的属性名”
trim
可以自定义实现where和set
<select id="selectUsers" resultType="user">
select id id2,name name2,sex sex2 from tb_user
<trim prefix=" Where" prefixOverrides="and">
<if test="name!=null and name!=''">
and name like #{name}
</if>
<if test="sex!=null">
and sex=#{sex}
</if>
<if test="age!=null">
and age=#{age}
</if>
</trim>
</select>
<update id="updateById">
update tb_user
<!--
prefix:前缀
suffix:后缀
prefixOverrides
suffixOverrides:覆盖最后指定的字符
-->
<trim prefix="SeT" suffixOverrides=",">
<if test="name!=null and name!=''">
name=#{name},
</if>
<if test="age!=null">
age=#{age},
</if>
<if test="sex!=null">
sex=#{sex},
</if>
</trim>
where id=#{id}
</update>
sql
定义sql片段,定义的sql片段可以重复使用的模板
select * from 表
// 表30个字段
select 30个字段 from
<!--
sql片段
-->
<sql id="fields">
id,name,sex,age,'hello'
</sql>
<sql id="descStr">
order by id desc,name asc
</sql>
include
<!--
refid:引用的sql片段id
-->
<include refid="fields"></include>
高级查询结果映射*
多表查询
订单主表:1
订单明细表:n
查询订单明细,并关联显示订单主表信息
订单明细id 商品id 商品个数 单价 小计 订单编号 订单状态
关联查询:
内连接(隐式内连接,显示内连接)
外连接(左,右)
全连接(full join)x
select a.*,b.order_no,b.flag from tb_order_detail a
left join tb_order_main b on b.id = a.main_id
注意
多表查询不能使用号,会出现重复的列。技巧:需要显示的字段多的表使用x.,另外的表只能使用字段
扩展类
Dto结尾的,对目标类型进行扩展,需要通过扩展类来描述查询结果表之间的关系
<resultMap type="OrderDetailDto" id="map22">
<id column="id" property="id"/>
<result column="main_id" property="mainId"/>
<result column="goods_id" property="goodsId"/>
<result column="num" property="num"/>
<result column="total" property="total"/>
<result column="order_no" property="orderNo"/>
<result column="flag" property="flag"/>
</resultMap>
<select id="selectDetailsByMain" resultMap="map22">
select a.*,b.order_no,b.flag from tb_order_detail a
left join tb_order_main b on b.id = a.main_id
</select>
一对一
扩展类
添加一个自定义类型的属性
package com.xxx.pojo;
/**
* 1个dto对象,这个对象时一个detail,同时对应一个main
* @author admin
*
*/
public class OrderDetailDto extends OrderDetail{
private OrderMain main;
public OrderMain getMain() {
return main;
}
public void setMain(OrderMain main) {
this.main = main;
}
}
<resultMap type="OrderDetailDto" id="map22">
<id column="id" property="id"/>
<result column="main_id" property="mainId"/>
<result column="goods_id" property="goodsId"/>
<result column="num" property="num"/>
<result column="total" property="total"/>
<!-- 一对一的结果映射使用association
property: 自定义类型的属性的属性名
javaType: 当前自定义类型的权限定类名或别名
-->
<association property="main" javaType="ordermain">
<!-- 定义main的映射规则 -->
<!-- main中主键列的映射规则
可以省略,不省略(能够提供封装的效率)
<id column="main_id" property="id"/>
-->
<result column="order_no" property="orderNo"/>
<result column="flag" property="flag"/>
</association>
</resultMap>
<select id="selectDetailsByMain" resultMap="map22">
select a.*,b.order_no,b.flag from tb_order_detail a
left join tb_order_main b on b.id = a.main_id
</select>
一对多
淘宝中:实现我的订单功能
1?request作用域中存放什么样的数据
List<?>
List
?
// 1.确定需要几张表
// 2.确定表间过膝
// 3.确定连接方式
order_main tb_order_detail
1vN
确定主从关系
SELECT a.*,b.id detal_id,b.goods_id ,b.num,b.total,b.price FROM tb_order_main a LEFT JOIN
tb_order_detail b ON a.id = b.main_id
扩展类
package com.xxx.pojo.dto;
import java.util.List;
import com.xxx.pojo.OrderDetail;
import com.xxx.pojo.OrderMain;
public class OrderMainDto extends OrderMain{
List<OrderDetail> details;
public List<OrderDetail> getDetails() {
return details;
}
public void setDetails(List<OrderDetail> details) {
this.details = details;
}
}
需要在扩展类在中增加集合类型的属性
List<OrderMainDto> mains;
request.setAttribute("mains",mains);
request.getDispatch('/order.jsp').forward(request,response);
<c:foreach items="${mains}" var="main">
<div>${main.orderNo},${main.total}</div>
<c:foreach items="${main.details}" var="details">
商品id:${details.goodsId}
<select id="selectMainsByDetails" resultMap="map23">
SELECT a.*,b.id detail_id,b.goods_id ,b.num,b.total,b.price FROM tb_order_main a LEFT JOIN
tb_order_detail b ON a.id = b.main_id
</select>
<!--
定义映射规则
id:唯一标识
type:需要映射的类型的全限定类名或别名
-->
<resultMap type="ordermain" id="map1">
<!--
定义主键列的映射规则
column:指定需要映射的列名
property:指定需要映射到type中的属性的属性名
jdbcType:列在数据库中的类型(省略)
javaType:属性的类型(省略)
-->
<id column="id" property="id"/>
<!--
普通列的映射规则
column:指定需要映射的列名
property:指定需要映射到type中的属性的属性名
jdbcType:列在数据库中的类型(省略)
javaType:属性的类型(省略)
-->
<result column="order_no" property="orderNo"/>
<result column="user_id" property="userId"/>
<result column="flag" property="flag"/>
<result column="total" property="total"/>
</resultMap>
<!--
extends:继承一个resultMap的映射规则
-->
<resultMap id="map23" type="ordermaindto" extends="map1">
<!-- collection集合类型的属性的映射方式
property:扩展类中集合类型的属性的属性名
ofType: 集合类型的泛型的全限定类名或别名
-->
<collection property="details" ofType="orderdetail">
<!-- 集合中每个实体类定义映射规则
推荐写id,为了提供封装效率
-->
<id column="detail_id" property="id"/>
<result column="goods_id" property="goodsId"/>
<result column="num" property="num"/>
<result column="total" property="total"/>
</collection>
</resultMap>
多对多
采用association和collection的嵌套使用
用户表:
create table tb_user( id int not null primary key auto_increment, name varchar(200) );
商品表
create table tb_goods( id int not null primary key auto_increment, name varchar(20), price int );
查询用户信息并显示用户购买的商品信息
用户1
订单主表n 1
订单明细 n 1
商品 1
- SQL语句
SELECT * FROM tb_user a
LEFT JOIN tb_order_main b on b.user_id = a.id
LEFT JOIN tb_order_detail c on c.main_id = b.id
LEFT JOIN tb_goods d on c.goods_id = d.id
- 确定下扩展类
- 将查询结果映射
<resultMap type="userdto" id="map24">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="mains" ofType="OrderMainDto">
<id column="main_id" property="id"/>
<result column="order_no" property="orderNo"/>
<result column="flag" property="flag"/>
<result column="total" property="total"/>
<result column="id" property="userId"/>
<collection property="details" ofType="OrderDetailDto">
<id column="detail_id" property="id"/>
<result column="num" property="num"/>
<result column="xj" property="total"/>
<association property="goods" javaType="goods">
<id column="goods_id" property="id"/>
<result column="goods_name" property="name"/>
</association>
</collection>
</collection>
</resultMap>
<select id="selectUserGoods" resultMap="map24">
SELECT a.*,b.id main_id,b.order_no,b.flag,b.total,
c.id detail_id,c.num,c.total xj,d.id goods_id,d.name goods_name
FROM tb_user a
LEFT JOIN tb_order_main b on b.user_id = a.id
LEFT JOIN tb_order_detail c on c.main_id = b.id
LEFT JOIN tb_goods d on c.goods_id = d.id
</select>
<resultMap type="userdto" id="map24">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="goods" ofType="goods">
<id column="goods_id" property="id"/>
<result column="goods_name" property="name"/>
</collection>
</resultMap>
<select id="selectUserGoods" resultMap="map24">
SELECT a.*,b.id main_id,b.order_no,b.flag,b.total,
c.id detail_id,c.num,c.total xj,d.id goods_id,d.name goods_name
FROM tb_user a
LEFT JOIN tb_order_main b on b.user_id = a.id
LEFT JOIN tb_order_detail c on c.main_id = b.id
LEFT JOIN tb_goods d on c.goods_id = d.id
</select>
总结
1.重点在于编写关联查询的sql语句
2.创建扩展类
3.在翻译映射规则(主键列用id,普通列用result,自定义类型的属性用association,集合类型的属性用collection)
select 查询字段列表,函数(sum(a)),常量('hello'),子查询(子查询结果只能为一条)
from 表名,查询结果(select * from ssdsd) b
where
group by
having
order by
limit
admin,(select sex from 表)
延迟加载
延迟加载必须使用resultMap,只有在高级查询结果映射时才会使用延迟加载。按需加载
select * from tb_user a left join tb_role b on a.role_id = b.id
电商项目中需要考虑性能
- 单表查询的性能>多表查询
- 查询结果既包含用户信息也包含角色信息(存在数据传输的问题)
100条数据 100条用户信息和100条关联查询结果
- 有些设计中,有的用户不一定需要查看角色信息
select * from s_user
mybatis默认关闭了延迟加载(需要手动开启延迟加载)
<setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/>
1对1延迟加载
将多表查询拆分成单表查询
1.查询所有的订单明细信息
2.根据订单主表id查询主表信息
<resultMap type="OrderDetailDto" id="map25">
<id column="id" property="id"/>
<result column="main_id" property="mainId"/>
<result column="goods_id" property="goodsId"/>
<result column="num" property="num"/>
<result column="total" property="total"/>
<!-- 一对一的结果映射使用association
property: 自定义类型的属性的属性名
javaType: 当前自定义类型的权限定类名或别名
select:
写mapperstatementId(namespace.sqlid)(同一个映射文件中可以省略namespace)
可以调用其他映射文件中的查询
当前属性的封装需要调用其他的查询结果(被调用的查询的返回结果类型必须和javaType一致)
column: 指定当前查询结果的列名,作为select调用的参数(列都是外键列)
如果是被select调用的标签,可以不 用声明在抽象方法中
如果是联合组件column="{key=column,key2=column2}"
主键列:联合主键
1 1 2
1 2 3
2 2 3
-->
<association property="main" javaType="ordermain" select="selectMainById" column="{id=main_id,id2=id}">
</association>
</resultMap>
<select id="selectDetails" resultMap="map25">
select * from tb_order_detail
</select>
<!-- 根据订单主表id查询订单信息 -->
<select id="selectMainById" parameterType="map" resultType="ordermain">
select id,order_no orderNo,user_id userId,flag,total
from tb_order_main where id=#{id} and id=#{id2}
</select>
1对多延迟加载
<!-- 先查询所有的订单主表 -->
<resultMap id="map26" type="ordermaindto">
<id column="id" property="id"/>
<result column="order_no" property="orderNo"/>
<result column="user_id" property="userId"/>
<result column="flag" property="flag"/>
<result column="total" property="total"/>
<!--
select:调用的查询结果必须是>=0条
调用的查询的sqlid,根据主表Id查询订单信息
column:指定当前查询结果的列名,主键列
如果是被select调用的标签,可以不 用声明在抽象方法中
如果是联合组件column="{key=column,key2=column2}"
主键列:联合主键
1 1 2
1 2 3
2 2 3
-->
<collection property="details" ofType="orderdetaildto" select="selectDetailsByMainId" column="id"></collection>
</resultMap>
<select id="selectMains1" resultMap="map26">
select * from tb_order_main
</select>
<!-- 根据订单主表id查询该主表对应的明细 -->
<select id="selectDetailsByMainId" parameterType="int" resultType="orderdetaildto">
select id,main_id mainId,goods_id goodsId,num,total from
tb_order_detail where main_id=#{mainId}
</select>
缓存
计算机的存储机制:内存储器、外存储器
外存储器:硬盘、U盘、软盘
优点:存储空间大
64核 20T
缺点:读写效率低
内存储器:内存
优点:读写效率高
缺点:空间小
如果将数据存储在内容中,操作快。考虑内存中数据的大小。内存管理的数据库
所有的非关系型数据库都是基于内存存储。
内存的设计方式:map
缓存机制
一级缓存
mybatis中以及缓存是sqlSession级别的缓存,同一个sqlSession多次调用相同的mapperstatement(sqlid相同,输入参数也相同)会触发一级缓存。默认开启了一级缓存。
package com.xxx.test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import com.xxx.mapper.UserMapper;
import com.xx.pojo.dto.OrderMainDto;
import com.xx.util.SqlSessionUtil;
public class Test2 {
private static Map cache = new HashMap();
public static void main(String[] args) {
SqlSession session = SqlSessionUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.selectMains1(); // 第一 次调用selectMains1 从数据库中查询
mapper.selectMains1();
mapper.selectMains1();
mapper.selectMains1();
session.close();
}
}
二级缓存
mybatis二级缓存是mapper级别的缓存,同一个mapper的同一一个方法(包含输入参数相同)被不同的sqlSession调用,多次调用会触发二级缓存。(默认未开启二级缓存,需要手动开启)
- 局部缓存开关
<!-- 开启了局部的二级缓存开关 -->
<cache></cache>
- 全局缓存开关
<setting name="cacheEnabled" value="true"/>
service
n:
SqlSession session = SqlSessionUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.selectMains1(); // 第一 次调用selectMains1 从数据库中查询
session.close();
二级缓存发生的几率很高,内存溢出。序列化的功能。使用频率较低的数据序列化到磁盘中,反序列化到内存中
注意
1.使用二级缓存,结果映射的类必须可序列化
2.不同的sqlSession必须来自于同一个sqlSessionFactory
package com.xxx.test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import com.xxx.mapper.UserMapper;
import com.xxx.pojo.dto.OrderMainDto;
import com.xxx.util.SqlSessionUtil;
public class Test2 {
private static Map cache = new HashMap();
public static void main(String[] args) {
SqlSession session = SqlSessionUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.selectMains1(); // 第一 次调用selectMains1 从数据库中查询
session.close();
SqlSession session2 = SqlSessionUtil.getSession();
UserMapper mapper2 = session2.getMapper(UserMapper.class);
mapper2.selectMains1(); // 第一 次调用selectMains1 从数据库中查询
session2.close();
// update
SqlSession session4 = SqlSessionUtil.getSession();
UserMapper mapper4 = session4.getMapper(UserMapper.class);
mapper4.deleteMainById(2);
session4.close();
SqlSession session3 = SqlSessionUtil.getSession();
UserMapper mapper3 = session3.getMapper(UserMapper.class);
mapper3.selectMains1(); // 第一 次调用selectMains1 从数据库中查询
session3.close();
}
}
分页
mybatis业务层分页
public class OrderMainServiceImpl{
public Page selectMainByPage(Integer pageNumber,Integer pageSize){
//调用Mapper
page.setTotal(mapper.selectMainCount())
page.setPages(page.getTotal()%pageSize==0?page.getTotal()/pageSize:page.getTotal()/pageSize+1);
mapper.selectBylimit()
}
}
1. 引入分页插件
需要下载依赖并添加到构建路径中
- pagehelper.jar
- jsqlParse.jar
2. 配置拦截器插件
特别注意,新版拦截器是 com.github.pagehelper.PageInterceptor
。 com.github.pagehelper.PageHelper
现在是一个特殊的 dialect
实现类,是分页插件的默认实现类,提供了和以前相同的用法。
1). 在 MyBatis 配置 xml 中配置拦截器插件
<!--
plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
properties?, settings?,
typeAliases?, typeHandlers?,
objectFactory?,objectWrapperFactory?,
plugins?,
environments?, databaseIdProvider?, mappers?
-->
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
<property name="param1" value="value1"/>
</plugin>
</plugins>
分页插件参数介绍
helperDialect
(分页的方言):分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect
属性来指定分页插件使用哪种方言。配置时,可以使用下面的缩写值:oracle
,mysql
,mariadb
,sqlite
,hsqldb
,postgresql
,db2
,sqlserver
,informix
,h2
,sqlserver2012
,derby
(完整内容看 PageAutoDialect) **特别注意:**使用 SqlServer2012 数据库时,需要手动指定为sqlserver2012
,否则会使用 SqlServer2005 的方式进行分页,还可以设置useSqlserver2012=true
将2012改为sqlserver的默认方式。 你也可以实现AbstractHelperDialect
,然后配置该属性为实现类的全限定名称即可使用自定义的实现方法。useSqlserver2012
(sqlserver):使用 SqlServer2012 数据库时,需要手动指定为sqlserver2012
,否则会使用 SqlServer2005 的方式进行分页,还可以设置useSqlserver2012=true
将2012改为sqlserver的默认方式。pageSizeZero
:默认值为false
,当该参数设置为true
时,如果pageSize=0
或者RowBounds.limit = 0
就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是Page
类型)。
如何在代码中使用
. PageHelper.startPage
静态方法调用
除了 PageHelper.startPage
方法外,还提供了类似用法的 PageHelper.offsetPage
方法。
在你需要进行分页的 MyBatis 查询方法前调用 PageHelper.startPage
静态方法即可,紧跟在这个方法后的第一个MyBatis 查询方法会被进行分页。
例一:
//获取第1页,10条内容,默认查询总数count
// 参数1 pageNumber
// 参数2 pageSize
PageHelper.startPage(1, 10);
//紧跟着的第一个select方法会被分页
int i=0;
int b=1;
int c=2;
List<User> list = userMapper.selectIf(1);
List<User> list1 = userMapper.selectIf(1);
List<User> list2 = userMapper.selectIf(1);
List<User> list3 = userMapper.selectIf(1);
//用PageInfo对结果进行包装==Page
PageInfo page = new PageInfo(list);
//测试PageInfo全部属性
//PageInfo包含了非常全面的分页属性
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());
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2022 abel533@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.github.pagehelper;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* 对Page<E>结果进行包装
* <p/>
* 新增分页的多项属性,主要参考:http://bbs.csdn.net/topics/360010907
*
* @author liuzh/abel533/isea533
* @version 3.3.0
* @since 3.2.2
* 项目地址 : http://git.oschina.net/free/Mybatis_PageHelper
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class PageInfo<T> extends PageSerializable<T> {
public static final int DEFAULT_NAVIGATE_PAGES = 8;
public static final PageInfo EMPTY = new PageInfo(Collections.emptyList(), 0);
/**
* 当前页
*/
private int pageNum;
/**
* 每页的数量
*/
private int pageSize;
/**
* 当前页的数量
*/
private int size;
/**
* 由于startRow和endRow不常用,这里说个具体的用法
* 可以在页面中"显示startRow到endRow 共size条数据"
* 当前页面第一个元素在数据库中的行号
*/
private long startRow;
/**
* 当前页面最后一个元素在数据库中的行号
*/
private long endRow;
/**
* 总页数
*/
private int pages;
/**
* 前一页
*/
private int prePage;
/**
* 下一页
*/
private int nextPage;
/**
* 是否为第一页
*/
private boolean isFirstPage = false;
/**
* 是否为最后一页
*/
private boolean isLastPage = false;
/**
* 是否有前一页
*/
private boolean hasPreviousPage = false;
/**
* 是否有下一页
*/
private boolean hasNextPage = false;
/**
* 导航页码数
*/
private int navigatePages;
/**
* 所有导航页号
*/
private int[] navigatepageNums;
/**
* 导航条上的第一页
*/
private int navigateFirstPage;
/**
* 导航条上的最后一页
*/
private int navigateLastPage;
public PageInfo() {
}
/**
* 包装Page对象
*
* @param list
*/
public PageInfo(List<? extends T> list) {
this(list, DEFAULT_NAVIGATE_PAGES);
}
/**
* 包装Page对象
*
* @param list page结果
* @param navigatePages 页码数量
*/
public PageInfo(List<? extends T> list, int navigatePages) {
super(list);
if (list instanceof Page) {
Page page = (Page) list;
this.pageNum = page.getPageNum();
this.pageSize = page.getPageSize();
this.pages = page.getPages();
this.size = page.size();
//由于结果是>startRow的,所以实际的需要+1
if (this.size == 0) {
this.startRow = 0;
this.endRow = 0;
} else {
this.startRow = page.getStartRow() + 1;
//计算实际的endRow(最后一页的时候特殊)
this.endRow = this.startRow - 1 + this.size;
}
} else if (list instanceof Collection) {
this.pageNum = 1;
this.pageSize = list.size();
this.pages = this.pageSize > 0 ? 1 : 0;
this.size = list.size();
this.startRow = 0;
this.endRow = list.size() > 0 ? list.size() - 1 : 0;
}
if (list instanceof Collection) {
calcByNavigatePages(navigatePages);
}
}
public static <T> PageInfo<T> of(List<? extends T> list) {
return new PageInfo<T>(list);
}
public static <T> PageInfo<T> of(List<? extends T> list, int navigatePages) {
return new PageInfo<T>(list, navigatePages);
}
/**
* 返回一个空的 Pageinfo 对象
*
* @return
*/
public static <T> PageInfo<T> emptyPageInfo() {
return EMPTY;
}
public void calcByNavigatePages(int navigatePages) {
setNavigatePages(navigatePages);
//计算导航页
calcNavigatepageNums();
//计算前后页,第一页,最后一页
calcPage();
//判断页面边界
judgePageBoudary();
}
/**
* 计算导航页
*/
private void calcNavigatepageNums() {
//当总页数小于或等于导航页码数时
if (pages <= navigatePages) {
navigatepageNums = new int[pages];
for (int i = 0; i < pages; i++) {
navigatepageNums[i] = i + 1;
}
} else { //当总页数大于导航页码数时
navigatepageNums = new int[navigatePages];
int startNum = pageNum - navigatePages / 2;
int endNum = pageNum + navigatePages / 2;
if (startNum < 1) {
startNum = 1;
//(最前navigatePages页
for (int i = 0; i < navigatePages; i++) {
navigatepageNums[i] = startNum++;
}
} else if (endNum > pages) {
endNum = pages;
//最后navigatePages页
for (int i = navigatePages - 1; i >= 0; i--) {
navigatepageNums[i] = endNum--;
}
} else {
//所有中间页
for (int i = 0; i < navigatePages; i++) {
navigatepageNums[i] = startNum++;
}
}
}
}
/**
* 计算前后页,第一页,最后一页
*/
private void calcPage() {
if (navigatepageNums != null && navigatepageNums.length > 0) {
navigateFirstPage = navigatepageNums[0];
navigateLastPage = navigatepageNums[navigatepageNums.length - 1];
if (pageNum > 1) {
prePage = pageNum - 1;
}
if (pageNum < pages) {
nextPage = pageNum + 1;
}
}
}
/**
* 判定页面边界
*/
private void judgePageBoudary() {
isFirstPage = pageNum == 1;
isLastPage = pageNum == pages || pages == 0;
hasPreviousPage = pageNum > 1;
hasNextPage = pageNum < pages;
}
/**
* 是否包含内容
*/
public boolean hasContent() {
return this.size > 0;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public long getStartRow() {
return startRow;
}
public void setStartRow(long startRow) {
this.startRow = startRow;
}
public long getEndRow() {
return endRow;
}
public void setEndRow(long endRow) {
this.endRow = endRow;
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
public int getPrePage() {
return prePage;
}
public void setPrePage(int prePage) {
this.prePage = prePage;
}
public int getNextPage() {
return nextPage;
}
public void setNextPage(int nextPage) {
this.nextPage = nextPage;
}
public boolean isIsFirstPage() {
return isFirstPage;
}
public void setIsFirstPage(boolean isFirstPage) {
this.isFirstPage = isFirstPage;
}
public boolean isIsLastPage() {
return isLastPage;
}
public void setIsLastPage(boolean isLastPage) {
this.isLastPage = isLastPage;
}
public boolean isHasPreviousPage() {
return hasPreviousPage;
}
public void setHasPreviousPage(boolean hasPreviousPage) {
this.hasPreviousPage = hasPreviousPage;
}
public boolean isHasNextPage() {
return hasNextPage;
}
public void setHasNextPage(boolean hasNextPage) {
this.hasNextPage = hasNextPage;
}
public int getNavigatePages() {
return navigatePages;
}
public void setNavigatePages(int navigatePages) {
this.navigatePages = navigatePages;
}
public int[] getNavigatepageNums() {
return navigatepageNums;
}
public void setNavigatepageNums(int[] navigatepageNums) {
this.navigatepageNums = navigatepageNums;
}
public int getNavigateFirstPage() {
return navigateFirstPage;
}
public int getNavigateLastPage() {
return navigateLastPage;
}
public void setNavigateFirstPage(int navigateFirstPage) {
this.navigateFirstPage = navigateFirstPage;
}
public void setNavigateLastPage(int navigateLastPage) {
this.navigateLastPage = navigateLastPage;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("PageInfo{");
sb.append("pageNum=").append(pageNum);
sb.append(", pageSize=").append(pageSize);
sb.append(", size=").append(size);
sb.append(", startRow=").append(startRow);
sb.append(", endRow=").append(endRow);
sb.append(", total=").append(total);
sb.append(", pages=").append(pages);
sb.append(", list=").append(list);
sb.append(", prePage=").append(prePage);
sb.append(", nextPage=").append(nextPage);
sb.append(", isFirstPage=").append(isFirstPage);
sb.append(", isLastPage=").append(isLastPage);
sb.append(", hasPreviousPage=").append(hasPreviousPage);
sb.append(", hasNextPage=").append(hasNextPage);
sb.append(", navigatePages=").append(navigatePages);
sb.append(", navigateFirstPage=").append(navigateFirstPage);
sb.append(", navigateLastPage=").append(navigateLastPage);
sb.append(", navigatepageNums=");
if (navigatepageNums == null) {
sb.append("null");
} else {
sb.append('[');
for (int i = 0; i < navigatepageNums.length; ++i) {
sb.append(i == 0 ? "" : ", ").append(navigatepageNums[i]);
}
sb.append(']');
}
sb.append('}');
return sb.toString();
}
}