什么的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