Java阶段四Day07

Java阶段四Day07

文章目录

  • Java阶段四Day07
    • 关于MyBatis分页
    • 链式写法
    • 关于RESTful
    • 关于事务
      • 添加事务
      • 处理事务时执行过程

关于MyBatis分页

对于分页采取简单原则,先分再说,将查询的所有字段都进行分页,对其中的不需分页操作进行单独设置,将其分页值调大,除非非常确定字段是否添加分页

引入依赖

<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

在设计开发中:能不暴露的就不暴露,能少一些参数就少一些参数,用户所选越多,后台压力越大

使用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 或其子孙类异常,触发回滚机制

你可能感兴趣的:(培训之旅,java,mybatis,开发语言)