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/