什么的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
来源:https://www.cnblogs.com/aoyihuashao/p/10193799.html