美文网首页springboot spring-data
Spring Data JPA - 中文手册

Spring Data JPA - 中文手册

作者: Herman7z | 来源:发表于2018-03-22 14:10 被阅读1633次

    Spring Data JPA - 中文手册

    参考文档:
    http://blog.csdn.net/liuchuanhong1/article/details/70244261?utm_source=gold_browser_extension
    https://docs.spring.io/spring-data/jpa/docs/1.11.9.RELEASE/reference/html/#specifications
    https://www.v2ex.com/t/350737

    项目中使用spring data jpa

    @EnableJpaRepositories
    class Config {}
    

    查询方法的生成策略 Query lookup strategies

    • CREATE 根据方法的名字直接创建对应的查询语句
    • USE_DECLARED_QUERY 使用声明的查询语句,如果每一找到声明的语句将会抛出一个异常,声明一个语句使用 @NamedQuery
    • CREATE_IF_NOT_FOUND 首先查找是否已经有声明的查询语句,如果每一再创建,这是默认的策略

    通过方法名来定义语句

    public interface PersonRepository extends Repository<User, Long> {
    
      // 使用 distinct 关键字
      List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
      List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
    
      // 忽略大小写进行匹配 IgnoreCase
      List<Person> findByLastnameIgnoreCase(String lastname);
      // 对所有的查询条件进行忽略大小写进行匹配 IgnoreCase
      List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
    
      // Order By 语句
      List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
      List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
      
      //Person 中有个Address, Address中有zipCode属性,直接通过zipCode来查询Persion
      List<Person> findByAddressZipCode(ZipCode zipCode);
      //和上面的方法完成同样的功能, 使用 _ 可以用来区分属性,避免生成错误的语句
      List<Person> findByAddress_ZipCode(ZipCode zipCode);
      
      
      //限制返回数据的条数 Limiting query results
      User findFirstByOrderByLastnameAsc();
      User findTopByOrderByAgeDesc();
      
      Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
      Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
      
      List<User> findFirst10ByLastname(String lastname, Sort sort);
      List<User> findTop10ByLastname(String lastname, Pageable pageable);
      
      //异步查询,调用查询方法后立即返回 Async query results
      @Async
      Future<User> findByFirstname(String firstname);
      @Async
      CompletableFuture<User> findOneByFirstname(String firstname);
      @Async
      ListenableFuture<User> findOneByLastname(String lastname);    
    }
    

    为所有生成的Repository添加一些额外的方法

    1. 声明一个接口,定义出需要添加的方法
    @NoRepositoryBean
    public interface MyRepository<T, ID extends Serializable>
      extends PagingAndSortingRepository<T, ID> {
    
      void sharedCustomMethod(ID id);
    }
    
    2. 实现定义的接口
    public class MyRepositoryImpl<T, ID extends Serializable>
      extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {
    
      private final EntityManager entityManager;
    
      public MyRepositoryImpl(JpaEntityInformation entityInformation,
                              EntityManager entityManager) {
        super(entityInformation, entityManager);
    
        // Keep the EntityManager around to used from the newly introduced methods.
        this.entityManager = entityManager;
      }
    
      public void sharedCustomMethod(ID id) {
        // implementation goes here
      }
    }
    
    3.配置实现类
    @Configuration
    @EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
    class ApplicationConfiguration { … }
    

    Web support

    @Configuration
    @EnableWebMvc
    @EnableSpringDataWebSupport
    class WebConfiguration { }
    

    @EnableSpringDataWebSupport 将会开启一些组件,具体可以打开源码查看

    • PageableHandlerMethodArgumentResolver 通过参数可以自动注入 Pageable 对象到控制器
      page -> Page you want to retrieve, 0 indexed and defaults to 0. |
      size -> Size of the page you want to retrieve, defaults to 20. |

    • SortHandlerMethodArgumentResolver 通过参数可以自动注入 Sort 对象到控制器
      sort -> Properties that should be sorted by in the format property,property(,ASC|DESC). Default sort direction is ascending. Use multiple sort parameters if you want to switch directions, e.g. ?sort=firstname&sort=lastname,asc. |

    • DomainClassConverter 传入一个对象Id 就可以直接转换成 需要对象

     @Controller
        @RequestMapping("/users")
        public class UserController {
        
          @Autowired UserRepository repository;
        
          @RequestMapping
          public String showUsers(Model model, Pageable pageable) {
        
            model.addAttribute("users", repository.findAll(pageable));
            return "users";
          }
        }
    

    如果在需要注入对个Pageable对象到controller中,可以使用@Qualifier来定义前缀,下划线分隔,eg: foo_page=1

    public String showUsers(Model model,
          @Qualifier("foo") Pageable first,
          @Qualifier("bar") Pageable second) { … }
    

    @PageableDefault 配置默认的分页,如果前端没有传入分页信息,可以使用来设置默认的分页,默认是 new PageRequest(0, 20)

    Supported keywords inside method names

    Keyword Sample JPQL snippet
    And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
    Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
    Is,Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
    Between findByStartDateBetween … where x.startDate between ?1 and ?2
    LessThan findByAgeLessThan … where x.age < ?1
    LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
    GreaterThan findByAgeGreaterThan … where x.age > ?1
    GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
    After findByStartDateAfter … where x.startDate > ?1
    Before findByStartDateBefore … where x.startDate < ?1
    IsNull findByAgeIsNull … where x.age is null
    IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
    Like findByFirstnameLike … where x.firstname like ?1
    NotLike findByFirstnameNotLike … where x.firstname not like ?1
    StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
    EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
    Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
    OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
    Not findByLastnameNot … where x.lastname <> ?1
    In findByAgeIn(Collection<Age> ages) … where x.age in ?1
    NotIn findByAgeNotIn(Collection<Age> ages) … where x.age not in ?1
    True findByActiveTrue() … where x.active = true
    False findByActiveFalse() … where x.active = false
    IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)
    使用@Query来声明语句
    public interface UserRepository extends JpaRepository<User, Long> {
    
      @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
        countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
        nativeQuery = true)
      Page<User> findByLastname(String lastname, Pageable pageable);
    }
    
    Using SpEL expressions
    public interface UserRepository extends JpaRepository<User,Long> {
    
      @Query("select u from #{#entityName} u where u.lastname = ?1")
      List<User> findByLastname(String lastname);
    }
    

    entityName 表示实体类的名字

    使用查询提示,具体的查询提示的值是根据JPA底层实习框架提供的
    public interface UserRepository extends Repository<User, Long> {
    
      @QueryHints(value = { @QueryHint(name = "name", value = "value")},
                  forCounting = false)
      Page<User> findByLastname(String lastname, Pageable pageable);
    }
    

    自定义查询返回的返回结果

    1. 通过定义接口的方式来返回数据

    假如实体对象:

    class Person {
      @Id UUID id;
      String firstname, lastname;
      Address address;
    
      static class Address {
        String zipCode, city, street;
      }
    }
    

    可以这样定义一个接口:

    interface PersonSummary {
      String getFirstname();
      String getLastname();
      AddressSummary getAddress();
    
      interface AddressSummary {
        String getCity();
      }
    }
    

    查询方法的定义:

    interface PersonRepository extends Repository<Person, UUID> {
      Collection<PersonSummary> findByLastname(String lastname);
    }
    

    这样就可以完成自定义查询,还有其他方式定义接口

    interface NamesOnly {
    
      @Value("#{target.firstname + ' ' + target.lastname}")
      String getFullName();
      …
    }
    

    组合了firstname lastname, 返回的对象将会用 target 变量替代。如果想要做更加复杂的编程可以使用下面的方法,使用default定义一个方法

    interface NamesOnly {
    
      String getFirstname();
      String getLastname();
    
      default String getFullName() {
        //定义自己的逻辑实现,可以通过ApplicationContext 来获取bean对象来调用方法
        return getFirstname.concat(" ").concat(getLastname());
      }
    }
    

    调用容器中的Bean来返回结果的另一种方式@myBean

    @Component
    class MyBean {
    
      String getFullName(Person person) {
        …
      }
    }
    
    interface NamesOnly {
    
      @Value("#{@myBean.getFullName(target)}")
      String getFullName();
      …
    }
    

    如果想要使用 方法中的参数可以用这种方式

    interface NamesOnly {
    
      @Value("#{args[0] + ' ' + target.firstname + '!'}")
      String getSalutation(String prefix);
    }
    

    args[i] i 表示方法中的第几个

    2. 通过定义DTO的方式来返回数据

    这种方式底层会根据DTO暴露的构造放的参数名字来加载 需要的字段
    定义的DTO

    class NamesOnly {
      private final String firstname, lastname;
    
      NamesOnly(String firstname, String lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
      }
      String getFirstname() {
        return this.firstname;
      }
      String getLastname() {
        return this.lastname;
      }
      // equals(…) and hashCode() implementations
    }
    

    定义的Repository

    interface PersonRepository extends Repository<Person, UUID> {
      Collection<T> findByLastname(String lastname, Class<T> type);
    }
    

    使用方式如下:

    void someMethod(PersonRepository people) {
      Collection<Person> aggregates = people.findByLastname("Matthews", Person.class);
      Collection<NamesOnly> aggregates = people.findByLastname("Matthews", NamesOnly.class);
    }
    
    存储过程的使用

    参考:https://docs.spring.io/spring-data/jpa/docs/1.11.9.RELEASE/reference/html/#jpa.stored-procedures

    Specifications
    public class CustomerSpecs {
    
      public static Specification<Customer> isLongTermCustomer() {
        return new Specification<Customer>() {
          public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query,
                CriteriaBuilder builder) {
    
             LocalDate date = new LocalDate().minusYears(2);
             return builder.lessThan(root.get(_Customer.createdAt), date);
          }
        };
      }
    
      public static Specification<Customer> hasSalesOfMoreThan(MontaryAmount value) {
        return new Specification<Customer>() {
          public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
                CriteriaBuilder builder) {
    
             // build query here
          }
        };
      }
    }
    
    List<Customer> customers = customerRepository.findAll(isLongTermCustomer());
    
    Locking
    interface UserRepository extends Repository<User, Long> {
      // Plain query method
      @Lock(LockModeType.READ)
      List<User> findByLastname(String lastname);
    }
    
    Auditing

    提供的注解,在实体中添加:
    @CreatedBy, @LastModifiedBy, @CreatedDate, @LastModifiedDate

    实现接口AuditorAware@CreatedBy, @LastModifiedBy 设置值

    class SpringSecurityAuditorAware implements AuditorAware<User> {
      public User getCurrentAuditor() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
          return null;
        }
        return ((MyUserDetails) authentication.getPrincipal()).getUser();
      }
    }
    

    最后在实体上添加注解@EntityListeners(AuditingEntityListener.class)

    @Entity
    @EntityListeners(AuditingEntityListener.class)
    public class MyEntity {
    }
    
    获取 EntityManager 对象

    可以使用@PersistenceContext. spring data jpa 还提供了一种方式:

    class UserRepositoryImpl implements UserRepositoryCustom {
      private final EntityManager em;
      @Autowired
      public UserRepositoryImpl(JpaContext context) {
        this.em = context.getEntityManagerByManagedType(User.class);
      }
    }
    

    https://docs.spring.io/spring-data/jpa/docs/1.11.9.RELEASE/reference/html/#appendix

    更多spring 相关文章:
    http://blog.xianshiyue.com/tag/springboot/
    http://blog.xianshiyue.com/tag/spring-cloud/
    http://blog.xianshiyue.com/tag/springmvc/

    相关文章

      网友评论

        本文标题:Spring Data JPA - 中文手册

        本文链接:https://www.haomeiwen.com/subject/ejjaqftx.html