手把手教mybatis 第三节

手把手教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>当逻辑表达式成立时,标签内部的内容显示
逻辑运算符:
==
!=
> &gt; 转义支付
< &lt; 转义字符
>=
<=
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 &lt;#{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.PageInterceptorcom.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>

分页插件参数介绍

  1. helperDialect(分页的方言):分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect属性来指定分页插件使用哪种方言。配置时,可以使用下面的缩写值: oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derby (完整内容看 PageAutoDialect) **特别注意:**使用 SqlServer2012 数据库时,需要手动指定为 sqlserver2012,否则会使用 SqlServer2005 的方式进行分页,还可以设置 useSqlserver2012=true 将2012改为sqlserver的默认方式。 你也可以实现 AbstractHelperDialect,然后配置该属性为实现类的全限定名称即可使用自定义的实现方法。
  2. useSqlserver2012(sqlserver):使用 SqlServer2012 数据库时,需要手动指定为 sqlserver2012,否则会使用 SqlServer2005 的方式进行分页,还可以设置 useSqlserver2012=true将2012改为sqlserver的默认方式。
  3. 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();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_AndyLau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值