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
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。