Spring Data JPA 入门,有这一篇就够了!

最近对 Spring Data JPA 进行了一些研究,基本上是参考 Spring Data JPA - Reference Documentation 官方文档,总结分享出来,你们能够了解一下 Spring Data JPA 的基本用法。
html




01Introduction

java



官方定义:git

Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. It makes it easier to build Spring-powered applications that use data access technologies.github


要解释清楚 Spring Data JPA 是什么,那么须要一步步提及:redis


  • 期初 JAVA 须要经过各个数据库厂商提供的API进行数据库的访问,后来 JAVA 提出了 JDBC,程序直接使用 JDBC 这套规范就能够跟各个数据库进行对接;spring

  • 接着诞生了 ORM 技术,简化了 Java 对象的持久化工做,出现了 Hibernate、 TopLink 等 ORM 框架;mongodb

  • Sun 公司在 JDK1.5 的时候,吸取了 Hibernate、 TopLink等 ORM 框架的优势,提出了 Java 持久化规范:JPA ;数据库

  • Hibernate 在 3.2 的时候提供了 JPA 的实现,其他的 JPA 的供应商还有诸如 OpenJPA、 Toplink等;Spring 在作持久化这一块的工做,开发了 Spring-data-xxx 这一系列包,如:Spring-data-jpa, Spring-data-redis, Spring-data-mongodb 等等,这些都是 Spring 提供的基于 JPA 和其余一些 NOSQL 的 Repository。api


Spring Data JPA 是在 JPA 规范的基础下提供了 Repository 层的实现,可是使用哪一款 ORM 须要你本身去决定;相比咱们更为熟悉的 Hibernate 和 MyBatis, Spring Data JPA 能够看作更高层次的抽象。app




02Dependencies



具体步骤能够参考官网:[spring-data-jpa/quick-start]


<dependencies>        <dependency>            <groupId>org.springframework.data</groupId>            <artifactId>spring-data-jpa</artifactId>        </dependency></dependencies>


若是要在Spring Boot下使用Spring Data JPA的话,须要引入:


<dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter-data-jpa</artifactId></dependency>




03Working with Spring Data Repositories



让咱们看一个简单的例子,实现增删查改的功能;从例子中咱们能够发现,能够经过方法名称的定义,就能够达到 SQL 的效果:


好比 findByName(String name) 就至关于:


select * from user where name = ?


public interface UserCrudRepository                   extends CrudRepository<User, String>{        User findOne(String userid);        List<User> findByName(String name);        List<User> findByNameAndAgeLessThan(String name, int age);        void deleteByNameAndAgeLessThan(String name, int age);        List<User> findDistinctByName(String name);        List<User> findByNameIgnoreCase(String name);        User findFirstByOrderByUseridDesc();}




04Repositories



Spring Date JPA提供了几个接口:


  • Repository:最顶层的接口,是一个空的接口,目的是为了统一全部Repository的类型,且能让组件扫描的时候自动识别。
  • CrudRepository:是Repository的子接口,提供CRUD的功能。
  • PagingAndSortingRepository :是CrudRepository的子接口,添加分页和排序的功能。
  • JpaRepository :是PagingAndSortingRepository的子接口,增长了批量操做等功能。


以上四个 XxxRepositoty 愈来愈具体、功能愈来愈丰富,从使用的角度应该是继承本身用到的最小集,如需扩展再作调整,嫌麻烦的话直接继承 JpaRepository 也不要紧。




05Query creation



能够直接使用 CrudRepository 中的 findOne 和 findAll 方法:



public interface UserCrudRepository                   extends CrudRepository<User, String>{    /**     * CrudRepository 中存在的方法,这里能够不写此方法     * CrudRepository中存在的方法:     * https://github.com/spring-projects/spring-data-commons/blob/master/src/main/java/org/springframework/data/repository/CrudRepository.java#L27     */    User findOne(String userid);    /**     * 查询全部 能够不写此方法     * 不写,须要在查询完成后强转:List<User> userAfterDel = (List<User>) this.repository.findAll();     * 写:List<User> userAfterDel = this.repository.findAll();     */    //List<User> findAll();}


也能够对查询方法进行扩展,扩展的格式为:find…By, read…By, query…By, get…By, count…By 。


public interface UserCrudRepository                    extends CrudRepository<User, String>{    /**     * 按照姓名查询,非主键 更多用法参考:     * http://docs.spring.io/spring-data/jpa/docs/1.10.6.RELEASE/reference/html/#new-features.1-10-0     * Table 4. Supported keywords inside method names     *     * @param name     * @return     */    List<User> findByName(String name);     List<User> queryByName(String name);     List<User> findByNameAndAgeLessThan(String name, int age);     List<User> findDistinctByName(String name);     List<User> findByNameIgnoreCase(String name);}


若是数据库字段带有[ _ ]符号的,JAVA的Entity类中对应的属性不能带有[ _ ],须要经过@Column(name = "")标签进行关联。


若是JAVA的Entity类中属性按照驼峰格式命名,也须要经过@Column(name = "")标签进行关联。




06Naming Strategy



解决上面的第二点问题:若是 JAVA 的 Entity 类中属性按照驼峰格式命名,也须要经过 @Column(name = "") 标签进行关联。


JPA 中的 NamingStrategy 接口(最早是 Hibernate 中提出),实现接口中的表名与列名命名函数,能够完成本身的命名策略设定。


举个例子,咱们常常会遇到这样的问题,表的列明都是带的:user_id,user_name…,若是 User.java 中的属性都是不带  的,那么须要使用 @Column 标签进行和数据库字段的绑定:


@Entity@Table(name = "USER")@Datapublic class User {  @Column(name = "user_id")  @Id  private String userId;   @Column(name = "user_name")  //若是这样的字段不少,也是一个比较大的工做量  private String userName;}


而经过 NamingStrateg y接口的实现,能够统一的完成驼峰转下划线的功能,userId → user_id 、 userName → user_name;


Spring Boot提供了Configure JPA properties,咱们能够能够直接在application.yml中进行配置:


spring.jpa.hibernate.naming.physical-strategy = com.example.MyPhysicalNamingStrategy



解决上面的第二点问题:若是 JAVA 的 Entity 类中属性按照驼峰格式命名,也须要经过 @Column(name = "") 标签进行关联。


在Hibernate5中,提供了两种映射策略:


--不作任何修改,userId→ userIduserid → useridspring.jpa.hibernate.naming.physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl        
--将驼峰修改为下划线,userIduser_id,若是不作配置,默认这个策略spring.jpa.hibernate.naming.physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy  


@Entity@Table(name = "USER")@Datapublic class User {  @Id  private String userId;  //不须要在使用@Column(name = "user_id"),会自动转成 user_id   private String userName; //自动转成 user_name}



同时会有一个问题,若是数据库中表的列名规则不统一,好比有些带下划线,有些不带下划线的话,那么在编写Entity的时候,带下划线的字段写成驼峰格式,不带下划线的字段写成全小写,看起来也比较怪异。


@Entity@Table(name = "USER")@Datapublic class User {  @Id  private String userId;  //不须要在使用@Column(name = "user_id")   private String userName;   private String cardid;  //列名为 cardid ,不带下划线,因此这里不能写成驼峰格式,可是从代码风格上看,感受比较怪异}


若是有更特殊的要求,例如数据库中全部的表或者字段都带固定格式的前缀,CSC_USER 表,字段有CSC_USER_ID, CSC_USER_NAME,能够本身去实现 NamingStrategy 接口(若是是 Spring Boot 的话,须要实现 PhysicalNamingStrategy 接口),在里面去增长前缀名称。




07Naming Strategy



使用 @Query Annotation 绑定SQL语句(这样写,看起来是否是更直观):


public interface UserCrudRepository                   extends CrudRepository<User, String>{    //@Query("select u from User u where u.name = ?1")    //@Query("select u from User u where 1=1 and u.name = ?1")    //@Query("select u.userid from User u where 1=1 and u.name = ?1")    //@Query(value="select * from User u where u.name = ?1", nativeQuery = true)    List<User> findByName(String name);}




使用 @Param Annotation 能够将参数中的名字和query中的名字进行绑定:


public interface UserCrudRepository                    extends CrudRepository<User, String>{    @Query("select u from User u where u.name = :name and u.gender = :gender")    List<User> findUsersByNameAndGender(@Param("name")String name , @Param("gender")String gender);}




08Using Sort



public interface UserCrudRepository                    extends CrudRepository<User, String>    //可使用 UserCrudRepository    //在方法使用 OrderBy    List<User> findByOrderByUseridDesc();}


public interface UserPageRepository                   extends PagingAndSortingRepository<User, String> {      //使用 Sort     List<User> findAll(Sort sort);}




09Using Pageable



须要继承PagingAndSortingRepository 或更高级的 JpaRepository 。


public class UserPageRepositoryTest {  @Test  public void findPageable() throws Exception {    Page<User> usersPageOne = this.repository.findAll(new PageRequest(0, 2)); //1.PageRequest(int page, int size) 2.page从0开始     assertThat(usersPageOne.getContent().size()).isEqualTo(2);    assertThat(usersPageOne.getContent().get(0).getUserId()).isEqualTo("1");    assertThat(usersPageOne.hasPrevious()).isEqualTo(false);  //判断是否有上一页     Page<User> usersPageTwo = this.repository.findAll(usersPageOne.nextPageable());  //查询下一页    assertThat(usersPageTwo.getContent().size()).isEqualTo(2);    assertThat(usersPageTwo.getContent().get(0).getUserId()).isEqualTo("3");  }}


也能够经过 [first] 或 [top] 关键字,取得结果集的前N条数据。


public interface UserCrudRepository                    extends CrudRepository<User, String>{    User findFirstByOrderByUseridDesc();    User findFirstByGenderOrderByUseridDesc(String gender);}




10Save and Delete methods



保存一个 Entity 须要使用 CrudRepository.save(…) 方法,包括 Insert 和 Update。


Delete 方法基本能够参考 Query 方法,除了 delete() 和 deleteAll() 以外,还可使用 deleteBy… 的方式,一样也可使用 @query 标签。


在 JpaRepository 接口中,提供了 deleteInBatch() 的方法:


Deletes the given entities in a batch which means it will create a single {@link Query}.




11Why use Spring Date JPA



能够看出 Spring Data JPA 和经常使用的 Hibernate 和 MyBatis 相比,在编码上更简洁,减小 Boilerplate Code


实际上交易型的微服务应用的查询一般只是明细查询或简单的列表查询,而真正的复杂查询或者数据分析一般应是另建应用或者全文检索或者 ODS/DW 之类、并从交易型微服务同步数据,而这一部分是走 MyBatis 仍是 NOSQL 另说。


Feign 虽然解决的不是 Spring Data Repository 同一个领域的问题,可是实现哲学是一致的,经过接口、COC 尽可能减小 Rest 调用的 Boilerplate Code。


@FeignClient(url = "https://api.github.com")interface GitHubClient {      @RequestMapping(method = RequestMethod.GET, value = "/repos/{owner}/{repo}/contributors")    List<Contributor> contributors(@RequestParam("owner") String owner, @RequestParam("repo") String repo); }
相关文章
相关标签/搜索