Spring Data Jpa 学习笔记

时间:2022-05-15 15:13:39

 

什么的Spring Data JPA

Spring Data JPA为Java Persistence API(JPA)提供了存储库支持。它简化了需要访问JPA数据源的应用程序的开发。

maven依赖

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

核心概念

1、核心接口Repository

public interface Repository<T, ID>

It takes the domain class to manage as well as the ID type of the domain class as type arguments. This interface acts primarily as a marker interface to capture the types to work with and to help you to discover interfaces that extend this one.

T : 表实体类型的泛型。
ID: 表关键字类型,可为组合。
举例:

interface UserRepository extends CrudRepository<User, Long>

2、CrudRepository

public interface CrudRepository<T, ID> extends Repository<T, ID>

提供复杂的CURD功能。 除了已经提供的接口,还可以新增接口,并不用自己实现。 比如countByXXX,deleteByXXX(返回个数),findByXXX,removeByXXX(返回集合),如:

  long deleteByLastname(String lastname);  
  List<User> removeByLastname(String lastname);

3、PagingAndSortingRepository

public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID>

多提供了2个接口,用于分页和排序访问。

4、JpaRepository

public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T>

从实现上看,是提供了一些批量操作接口,待考证。

5、@NoRepositoryBean

中间存储库接口使用注释@NoRepositoryBean。确保将该注释添加到Spring Data不应在运行时创建实例的所有存储库接口。

就像abstart class一样,注解了@NoRepositoryBean的接口不生成Bean。

初始化数据库

就是把存在 json或xml文件中的默认数据给导到数据库中。

定义库接口

1、支持可空性判断注解

@NonNull: Annotation to indicate that a specific parameter, return value, or field cannot be null (not needed on parameter and return value where @NonNullApi and @NonNullFields apply) .   
@Nullable: Annotation to indicate that a specific parameter, return value, or field can be null.    
@NonNullApi: Annotation at the package level that declares non-null as the default behavior for parameters and return values.   
@NonNullFields: Annotation at the package level that declares non-null as the default behavior for fields.

2、支持返回值Optional

Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress);

定义查询方法

两种方式 1、通过直接从方法名称派生查询。 2、通过使用手动定义的查询。

通过@EnableJpaRepositories(queryLookupStrategy = Key.CREATEIFNOT_FOUND)配置,值有如下3个:

    CREATE:根据名称创建。  
    USE_DECLARED_QUERY:尝试查找声明的,找不到抛异常。  
    CREATE_IF_NOT_FOUND(默认):先USE_DECLARED_QUERY,找不到按CREATE。  

名称关键字

1、find…By,read…By,query…By,count…By,和get…By
2、And Or 3、Is Equals
4、Between,LessThan,GreaterThan,After Before 
5、IgnoreCase、AllIgnoreCase
6、OrderBy...Asc/Desc
7、first或top,可接数字
8、Distinct
9、StartingWith EndingWith Containing
10、 Like,NotNull Not
11、In NotIn True False

支持分页

附加排序参数:Sort(不支持函数) JpaSort.unsafe(支持函数) 
附加分页参数:Pageable
返回分页结果:Page

支持流式结果

@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();

Stream<User> readAllByFirstnameNotNull();

@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);

try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
  stream.forEach(…);
}

异步查询结果

@Async
Future<User> findByFirstname(String firstname);               

@Async
CompletableFuture<User> findOneByFirstname(String firstname); 

@Async
ListenableFuture<User> findOneByLastname(String lastname);    

命名查询 先在Entity中定义NamedQuery注解,再在Repository中添加方法

@Entity
@NamedQuery(name = "User.findByEmailAddress",
  query = "select u from User u where u.emailAddress = ?1")
public class User {

}
public interface UserRepository extends JpaRepository<User, Long> {

  List<User> findByLastname(String lastname);

  User findByEmailAddress(String emailAddress);
}

直接运用@Query

public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.emailAddress = ?1")
  User findByEmailAddress(String emailAddress);
}

使用命名参数可以防止位置出错

public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
  User findByLastnameOrFirstname(@Param("lastname") String lastname,
                                 @Param("firstname") String firstname);
}

使用原生SQL语句查询

该@Query注释允许通过设定运行的原生查询nativeQuery标志设置为true,如图以下示例:

public interface UserRepository extends JpaRepository<User, Long> {

  @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
  User findByEmailAddress(String emailAddress);
}

原生分页查询

Spring Data JPA目前不支持对本机查询进行动态排序,因为它必须操纵声明的实际查询,而对于本机SQL,它无法可靠地执行。但是,您可以通过自己指定计数查询来使用本机查询进行分页,如以下示例所示:

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);
}

使用SpEL表达式

  @Query("select u from #{#entityName} u where u.lastname = ?1")
  List<User> findByLastname(String lastname);

如上,可使用#{#entityName}代替实体名。

Querydsl 扩展

Querydsl定义了查询条件,使用如下:

public interface QuerydslPredicateExecutor<T> { 
      Iterable<T> findAll(Predicate predicate);  
      //....    more functionality omitted.
}

interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> {
}

Predicate predicate = user.firstname.equalsIgnoreCase("dave")
    .and(user.lastname.startsWithIgnoreCase("mathews"));

userRepository.findAll(predicate);

修改查询

@Modifying
@Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);

删除

派生删除查询是执行查询然后调用CrudRepository.delete(Iterable users)结果并保持行为与其他delete(…)方法的实现同步的快捷方式CrudRepository。

View

声明一个需要的结果的Interface

interface NamesOnly {

  String getFirstname();
  String getLastname();
}

定义返回接口的查询方法

interface PersonRepository extends Repository<Person, UUID> {

  Collection<NamesOnly> findByLastname(String lastname);
}

结果也可以扩展其他表的对应关系

interface PersonSummary {

  String getFirstname();
  String getLastname();
  AddressSummary getAddress();

  interface AddressSummary {
    String getCity();
  }
}

还可以用SpEL表达式做简单的组合,但不建议这么做,下面有更好的方法

interface NamesOnly {

  @Value("#{target.firstname + ' ' + target.lastname}")
  String getFullName();
  …
}

使用接口的方法获取组合结果

interface NamesOnly {

  String getFirstname();
  String getLastname();

  default String getFullName() {
    return getFirstname.concat(" ").concat(getLastname());
  }
}

使用Bean的方法获取组合结果

@Component
class MyBean {

  String getFullName(Person person) {
    …
  }
}

interface NamesOnly {

  @Value("#{@myBean.getFullName(target)}")
  String getFullName();
  …
}

为了方便扩展,可以用泛型在使用时才指定返回View类型

interface PersonRepository extends Repository<Person, UUID> {

  <T> Collection<T> findByLastname(String lastname, Class<T> type);
}

创建库实例

通过JavaConfig

要指定扫描路径:

@EnableJpaRepositories("com.acme.repositories")

在Spring容器外使用

RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);

自定义查询方法

class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  public void someCustomMethod(User user) {
    // Your custom implementation
  }
}

实现本身不依赖于Spring Data,可以是常规的Spring bean。因此,您可以使用标准依赖项注入行为将引用注入其他bean(例如a JdbcTemplate),参与方面等。

网络支持

参考文档

两个特性,没具体看

EntityManagerFactory
PlatformTransactionManager

基于注解的配置

@Configuration
@EnableJpaRepositories
@EnableTransactionManagement
class ApplicationConfig {

  @Bean
  public DataSource dataSource() {

    EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
    return builder.setType(EmbeddedDatabaseType.HSQL).build();
  }

  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setGenerateDdl(true);

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setPackagesToScan("com.acme.domain");
    factory.setDataSource(dataSource());
    return factory;
  }

  @Bean
  public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {

    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(entityManagerFactory);
    return txManager;
  }
}

上述配置类使用EmbeddedDatabaseBuilderAPI来设置嵌入式HSQL数据库spring-jdbc。然后Spring Data设置EntityManagerFactory并使用Hibernate作为示例持久性提供程序。这里声明的最后一个基础架构组件是JpaTransactionManager。最后,该示例使用@EnableJpaRepositories注释激活Spring Data JPA存储库

默认情况下,Spring Data JPA存储库是默认的Spring bean。它们是单例范围并且急切地初始化。在启动期间,他们已经与JPA EntityManager进行交互以进行验证和元数据分析。Spring Framework支持EntityManagerFactory在后台线程中初始化JPA ,因为该进程通常占用Spring应用程序中的大量启动时间。为了有效地利用后台初始化,我们需要确保尽可能晚地初始化JPA存储库。

从Spring Data JPA 2.1开始,您现在可以配置BootstrapMode

保存实体

可以使用该CrudRepository.save(…)方法执行保存实体。它通过使用基础JPA持久化或合并给定实体EntityManager。如果实体尚未持久化,则Spring Data JPA会通过调用该entityManager.persist(…)方法来保存实体。否则,它会调用该entityManager.merge(…)方法。

存储过程

按示例查询

它允许动态创建查询,并且不需要您编写包含字段名称的查询。

简单使用如下: 构造匹配器: 
1:

Person person = new Person();                         
person.setFirstname("Dave");                          

Example<Person> example = Example.of(person);   

2:

Person person = new Person();                          
person.setFirstname("Dave");                           

ExampleMatcher matcher = ExampleMatcher.matching()     
  .withIgnorePaths("lastname")                         
  .withIncludeNullValues()                             
  .withStringMatcherEnding();                          

Example<Person> example = Example.of(person, matcher); 

库扩展QueryByExampleExecutor接口:

public interface QueryByExampleExecutor<T> {

  <S extends T> S findOne(Example<S> example);

  <S extends T> Iterable<S> findAll(Example<S> example);

  // … more functionality omitted.
}

查询:

public interface PersonRepository extends JpaRepository<Person, String> { … }

public class PersonService {

  @Autowired PersonRepository personRepository;

  public List<Person> findPeople(Person probe) {
    return personRepository.findAll(Example.of(probe));
  }
}

事务性

public interface UserRepository extends CrudRepository<User, Long> {

  @Override
  @Transactional(timeout = 10)
  public List<User> findAll();

  // Further query method declarations
}

@Transactional(readOnly = true)
public interface UserRepository extends JpaRepository<User, Long> {

  List<User> findByLastname(String lastname);

  @Modifying
  @Transactional
  @Query("delete from User u where u.active = false")
  void deleteInactiveUsers();
}

通常,您希望将readOnly标志设置为true,因为大多数查询方法只读取数据。与此相反,deleteInactiveUsers()使用@Modifying注释并覆盖事务配置。因此,该方法在readOnly标志设置为的情况下运行false。

Lock

interface UserRepository extends Repository<User, Long> {

  // Plain query method
  @Lock(LockModeType.READ)
  List<User> findByLastname(String lastname);
}

审计

CDI集成

在容器外使用,具体略。

参考

https://docs.spring.io/spring-data/jpa/docs/2.1.3.RELEASE/reference/html/

https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#spring-core