Skip to content

Spring Data JPA(一)

sqmax edited this page Jun 10, 2018 · 3 revisions

在Spring Boot中使用JPA,需要引入如下依赖:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

现在对本项目中使用到的JPA相关的知识点做一个梳理。

本项目中的DAO层放在com.imooc.repository包下。以我们的ProductCategoryRepository这个DAO为例,先看它的接口定义:

public interface ProductCategoryRepository extends JpaRepository<ProductCategory,Integer> {

    List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList);
}

可以看到它继承自JpaRepository,其实我们这里所有的DAO都继承之JpaRepository接口。现在我们来研究一下JpaRepository接口。下面是一个继承关系图。

可以看到JpaRepository接口间接继承自CrudRepository,而CrudRepository大致方法如下:

@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable>
  extends Repository<T, ID> {

  <S extends T> S save(S entity);      

  Optional<T> findById(ID primaryKey); 

  Iterable<T> findAll();               

  long count();                        

  void delete(T entity);               

  boolean existsById(ID primaryKey);   

  // … more functionality omitted.
}

它定义了一些CRUD方法,所以继承自的类可以直接获得这些对数据库操作的CRUD方法。

从图中还可以看到JpaRepository继承自PagingAndSortingRepository,它是一个Spring Data JPA中实现分页的接口,它的定义如下:

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable>
  extends CrudRepository<T, ID> {

  Iterable<T> findAll(Sort sort);

  Page<T> findAll(Pageable pageable);
}

着重看Page<T> findAll(Pageable pageable);这个方法,它就是与分页有关的,它的参数是一个接口,我们使用时传入一个PageRequest。如下是Controller层的一个使用:

@GetMapping("/list")
    public ModelAndView list(@RequestParam(value = "page" ,defaultValue="1") Integer page,
                             @RequestParam(value = "size",defaultValue = "10") Integer size,
                             Map<String,Object> map){
        PageRequest request=new PageRequest(page-1,size);
        Page<ProductInfo> productInfoPage=productService.findAll(request);
        map.put("productInfoPage",productInfoPage);
        map.put("currentPage",page);
        map.put("size",size);
        return new ModelAndView("product/list",map);
    }

它调用的Service层如下:

@Override
public Page<ProductInfo> findAll(Pageable pageable) {
    return repository.findAll(pageable);
}

DAO层更简单,没有任何方法,它的CRUD方法和分页方法都继承与父接口。并且它指定了两个泛型参数<ProductInfo,String>,第一个参数是操作的实体类,第二个参数是主键类型。

public interface ProductInfoRepository extends JpaRepository<ProductInfo,String>{

}

对于Controller层返回的Page<ProductInfo> productInfoPage对象,在视图层我们使用的是freemarker模板引擎,分页的使用方式如下:

<div class="col-md-12 column">
    <ul class="pagination pull-right">
    <#if currentPage lte 1>
        <li class="disabled"><a href="#">上一页</a></li>
    <#else>
        <li><a href="/sell/seller/product/list?page=${currentPage - 1}&size=${size}">上一页</a></li>
    </#if>

    <#list 1..productInfoPage.getTotalPages() as index>
        <#if currentPage == index>
            <li class="disabled"><a href="#">${index}</a></li>
        <#else>
            <li><a href="/sell/seller/product/list?page=${index}&size=${size}">${index}</a></li>
        </#if>
    </#list>

    <#if currentPage gte productInfoPage.getTotalPages()>
        <li class="disabled"><a href="#">下一页</a></li>
    <#else>
        <li><a href="/sell/seller/product/list?page=${currentPage + 1}&size=${size}">下一页</a></li>
    </#if>
    </ul>
</div>

@NoRepositoryBean

观察一下,可以看到JpaRepository,PagingAndSortingRepository这些接口都有一个@NoRepositoryBean注解,用官方的话说就是一个intermediate Repository,它会告诉Spring Data不会为这个接口创建代理bean,因为我们JpaRepository这些Repo都是为了来扩展功能的,是用来被继承的,而我们的ProductInfoRepository没有使用这个注解,Spring Data都会为它生成一个代理,并实现里面的方法。

该问题可以参考Stackoverflow上的的解答:Understanding the Spring Data JPA @NoRepositoryBean interface

参考:Spring Data JPA文档