对于分页采取简单原则,先分再说,将查询的所有字段都进行分页,对其中的不需分页操作进行单独设置,将其分页值调大,除非非常确定字段是否添加分页
引入依赖
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
<version>1.4.1version>
dependency>
编写测试类
void listTagType(){
int pagNum = 2; //页码
int pagSize = 2; // 每页记录数
PageHelper.startPage(pagNum, pagSize); //设置分页参数
List<?> list = tagMapper.listTagType(); //【注意】必须紧随其后
System.out.println("查询完成,列表类型" + list.getClass().getName());
System.out.println("查询列表完成,列表项的数量:" + list.size());
System.out.println(list);
for (Object item : list) {
System.out.println("列表项:" + item);
}
System.out.println("______________");
PageInfo<?> pageInfo = new PageInfo<>(list);
System.out.println(pageInfo);
}
对于接口中返回值的设计
采用Page
Page
,没有使用PageInfo
更精彩,可能有些信息就没被显示采用PageInfo
Page
比,编码难度低,返回数据更多,但是存在依赖框架import com.github.pagehelper.PageInfo;
,当以后项目中换了其他的分页依赖,Service
也需要跟着修改采用自定义PageData
基于上面的问题,仿写一个自定义的PageInfo
,起名PageData
,摆脱依赖框架。
import io.swagger.models.auth.In;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
/**
* 分页数据类
*/
@Data
@Accessors(chain = true)
public class PageData<T> implements Serializable {
/**
* 每页记录数
*/
private Integer pageSize;
/**
* 当前页码
*/
private Integer currentPage;
/**
* 最大页码
*/
private Integer maxPage;
/**
* 数据列表
*/
private List<T> list;
/**
* 记录总数
*/
private Long total;
}
在书写查询标签类别列表时,有一方法如下:
@Override
public PageData<TagTypeListItemVO> listTagType(Integer pageNum,Integer pageSize) {
log.debug("开始执行【查询标签类别列表】,页码:1,每页记录数:{}",pageNum,pageSize);
PageHelper.startPage(pagNum, pagSize); //设置分页参数
List<TagTypeListItemVO> list = tagMapper.listTagType();
PageInfo<TagTypeListItemVO> pageInfo = new PageInfo<>(list);
PageDataTagTypeListItemVO> pageData = new PageData<>();
pageData.setTotal(pageInfo.getTotal());
pageData.setMaxPage(pageInfo.getPages());
pageData.setCurrentPage(pageInfo.getPageNum());
pageData.setPageSize(pageInfo.getPageSize());
pageData.setList(pageInfo.getList());
return pageData;
}
上述方法虽然没有问题,但是setter
方法过于多,导致不是很简洁,可以使用来链式写法,在PageData
的类上加@Accessors(chain = true)
,代码可变成在一个setter
方法后不用;
结束,而是可以继续.
下一个setter
方法
@Override
public PageData<TagTypeListItemVO> listTagType(Integer pageNum,Integer pageSize) {
log.debug("开始执行【查询标签类别列表】,页码:1,每页记录数:{}",pageNum,pageSize);
PageHelper.startPage(pagNum, pagSize); //设置分页参数
List<TagTypeListItemVO> list = tagMapper.listTagType();
PageInfo<TagTypeListItemVO> pageInfo = new PageInfo<>(list);
PageDataTagTypeListItemVO> pageData = new PageData<>();
pageData.setTotal(pageInfo.getTotal())
.setMaxPage(pageInfo.getPages())
.setCurrentPage(pageInfo.getPageNum())
.setPageSize(pageInfo.getPageSize())
.setList(pageInfo.getList());
return pageData;
}
其原理是未加链式注解时,setter
方法为
public PageData setTotal(Long total){
this.total = total;
}
加上@Accessors(chain = true)
后,setter
方法变为
public PageData setTotal(Long total){
this.total = total;
return this;
}
即每个方法返回自己的对象,就又可以继续调用方法。
最后将PageInfo
转换成PageData
的转换器写成工具类,以后可以多次复用
import com.github.pagehelper.PageInfo;
/**
* 将PageInfo转换成PageData的转换器工具类
*
* @author liner
* @version 1.0
*/
public class PageUtils {
/**
* 将PageHelper框架中的PageInfo类型对象转换成自定义的PageData类型对象
*
* @param pageInfo PageInfo对象
* @param PageInfo对象中的列表数据中的元素数据的类型
* @return 自定义的PageData类型的对象
*/
//static方法中 第一个泛型 指的是本方法中有个泛型 PageInfo 第二个泛型 指PageData
public synchronized static <T> PageData<T> convert(PageInfo pageInfo){
PageData<T> pageData = new PageData<>();
pageData.setTotal(pageInfo.getTotal())
.setMaxPage(pageInfo.getPages())
.setCurrentPage(pageInfo.getPageNum())
.setPageSize(pageInfo.getPageSize())
.setList(pageInfo.getList());
return pageData;
}
}
即最后成型的关于分页的方法写为
@Override
public PageData<TagTypeListItemVO> listTagType(Integer pageNum, Integer pageSize) {
log.debug("开始执行【查询标签类别列表】,页码:{},每页记录数:{}", pageNum, pageSize);
PageHelper.startPage(pageNum, pageSize);
List<TagTypeListItemVO> list = tagMapper.listTagType();
PageInfo<TagTypeListItemVO> pageInfo = new PageInfo<>(list);
return PageUtils.convert(pageInfo);
}
在设计开发中:能不暴露的就不暴露,能少一些参数就少一些参数,用户所选越多,后台压力越大
使用Restful,建议和提倡将id
作为url
的一部分,采用{}
包住id
,写在请求的Mapping参数中,易于理解当前请求在干什么,相比传统的将请求参数放到?
后面跟有优势。更加的简短,且这些放在url
中的变量值都具有唯一性,并在方法的传参中使用@PathVariable
通过在{id}
中加入正则表达式和使用 @Range
对参数值进行取值检查,且对于把注解直接加到参数上的检查,需要在当前类上加入@Validated
,从而使注解生效
@ApiOperation("删除标签")
@ApiOperationSupport(order = 200)
@PostMapping("/{id:[0-9]+}/delete") //404更早的发现错误,400在尝试请求之后发现错误
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "标签ID", required = true, dataType = "long"),
})
public JsonResult deleteNew(@PathVariable @Range(min = 1,message = "删除标签失败,请提交合法的ID值") Long id) {
log.debug("开始处理【删除标签】的请求,参数:{}", id);
tagService.deleteById(id);
return JsonResult.ok();
}
事务(Transaction):是一种数据库中能够保证一系列的写操作(增、删、改)要么全部成功,要么全部失败的机制
假设存在某个转账行为:
update 账户表 set 余额=余额-1000 where 账户名='张三';
update 账户表 set 余额=余额+1000 where 账户名='李四';
以上2个 UPDATE 操作,无论它们执行先后顺序是哪种,只要第1条执行成功,第2条没有执行成功,就是不可接受的结果
在基于Spring JDBC
的数据库编程中,如果需要某个方法是“事务性"的,在此方法上添加@Transactional
注解即可。关于@Transactional
注解,可以添加在:
提示: SpringJDBC框架在实现事务管理时,使用了基于接口的代理模式
使用@Transactional
注解时,应该在接口的抽象方法上使用此注解,但是,在学习过程中,建议在接口上添加此注解
try {
开启事务:BEGIN
执行业务
提交事务:COMMIT
}catch (RuntimeException e) {
回滚事务:ROLLBACK
}
默认情况下,Spring JDBC
会在执行业务的过程中出现RuntimeException
时执行回滚,如果需要根据其它异常执行回滚,可以配置@Transactional
注解的rollbackFor
属性
@Transactional (rollbackFor = {
ServiceException.class,NullPointerException.class
})
或者,配置rollbackForclassName
属性,例如:
@Transactional (rollbackForclassName =
"com.liner.demo","java.lang.NullPointerException"
})
还可以配置noRollbackFor
属性,指定哪些异常不会导致回滚
@Transactional (noRollbackFor = {
classcastException.c1ass,IndexoutofBoundsException.class
})
也有对应的noRollbackForClassName
属性
注意:无论如何配置,导致回滚的异常类型都必须是RuntimeException
的子孙类。
由于Spring JDBC
会根据 RuntimeException
及其子孙类异常执行回滚,所以,在业务方法中执行任何增、删、改操作时,都应该及时获取受影响的行数,并判断此值是否符合预期,如果不符合,必须抛出RuntimeException
或其子孙类异常,触发回滚机制