Spring Data JPA使用手册
Spring Data JPA是什么?
Spring Data JPA 为 Jakarta Persistence API (JPA) 提供存储库支持。它简化了需要访问 JPA 数据源的应用程序的开发。
- 在原生Hibernate中,操作数据库的对象叫Session。
- 在JPA中,操作数据库的对象叫做EntityManager。
- 在Mybatis中,操作数据库的对象叫SqlSession。
定义Repository接口
Repository类
Spring Data JPA提供了接口Repository,框架会对继承这个接口的接口,实现强大CRUD功能。
还提供了CrudRepository、ListCrudRepository、PagingAndSortingRepository等接口,这些接口继承了Repository,提供特定的功能。
SimpleJpaRepositoryCrudRepositoryListCrudRepositoryReactiveCrudRepository:reactiveRxJava3CrudRepository:reactiveCoroutineCrudRepository:KotlinPagingAndSortingRepositoryReactiveSortingRepositoryRxJava3SortingRepositoryCoroutineSortingRepositoryJpaRepository
启用包扫描注解
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa"):指定JPA资源类的扫描包路径@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo"):指定Mongo资源类的扫描包路径
使用Filter属性和@Filter,可以过滤不想被Spring扫描到的Repository接口。
@Configuration
@EnableJpaRepositories(basePackages = "com.acme.repositories",
includeFilters = { @Filter(type = FilterType.REGEX, pattern = ".*SomeRepository") },
excludeFilters = { @Filter(type = FilterType.REGEX, pattern = ".*SomeOtherRepository") })
class ApplicationConfiguration {
@Bean
EntityManagerFactory entityManagerFactory() {
// …
}
}JpaRepository的常用方法
find:查找save:插入或更新save:调用后保存到持久化上下文,直到调用flush才保存到数据库。saveAndFlush:调用后保存到持久化上下文,并立即调用flush保存到数据库。
delete:删除count:数量exist:是否存在
@RepositoryDefinition 自定义的Repository需要使用@NoRepositoryBean注解
属性表达式
_:使用_符号表示属性的层级。
List<Person> findByAddress_ZipCode(ZipCode zipCode);滚动查询
Window<T>WindowIterator
查询返回值
- Iterable
- List
- Set
- Streamable
- Stream
空值处理
@NonNullApi:注解到包,声明方法参数或返回值不接受空值也不产生空值。@NonNull:注解到方法参数或方法上,声明方法参数或返回值不接受空值也不产生空值。@Nullable:注解到方法参数或方法上,声明方法参数或返回值可以为空。
异步查询结果
@Async:注解方法Future<User>:作为方法返回值CompletableFuture<User>:作为方法返回值
自定义Repository
SimpleJpaRepository是一个基础的Jpa实现类,提供了常用的CRUD接口。
默认情况下,我们只需要定义一个继承Repositories接口的接口,Spring-data-jpa会自动为我们生成代理实现类,此时实现类继承自SimpleJpaRepository类。
public interface SysUserRepository extends JpaRepository<SysUser, Long> {}假如我们想要在Repositories添加自定义方法,因为定义的是接口我们无法直接添加方法。如果既然有自定义方法也要有CRUD方法,我们就需要手动实现JpaRepository下所有方法。
Spring-data-jpa针对这种情况,提供了一种方式去实现,通过接口的多继承。
我们只需要定义一个固定后缀名称的实现类,Spring-data-jpa会自动发现这个类,并通过cglib生成代理实现类。此时的代理实现类继承了SimpleJpaRepository和自定义实现类,同时拥有了CRUD方法和自定义的方法。
步骤
- 自定义一个接口
CustomerRepository。
public interface CustomerRepository {
void printString(String str);
}- 自定义一个
CustomerRepository接口的实现类CustomerRepositoryImpl。
public class CustomerRepositoryImpl implements CustomerRepository {
@Override
public void printString(String str) {
System.out.println(str);
}
}当实现类有两个且类名相同、包名不同时,会有冲突,spring-data-jpa无法识别出使用哪个实现类。这时可以使用@Component("otherNameRepositoryImpl")指定其中一个实现类的bean name不为接口名+Impl。Spring-data-jpa就可以正常加载实现类了。
@Component("otherNameRepositoryImpl")
public class CustomerRepositoryImpl implements CustomerRepository {
@Override
public void printString(String str) {
System.out.println("two" + str);
}
}- 在声明的
Repository接口中继承自定义的CustomerRepository接口。
public interface SysUserRepository extends JpaRepository<SysUser, Long>, CustomerRepository {
}- 在
@EnableJpaRepositories(repositoryImplementationPostfix = "Impl")配置资源实现类的后缀,默认为Impl。 - 此时即可通过
SysUserRepository对象使用CustomerRepositoryImpl实现类中的方法。
@Test
void customerRepositoryPrintStringTest() {
sysUserRepository.printString("hello world");
}自定义Base Repositories
如要自定义一个基础Repostirories类可以继承SimpleJpaRepository类,并使用注解@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)配置这个基础Repostirories类。
class MyRepositoryImpl<T, ID>
extends SimpleJpaRepository<T, ID> { … }@Configuration
@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }自定义主键生成器
- 实现IdentifierGenerator接口的generate方法,自定义主键生成器。
public class SnowflakeIdGenerator implements IdentifierGenerator {
private AtomicLong id = new AtomicLong(100l);
@Override
public Long generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
return id.getAndIncrement();
}
}- 在实体类的id字段上添加
@GeneratedValue和@GenericGenerator注解。@GenericGenerator声明主键生成器。@GeneratedValue声明主键生成策略,引入主键生成器。- jpa会通过反射创建一个
SnowflakeIdGenerator的实例,并在整个应用程序的生命周期内重复使用这个实例来生成ID。
@Entity
@Table(name = "sys_user")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SysUser {
@Id
@GeneratedValue(generator = "snowflakeIdGenerator")
@GenericGenerator(name = "snowflakeIdGenerator", strategy = "com.chenzhuowen.springdatajpaspringbootstudy.generator.SnowflakeIdGenerator")
private Long id;
}- 在插入数据时,无需指定id属性值,jpa通过主键生成器获取主键值。
@Test
void insertAndSelectTest() {
SysUser chenzhuowen = SysUser.builder().name("chenzhuowen").age(28).email("abc@qq.com").gender(GenderEnum.MAN).build();
SysUser sysUser1 = sysUserService.insertAndSelect(chenzhuowen);
SysUser fuyue = SysUser.builder().name("fuyue").age(25).email("abc@qq.com").gender(GenderEnum.WOMAN).build();
SysUser sysUser2 = sysUserService.insertAndSelect(fuyue);
System.out.println(sysUser1);
System.out.println(sysUser2);
}分页
Jpa提供了Pageable类用于实现分页。使用PageRequest的of静态方法,可以方便地创建Pageable对象并配置分页规则。调用分页查询后返回一个Page对象,对象里面保存了分页查询的结果。
@Test
public void findAllPageTest() {
Pageable pageable = PageRequest.of(1, 2);
Page<SysUser> sysUserPage = sysUserRepository.findAll(pageable);
List<SysUser> sysUserList = sysUserPage.toList();
sysUserList.forEach(System.out::println);
}注意事项:
PageRequest.of(int page,int size),其中page页码参数的起始值为0。Pageable对象不能为null,如果不想分页可以传入Pageable.unpaged()。
排序
Sort类
Jpa提供Sort类用于实现结果排序。Sort类的by静态方法,可以方便地创建Sort对象并设置排序规则。
@Test
public void findAllSortTest() {
Sort sort = Sort.by("name").ascending().and(Sort.by("email").descending());
//Sort.by(Sort.Direction.ASC, "name");
List<SysUser> sysUsers = sysUserRepository.findAll(sort);
sysUsers.forEach(System.out::println);
}
- 多字段排序可以使用链式函数
and()方法设置排序规则。Sort对象不能为null,如果不想分页可以传入Sort.unsorted()。
TypeSort类
Jpa也提供了类型排序类,可以以类型安全的方式定义排序表达式。
@Test
public void findAllTypeSortTest() {
Sort.TypedSort<SysUser> sysUserTypedSort = Sort.sort(SysUser.class);
Sort sort = sysUserTypedSort.by(SysUser::getName).ascending().and(sysUserTypedSort.by(SysUser::getEmail).ascending());
List<SysUser> sysUsers = sysUserRepository.findAll(sort);
sysUsers.forEach(System.out::println);
}分页同时排序
PageRequest类的of方法支持传入Sort对象,可实现分页并排序。
@Test
public void findAllPageAndSortTest() {
Pageable pageable = PageRequest.of(0, 2, Sort.by(Sort.Direction.ASC, "age"));
Page<SysUser> sysUserPage = sysUserRepository.findAll(pageable);
List<SysUser> sysUserList = sysUserPage.toList();
sysUserList.forEach(System.out::println);
if (sysUserPage.hasNext()) {
Pageable nextPageable = sysUserPage.nextPageable();
Page<SysUser> nextPageSysUsers = sysUserRepository.findAll(nextPageable);
List<SysUser> nextPageSysUserList = nextPageSysUsers.stream().toList();
nextPageSysUserList.forEach(System.out::println);
}
}Spring Data Web Support
使用注解@EnableSpringDataWebSupport开启web支持,注解会注册一系列的组件,包括:
- 使用
DomainClassConverter类 让Spring MVC从请求参数或路径变量中解析Repository管理的domain类实例。 HandlerMethodArgumentResolver的实现,让Spring MVC从请求参数中解析Pageable和Sort实例。Jackson模块 对Point和Distance等类型进行序列化/反序列化,或存储特定的类型,这取决于使用的Spring数据模块。
DomainClassConverter
DomainClassConverter 类可以通过请求参数或路径变量中解析Repository 管理的 domain 类并获取到对象。
使用
@RestController
@RequestMapping(path = "/sysuser")
public class UserController {
@GetMapping(path = "/{id}")
public SysUser findById(@PathVariable("id") SysUser sysUser) {
return sysUser;
}
}请求
GET http://localhost:8080/sysuser/1
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 22 Jul 2023 03:34:30 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"id": 1,
"name": "Jone",
"age": 18,
"gender": "WOMAN",
"email": "test1@baomidou.com",
"version": 0,
"flag": true
}
Response file saved.
> 2023-07-22T113430.200.json
Response code: 200; Time: 7ms (7 ms); Content length: 101 bytes (101 B)HandlerMethodArgumentResolver
HandlerMethodArgumentResolver注册的同时也注册了PageableHandlerMethodArgumentResolver实例和SortHandlerMethodArgumentResolver实例,它可以实现从spring MVC请求中解析Pageable和Sort对象。
使用
@RestController
@RequestMapping(path = "/sysuser")
public class UserController {
@Autowired
SysUserRepository sysUserRepository;
@GetMapping(path = "/page")
public List<SysUser> findById(Pageable pageable) {
Page<SysUser> sysUserPage = sysUserRepository.findAll(pageable);
return sysUserPage.stream().toList();
}
}请求
GET http://localhost:8080/sysuser/page?page=0&size=2&sort=version,desc&sort=name,asc
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 22 Jul 2023 04:20:42 GMT
Keep-Alive: timeout=60
Connection: keep-alive
[
{
"id": 5,
"name": "Billie",
"age": 24,
"gender": "WOMAN",
"email": "test5@baomidou.com",
"version": 0,
"flag": true
},
{
"id": 2,
"name": "Jack",
"age": 20,
"gender": "WOMAN",
"email": "test2@baomidou.com",
"version": 0,
"flag": true
}
]
Response file saved.
> 2023-07-22T122042.200.json
Response code: 200; Time: 10ms (10 ms); Content length: 207 bytes (207 B)存储过程
- 使用
@NamedStoredProcedureQuery注解在实体类上,定义一个存储过程。
@Entity
@NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class),
@StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) })
public class User {}- 使用
@Procedure注解在方法上,引入一个存储过程,调用方法时即调用存储过程。
@Procedure(procedureName = "plus1inout")
Integer callPlus1InOut(@Param("arg") Integer arg);事务
默认情况下,继承自CrudRepository方法都从SimpleJpaRepository继承事务配置。对于对操作,默认事务配置为只读。其他的方法均被注解@Transactional,所以都默认应用了事务配置。
也可以在调用多个Repository的方法添加@Transactional,这将覆盖方法下的所有默认事务配置,子方法都进入到同一个事务中。
动态查询Specification
spring-data-jpa提供了Specification接口和JpaSpecificationExecutor接口,用于实现动态生成where条件。
Root<T> root: 这是 JPA Criteria 查询的根对象。它代表了实体类的根节点,可以通过它获取实体类的属性路径(属性表达式)。通常用于指定查询条件的属性。CriteriaQuery<?> query: 这是 JPA Criteria 查询的顶层查询对象。它可以用于指定查询的排序、分组等操作,以及获取底层的查询构建器CriteriaBuilder。CriteriaBuilder builder: 这是 JPA Criteria 查询的构建器对象。它提供了用于构建查询条件的方法,如equal、greaterThan、like等。您可以通过这个构建器来创建查询的各种条件和约束。
Specification的使用
- 创建一个静态方法,方法中实现
Specification接口的toPredicate方法并返回Specification对象,动态传入多个Predicate对象。
public class SysUserSpecification {
public static Specification<SysUser> hasNameAndAge(String name, Integer age) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (!ObjectUtils.isEmpty(name)) {
Predicate namePredicate = criteriaBuilder.equal(root.get("name"), name);
predicates.add(namePredicate);
}
if (!ObjectUtils.isEmpty(age)) {
Predicate agePredicate = criteriaBuilder.equal(root.get("age"), age);
predicates.add(agePredicate);
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}- Repository类继承
JpaSpecificationExecutor接口,JpaSpecificationExecutor会提供一系列接受Specification为参数的方法。
public interface SysUserRepository extends JpaRepository<SysUser, Long>, CustomerRepository, JpaSpecificationExecutor<SysUser> {
}- 生成一个
Specification对象,调用JpaSpecificationExecutor接口提供的方法。
@Test
void findAllBySpecificationTest() {
List<SysUser> sysUsers = sysUserRepository.findAll(SysUserSpecification.hasNameAndAge("Jone",18));
sysUsers.forEach(System.out::println);
sysUsers = sysUserRepository.findAll(SysUserSpecification.hasNameAndAge("Jone", null));
sysUsers.forEach(System.out::println);
sysUsers = sysUserRepository.findAll(SysUserSpecification.hasNameAndAge(null, 18));
sysUsers.forEach(System.out::println);
sysUsers = sysUserRepository.findAll(SysUserSpecification.hasNameAndAge(null, null));
sysUsers.forEach(System.out::println);
}Example 查询
Example 查询(QBE)是一种用户友好的查询技术,接口简单。它允许动态查询创建,不要求你写包含字段名的查询。
Example查询API查四部分组成:
Probe:带有填充字段的domain对象的实例。ExampleMatcher:包含了如何匹配特定字段的细节,可以在多个实例中重复使用。Example:一个Example由probe和ExmapleMatcher组成,用于创建查询。FetchableFluentQuery:FetchableFluentQuery提供了一个fluentAPI,它允许进一步定制从Example衍生的查询。使用fluent API,可以实现对查询指定排序投影和结果处理。
QueryByExampleExecutor接口
QueryByExampleExecutor接口提供了一些接收Example对象的方法。
public interface QueryByExampleExecutor<T> {
<S extends T> S findOne(Example<S> example);
<S extends T> Iterable<S> findAll(Example<S> example);
// … more functionality omitted.
}- 要使用
Example查询,repository需要继承QueryByExampleExecutor接口
public interface SysUserRepository extends JpaRepository<SysUser, Long>, CustomerRepository, JpaSpecificationExecutor<SysUser>, QueryByExampleExecutor<SysUser> {
}Example的简单使用
下面的例子,创建了一个SysUser实体类,实体类中设置了name的属性为"Jone"。通过Example.of方法创建出一个Example对象传入到查询方法中。查询方法findAll使用name属性创建查询条件,查询出name等于Jone的数据。
@Test
void findAllByExampleTest() {
SysUser jone = SysUser.builder().name("Jone").build();
List<SysUser> sysUsers = sysUserRepository.findAll(Example.of(jone));
sysUsers.forEach(System.out::println);
}事务
默认情况下,从 SimpleJpaRepository 继承的 repository 实例上的 CRUD 方法是事务性的。对于读操作,事务配置的 readOnly 标志被设置为 true。所有其他的都被配置为普通的 @Transactional,所以默认的事务配置也适用。由事务性 repository 片段支持的 repository 方法继承了实际片段方法的事务性属性。
如果一个service方法内包含多个repository方法,可以service方法上添加@Transactional注解,并需要在config类上添加@EnableTransactionManagement注解。
对于springboot项目,springboot的自动装配机制已经为我们添加了
@EnableTransactionMangement注解,因此可以省略。
@Service
public class SysUserService {
@Autowired
SysUserRepository sysUserRepository;
@Transactional
public SysUser insertAndSelect(SysUser sysUser) {
SysUser saveSysUser = sysUserRepository.save(sysUser);
Optional<SysUser> sysUserOptional = sysUserRepository.findById(saveSysUser.getId());
return sysUserOptional.get();
}
} @Test
void insertAndSelectTest() {
SysUser chenzhuowen = SysUser.builder().name("chenzhuowen").age(28).email("abc@qq.com").gender(GenderEnum.MAN).build();
SysUser sysUser1 = sysUserService.insertAndSelect(chenzhuowen);
SysUser fuyue = SysUser.builder().name("fuyue").age(25).email("abc@qq.com").gender(GenderEnum.WOMAN).build();
SysUser sysUser2 = sysUserService.insertAndSelect(fuyue);
System.out.println(sysUser1);
System.out.println(sysUser2);
}锁@Lock
在Spring Data JPA中,@Lock注解是用于在数据库中设置并发锁的注解。它可以应用于查询方法,用于控制数据库并发访问,从而避免数据不一致或并发冲突的问题。
@Lock注解接受一个LockModeType类型的参数。LockModeType有以下取值:
READ:乐观读WRITE:乐观写,更新时会对version值进行检查并自增OPTIMISTIC:乐观读,同READ类型相似。OPTIMISTIC_FORCE_INCREMENT:乐观写,同WRITE类型相似。PESSIMISTIC_READ:悲观读PESSIMISTIC_WRITE:悲观写PESSIMISTIC_FORCE_INCREMENT:悲观写,更新时会对version值进行检查并自增NONE:无锁
审计
JPA提供了简单的方式实现审计功能。审计功能可以在数据发生更改时,记录数据的更改人和发生时间。
在springboot项目中,要开启审计功能需要做如下工作:
- 使用
@CreatedBy、@LastModifiedBy、@CreatedDate、@LastModifiedDate注解在类字段上。 - 使用
@EnableJpaAuditing注解在启动类上以开启审计功能。 - 使用
@EntityListeners注解在实体类上监听实体类的变更。 - 实现
AuditorAware接口的getCurrentAuditor方法,获取当前用户信息,用于审计记录。
使用步骤
- 定义实体类,使用
@CreatedBy、@LastModifiedBy、@CreatedDate、@LastModifiedDate注解在类上字段,使用@EntityListeners注解类。
@Entity
@Table(name = "sys_user")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class SysUser {
@Id
@GeneratedValue(generator = "snowflakeIdGenerator")
@GenericGenerator(name = "snowflakeIdGenerator", strategy = "com.chenzhuowen.springdatajpaspringbootstudy.common.SnowflakeIdGenerator")
private Long id;
private String name;
private Integer age;
private GenderEnum gender;
private String email;
@Version
private Integer version;
@Builder.Default
private Boolean flag = true;
@CreatedBy
private Long createdBy;
@CreatedDate
private LocalDateTime createdDate;
@LastModifiedBy
private Long LastModifiedBy;
@LastModifiedDate
private LocalDateTime LastModifiedDate;
}- 实现
AuditorAware接口,获取当前用户信息,并将类注入到容器中。
@Component
public class UserIdAuditorAware implements AuditorAware<Long> {
@Override
public Optional<Long> getCurrentAuditor() {
return Optional.of(100L);
}
}- 在启动类上,添加
@EnableJpaAuditing注解,开启审计功能。
@SpringBootApplication
@EnableJpaRepositories
@EnableJpaAuditing
public class SpringDataJPASpringBootStduyApplicaiton {
public static void main(String[] args) {
SpringApplication.run(SpringDataJPASpringBootStduyApplicaiton.class, args);
}
}如果不想使用注解的形式去开启审计功能,可以将实体类实现
Auditable接口。这个接口为所有审计字段暴露了setter方法。
通用审计配置
@EntityListeners(AuditingEntityListener.class)
多表联合查询
spring-data-jpa提供了@ManyToMany、@OneToOne、@OneToMany、@ManyToOne、@JoinColume等注解实现多表联合查询功能,这些注解需要注解在实体类的字段上。
示例
- 定义两个存在关联的实体类。
@Entity
@Table(name = "sys_user")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SysUser {
@Id
@GeneratedValue(generator = "snowflakeIdGenerator")
@GenericGenerator(name = "snowflakeIdGenerator", strategy = "com.chenzhuowen.springdatajpaspringbootstudy.common.SnowflakeIdGenerator")
private Long id;
private String name;
private Integer age;
private GenderEnum gender;
private String email;
@Version
private Integer version;
@Builder.Default
private Boolean flag = true;
@OneToOne
@JoinColumn(name = "sys_user_detail_id")
private SysUserDetail sysUserDetail;
}
@Entity
@Table(name = "sys_user_detail")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SysUserDetail {
@Id
@GeneratedValue(generator = "snowflakeIdGenerator")
@GenericGenerator(name = "snowflakeIdGenerator", strategy = "com.chenzhuowen.springdatajpaspringbootstudy.common.SnowflakeIdGenerator")
private Long id;
private String country;
}
@OneToOne注解的FetchType属性,可以自定义关联实体数据的获取策略,有两个值:
LAZY:懒加载EAGER:急切加载,默认值。
- 对SysUser进行查询,会同时查询SysUserDetail。
@Test
public void findAllTest() {
List<SysUser> sysUsers = sysUserRepository.findAll();
sysUsers.forEach(System.out::println);
}打印sysUser对象时会同时打印sysUserDetail对象。
Spring Boot集成JPA
配置
spring.jpa.hibernate.ddl-auto
update:服务启动时判断与数据表的差异,对数据表进行更新。create:每次启动服务都会删除数据表及数据,重新生成。create-drop:当服务关闭时,删除数据表及数据,默认值。validate:启动时,验证数据库与实体类是否匹配,若不匹配则启动报错。none:关闭自动更新
Repository中自定义方法
- 查询:
findXXBy、readXXBy、queryXXBy、getXXBy(这几个等价) - 数量:
countXXBy - 存在:
existsXXBy - 删除:
deleteXXBy、removeXXBy(这几个等价)
语法大全
| 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 | 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 (parameter bound with appended %) |
| EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
| Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
| OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
| Not | findByLastnameNot | … where x.lastname <> ?1 |
| In | findByAgeIn(Collection ages) | … where x.age in ?1 |
| NotIn | findByAgeNotIn(Collection 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) |
常用注解
@EnableJpaRepositories:启用JpaRepository功能及包扫描功能。@Entity:注解在类上,标识类为实体类,与数据库绑定。@Table:注解在类上,name属性指定绑定的表名,默认是驼峰命名。@Id:注解在类字段上,表示字段对应为数据库id。@GeneratedValue():注解在类字段上,配置主键生成策略。TABLE:使用数据库中的一张特定的表来生成主键。这种策略通常会在数据库中创建一张存储主键值的表,并在表中维护一个计数器。SEQUENCE:使用数据库的序列生成策略,要求数据库支持序列。需要指定一个序列名和序列的初始值、分配大小等参数。IDENTITY:使用数据库的自增主键生成策略,要求数据库支持自增主键。UUID:生成UUID作为主键AUTO:默认策略,由持久化提供程序自动选择合适的生成策略,通常为数据库自增主键(如MySQL的AUTO_INCREMENT)或序列(如Oracle的SEQUENCE)
@Version:注解类字段,声明字段为乐观锁字段。@PrePersist:注解在实体类的方法上,用于在实体类持久化前执行一些执行一些动作。@Query:注解在Repository接口的方法上,传入自定义的sql,调用方法时执行自定义的sql。@Transactional:注解在方法上,使调用方法时进行事务控制。@Modifying:注解用于标记一个修改查询(Update 或 Delete)的方法,表明该方法会对数据库进行修改操作。它通常与 @Query 注解一起使用。@NamedStoredProcedureQuery:注解在类上,定义存储过程@Procedure:注解在方法上,引入存储过程,作为存储过程调用的入口。@QueryHint:注解在方法上,用于提供对查询的提示信息,以影响查询的执行方式和性能优化。它可以在查询方法上使用,以向JPA提供关于查询执行的一些建议。@Meta:注解在方法上,注解下有一个comment属性。通过给资源操作方法添加该注解,可以通过comment方便地定位和分析sql。
@Query
注解到方法上,自定义sql,调用方法则执行自定义sql。
传入参数的两种形式
一. 参数使用问号+数字形式绑定?1。
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);
}二. 使用@Param绑定sql参数与方法参数
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);
}参数nativeQuery
- 参数默认为false,当为false时,表示sql为非原生sql,sql中的表名表示项目中对应的实体类。
- 参数为true时,表示sql为原生sql,原生sql指赋值参数后可以直接到数据库运行的sql。
@Query(value = "select * from sys_user where age >= :age", nativeQuery = true)
List<SysUser> selectSysUserByAgeGreaterThanEqual(@Param("age") Integer age);参数countQuery
参数countQuery用于配置分页总页数的查询sql。如果需要分页,可以配置这个参数。如果没有配置,则jpa根据原始sql自动派生出分页sql。
@Query(value = "select * from sys_user where age >= :age", nativeQuery = true, countQuery = "select count(1) from sys_user where age >= :age")
Page<SysUser> selectSysUserByAgeGreaterThanEqualPage(@Param("age") Integer age, Pageable pageable)@Modifying
@Modifying用于定义更新/删除方法,需要使用搭配@Transactional使用。
@Modifying
@Transactional
@Query(nativeQuery = true, value = "update sys_user set age = :age where id = :id")
void updateAgeById(@Param("id") Long id, @Param("age") Integer age);