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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容