美文网首页
SpringData之JPA

SpringData之JPA

作者: 泊浮目 | 来源:发表于2016-12-31 17:48 被阅读3100次

    SpringData为我们使用统一的API来对主流的数据存储技术进行数据访问操作提供了支持(基于JPA标准)。这是Spring通过SpringDataCommons项目来实现的,它是上述各种SpringData项目的依赖。SpringDataCommons让我们在使用关系型或非关系型数据访问技术时都使用基于Spring的统一标注,该标准包含CRUD(创建、获取、更新、删除)查询、排序和分页的相关操作。

    SpringDataCommons

    SpringDataCommons是SpringDataRepository抽象。使用SpringDataRepository可以极大地减少数据访问层的代码。既然是数据访问操作的统一标准,拿肯定是定义了各种各样和数据访问相关的接口,SpringDataRepository抽象的根接口是Repository接口:

    public interface Repository<T, ID extends Serializable> {
    
    }
    //从源码中可以看出,它接受领域类(JPA为实体类)和领域类的id类型作为类型参数
    

    它的子接口CrudRepository定义了和CRUD操作相关的内容:

    @NoRepositoryBean
    public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
    
        /**
         * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the
         * entity instance completely.
         *
         * @param entity
         * @return the saved entity
         */
        <S extends T> S save(S entity);
    
        /**
         * Saves all given entities.
         *
         * @param entities
         * @return the saved entities
         * @throws IllegalArgumentException in case the given entity is {@literal null}.
         */
        <S extends T> Iterable<S> save(Iterable<S> entities);
    
        /**
         * Retrieves an entity by its id.
         *
         * @param id must not be {@literal null}.
         * @return the entity with the given id or {@literal null} if none found
         * @throws IllegalArgumentException if {@code id} is {@literal null}
         */
        T findOne(ID id);
    
        /**
         * Returns whether an entity with the given id exists.
         *
         * @param id must not be {@literal null}.
         * @return true if an entity with the given id exists, {@literal false} otherwise
         * @throws IllegalArgumentException if {@code id} is {@literal null}
         */
        boolean exists(ID id);
    
        /**
         * Returns all instances of the type.
         *
         * @return all entities
         */
        Iterable<T> findAll();
    
        /**
         * Returns all instances of the type with the given IDs.
         *
         * @param ids
         * @return
         */
        Iterable<T> findAll(Iterable<ID> ids);
    
        /**
         * Returns the number of entities available.
         *
         * @return the number of entities
         */
        long count();
    
        /**
         * Deletes the entity with the given id.
         *
         * @param id must not be {@literal null}.
         * @throws IllegalArgumentException in case the given {@code id} is {@literal null}
         */
        void delete(ID id);
    
        /**
         * Deletes a given entity.
         *
         * @param entity
         * @throws IllegalArgumentException in case the given entity is {@literal null}.
         */
        void delete(T entity);
    
        /**
         * Deletes the given entities.
         *
         * @param entities
         * @throws IllegalArgumentException in case the given {@link Iterable} is {@literal null}.
         */
        void delete(Iterable<? extends T> entities);
    
        /**
         * Deletes all entities managed by the repository.
         */
        void deleteAll();
    }
    

    CrudRepository的子接口PagingAndSortingRepository定义了分页和排序操作相关的内容:

    /**
     * Extension of {@link CrudRepository} to provide additional methods to retrieve entities using the pagination and
     * sorting abstraction.
     *
     * @author Oliver Gierke
     * @see Sort
     * @see Pageable
     * @see Page
     */
    @NoRepositoryBean
    public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
    
        /**
         * Returns all entities sorted by the given options.
         *
         * @param sort
         * @return all entities sorted by the given options
         */
        Iterable<T> findAll(Sort sort);
    
        /**
         * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
         *
         * @param pageable
         * @return a page of entities
         */
        Page<T> findAll(Pageable pageable);
    }
    
    

    不同的数据访问计数也提供了不同的Repository,如SpringDataJPA有JpaRepository、SpringDataMongoDB有MongoRepository。

    SpringData项目还给我们提供了一个激动人心的功能,即可以根据属性名进行记数、删除、查询方法等操作,如:

    public interface PersonRepository extends Repository<Person,Long>{
     //按照年龄计数
     Long countByAge(Integer age);
     //按照名字删除
     Long deleteByName(String name);
     //按照名字查询
     List<Person>findByName(String name);
     //按照名字和地址查询
     List<Person>findByNameAndAddress(String name,String address);
    }
    

    如何开始?

    定义数据访问层

    使用SpringDataJPA建立数据访问层十分简单,只需定义一个继承JpaRepository的接口即可,定义如下:

    public interface PersonRepository extends JpaRepository<Person,Long>{
        //定义数据访问操作的方法
    }
    

    配置使用SpringDataJPA

    在Spring环境中,使用SprintDataJPA可通过@EnableJpaRepositories注解来开启SpringDataJPA的支持,@EnableJpaRepositories接受的value参数用来扫描参数访问层所在包下的数据访问的接口定义。

    @Configuration
    @EnableJpaRepositories("com.camile.repos")
    public class JpaConfiguration{
        @Bean
        public EntityManagerFactory EntityManagerFactory(){
    
        }
        //还需配置DataSource、PlatformTransactionManager等相关必须bean
    }
    

    定义查询方法

    在讲解查询方法前,假设我们有一张数据表叫PERSON,有ID(Number)、NAME(Varchar2)、AGE(Number)、ADDRESS(Varchar2)几个字段;对应的实体类叫Person,分别有id(Long)、name(String)、age(Integer)、address(String)。下面我们就以这个简单的实体查询作为演示。

    根据属性名查询

    SpringDataJPA支持通过定义在Repository接口中的方法名定义查询,而方法名是根据实体类的属性名来确定的。

    常规查询。根据属性名来定义查询方式,示例如下:

    public interface PersonRepository extends JpaRepository<Person,Long>{
        //通过名字相等查询,参数为name
        //相当于JPQL:select p from Person p where p.name=?1
        List<Person> findByName(String name);
        //通过名字like查询,参数为name
        //相当于JPQL:select p from Person p where p.name like ?1
        List<Person>findByNameLike(String name);
        //通过名字和地址查询,参数为name和address
        //相当于JPQL:select p from Person p where p.name= ?1 and p.address =?2
        List<Person>findByNameAndAddress(String name,String address);
    }
    

    从代码可以看出,这里使用了findBy、Like、And这样的关键字。其中findBy可以用find、read、readBy、query、queryBy、get、getBy来代替。
    而Like和And这类查询关键字:

    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 (参数前面加 %)
    EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (参数后面加 %)
    Containing findByFirstnameContaining … where x.firstname like ?1 (参数两边加 %)
    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> age) … 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)

    限制结果数量。结果数量是用top和first关键字实现的,例如:

    public interface PersonRepository extends JpaRepository<Person,Longs>{
    //获得符合查询条件的前10条数据
    List<Person>findFirst10ByName(String name);
    //获得符合查询条件的前30条数据
    List<Person>findTop30ByName(String name);
    }

    使用JPA的NamedQuery查询

    SpringDtaJPA支持用JPA的NameQuery来定义查询方法,即一个名称映射一个查询语句。定义如下:

    @entitya
    @NamedQuery(name="person.findByName",query = "select p from Person p where p.name=?1")
    public class Person{
    
    }
    

    使用如下语句:

    public interface PersonRepository extends JpaRepository<Person,Long>{
        //这时我们使用的是NamedQuery里定义的查询语句,而不是根据方法名称查询
        List<Person>findByName(String name);
    }
    
    使用@Query查询
    • 使用参数索引。SpringDataJPA还支持用@Query注解在接口的方法上实现查询,例如:
    public interface PersonRepository extends JpaRepository<Person,Long>{
        @Query("select p from Person p where p.address=?1")
        List<Person>findByAddress(String address);
    }
    
    • 使用命名参数。上面的例子是使用参数的索引号来查询的,在SpringDataJPA里还支持在语句里用名称来匹配查询参数,例如:
    public interface PersonRepository extends JpaRepository<Person,Long>{
        @Query("select p from Person p where p.address = :address")
        List<Person>findByAddress(@Param("address")String address);
    }
    
    • 使用命名参数。上面的例子是使用参数的索引号来查询的,在SpringDataJPA里还支持在语句里用名称来匹配查询参数的,例如:
      public interface PersonRepository extends JpaRepository<Person,Long>{
      @Query("select p from Person p where p.address = :address")
      List<Person>findByAddress(@Param("address")String address);
      }
    • 更新查询。SpringDtaJPA支持@Modifying和@Query注解组合来事件更新查询,例如:
    public interface PersonRepository extends JpaRepository<Person,Long>{
        @Modifying
        @Transactional
        @Query("update Person p set p.name=?1")
        int setName(String name);//int 返回值表示更新语句影响的行数
    }
    
    Specification

    JAP提供了基于准则查询的方式,即Criteria查询。而SpringDataJPA提供了一个Specification(规范)接口让我们可以更方便地构造准则查询,Specification接口定义了一个toPredicate方法用来构造查询条件。

    • 定义。我们接口类必须实现JpaSpecificationExecutor接口,代码如下:
    public interface PersonRepository extends JpaRepository<Person,Long>,JpaSpecificationExecutor<Person>{
    
    }
    
    排序与分页

    SpringDataJPA充分考虑了在实际开发中所必需的排序和分页的场景,为我们提供了Sort类以及Page接口和Pageable接口。

    • 比如现在有个DAO层的类继承了JpaRepository
    public interface PersonRepository extends JpaRepository<Person,Long>{
        List<Person>findByName(String name,Sort sort);
        Page<Person>findByName(String name,Pageable pageable);
    }
    
    • 使用排序
    List<Person>people = personRepository.findByName("xx",new Sort(Direction.ASC,"age"));
    
    • 使用分页
    Page<Person>people2 = personRepository.findByName("xx",new PageRequest(0,10));
    
    自定义Repository的实现

    hibernate提供了根据实体类自动维护数据表结构的功能,可通过在application.properties文件里的spring.data.hibernate.ddl-auto进行配置,值如下:

    • create:启动时删除上一次生成的表,并根据实体类生成表,表中数据会被清空。
    • create-drop:启动时根据实体类生成表,sessionFactory关闭时表会被删除。
    • update:启动时会根据实体类生成表,当实体类属性变动的时候,表结构也会更新,在初期开发阶段使用此选项。
    • validate:启动时验证实体类和数据表是否一致,在我们的数据结构稳定时采用此选项。
    • none:不采取任何措施。

    相关文章

      网友评论

          本文标题:SpringData之JPA

          本文链接:https://www.haomeiwen.com/subject/gjfbvttx.html