Spring Data JPA 学习笔记(一)中文手册

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



作者:9527华安
链接:https://www.jianshu.com/p/aebc011fcb7d
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

猜你喜欢

转载自blog.csdn.net/luomao2012/article/details/97888169