SpringData之JPA

2016-12-31  本文已影响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查询
public interface PersonRepository extends JpaRepository<Person,Long>{
    @Query("select p from Person p where p.address=?1")
    List<Person>findByAddress(String address);
}
public interface PersonRepository extends JpaRepository<Person,Long>{
    @Query("select p from Person p where p.address = :address")
    List<Person>findByAddress(@Param("address")String address);
}
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方法用来构造查询条件。

public interface PersonRepository extends JpaRepository<Person,Long>,JpaSpecificationExecutor<Person>{

}
排序与分页

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

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进行配置,值如下:

上一篇下一篇

猜你喜欢

热点阅读