在 spring 数据 jpa 存储库方法查询上附加自定义条件
Appending custom conditions on spring data jpa repository method queries
精简版
我正在寻找一种方法来为存储库的所有 findBy 方法添加特定条件class
完整版
假设我有一个产品实体和一个客户实体。它们都扩展了 OwnerAwareEntity 并继承了 ownerRef 字段,该字段标识实体的所有者(可以是商家或合作伙伴)。我想在运行时修改 Product 和 Customer 的 findBy 方法,以便为它们附加 ownerRef 的附加条件。可以从用户会话中识别 ownerRef 值。
示例
提供公共 ownerRef 字段的父实体 class
public class OwnerAwareEntity implements Serializable {
private String ownerRef;
}
扩展 OwnerAwareEntity 的客户实体
public class Customer extends OwnerAwareEntity {
private String firstname;
private String mobile ;
}
扩展 OwnerAwareEntity 的产品实体
public class Product extends OwnerAwareEntity {
private String code;
private String name;
}
用于扩展 OwnerAwareRepository 的产品和客户的存储库 class
public interface OwnerAwareRepository extends JpaRepository {
}
public interface ProductRepository extends OwnerAwareRepository {
Product findByCode(String code );
}
public interface CustomerRepository extends OwnerAwareRepository {
Customer findByFirstname(String firstname );
}
这在执行时应该会产生如下查询
select P from Product P where P.code=?1 and P.ownerRef='aValue'
&
select C from Customer C where C.firstname=?1 and C.ownerRef='aValue'
我应该采用什么方法来实现此附加条件?。我只希望在父存储库为 OwnerAwareRepository 时发生此附加操作。
您可以在 Spring Data JPA 方法中使用 Predicate of QueryDSL (or Specification)。
示例:
interface UserRepository extends CrudRepository<User, Long>, QueryDslPredicateExecutor<User> {
}
Predicate predicate = QUser.user.firstname.equalsIgnoreCase("dave")
.and(user.lastname.startsWithIgnoreCase("mathews"));
userRepository.findAll(predicate);
要使用 QueryDSL 添加给你 pom.xml:
<dependencies>
//..
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>4.1.4</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>4.1.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
然后编译您的项目,您将获得 Q-类 个实体。
更多信息是 here.
TL;DR: 我使用了 Hibernate 的 @Filter 然后创建了一个 Aspect 来拦截方法
定义了具有以下结构的基础 class 实体
OwnerAwareEntity.java
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.ParamDef;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import java.io.Serializable;
@MappedSuperclass
@FilterDef(name = "ownerFilter", parameters = {@ParamDef(name = "ownerRef", type = "long")})
@Filter(name = "ownerFilter", condition = "OWNER_REF = :ownerRef")
public class OwnerAwareEntity implements Serializable{
@Column(name = "OWNER_REF",nullable = true)
private Long ownerRef;
}
我们在此实体上设置了过滤器。 hibernate @Filter 允许我们设置要附加到 select where 子句的条件。
接下来,为 OwnerAwareEntity 类型的实体定义了一个基础存储库
OwnerAwareRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;
@NoRepositoryBean
public interface OwnerAwareRepository<T, ID extends java.io.Serializable> extends JpaRepository<T, ID> {
}
创建了一个方面,它将拦截存储库中扩展 OwnerAwareRepository 的所有方法
OwnerFilterAdvisor.java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.hibernate.Filter;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Aspect
@Component
@Slf4j
public class OwnerFilterAdvisor {
@PersistenceContext
private EntityManager entityManager;
@Pointcut("execution(public * com.xyz.app.repository.OwnerAwareRepository+.*(..))")
protected void ownerAwareRepositoryMethod(){
}
@Around(value = "ownerAwareRepositoryMethod()")
public Object enableOwnerFilter(ProceedingJoinPoint joinPoint) throws Throwable{
// Variable holding the session
Session session = null;
try {
// Get the Session from the entityManager in current persistence context
session = entityManager.unwrap(Session.class);
// Enable the filter
Filter filter = session.enableFilter("ownerFilter");
// Set the parameter from the session
filter.setParameter("ownerRef", getSessionOwnerRef());
} catch (Exception ex) {
// Log the error
log.error("Error enabling ownerFilter : Reason -" +ex.getMessage());
}
// Proceed with the joint point
Object obj = joinPoint.proceed();
// If session was available
if ( session != null ) {
// Disable the filter
session.disableFilter("ownerFilter");
}
// Return
return obj;
}
private Long getSessionOwnerRef() {
// Logic to return the ownerRef from current session
}
}
顾问程序设置为拦截 classes 中扩展 OwnerAwareRepository 的所有方法。在拦截时,当前的休眠会话是从 entityManager(当前持久性上下文的)获得的,并且使用 "ownerRef" 的参数值启用过滤器。
还创建了一个配置文件来扫描顾问
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = {"com.xyz.app.advisors"})
public class AOPConfig {
}
这些文件就位后,您需要为需要所有者感知的实体完成以下操作
- 该实体需要扩展 OwnerAwareEntity
- 实体存储库class需要扩展OwnerAwareRepository
依赖关系
此设置要求 spring aop 在依赖项中。您可以将以下内容添加到 pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
优势
- 适用于所有 select 查询(findBy 方法、findAll 等)
- @查询方法也被拦截
- 实现简单
注意事项
- 删除或更新的where子句不受
影响
这个过滤器。
- 如果存储库包含 save/update/delete 方法并且如果
方法没有被标记为@Transactional,那么拦截器将给出
错误(您可以在这些
中捕获并让方法正常进行
例)
精简版
我正在寻找一种方法来为存储库的所有 findBy 方法添加特定条件class
完整版
假设我有一个产品实体和一个客户实体。它们都扩展了 OwnerAwareEntity 并继承了 ownerRef 字段,该字段标识实体的所有者(可以是商家或合作伙伴)。我想在运行时修改 Product 和 Customer 的 findBy 方法,以便为它们附加 ownerRef 的附加条件。可以从用户会话中识别 ownerRef 值。
示例
提供公共 ownerRef 字段的父实体 class
public class OwnerAwareEntity implements Serializable {
private String ownerRef;
}
扩展 OwnerAwareEntity 的客户实体
public class Customer extends OwnerAwareEntity {
private String firstname;
private String mobile ;
}
扩展 OwnerAwareEntity 的产品实体
public class Product extends OwnerAwareEntity {
private String code;
private String name;
}
用于扩展 OwnerAwareRepository 的产品和客户的存储库 class
public interface OwnerAwareRepository extends JpaRepository {
}
public interface ProductRepository extends OwnerAwareRepository {
Product findByCode(String code );
}
public interface CustomerRepository extends OwnerAwareRepository {
Customer findByFirstname(String firstname );
}
这在执行时应该会产生如下查询
select P from Product P where P.code=?1 and P.ownerRef='aValue'
&
select C from Customer C where C.firstname=?1 and C.ownerRef='aValue'
我应该采用什么方法来实现此附加条件?。我只希望在父存储库为 OwnerAwareRepository 时发生此附加操作。
您可以在 Spring Data JPA 方法中使用 Predicate of QueryDSL (or Specification)。
示例:
interface UserRepository extends CrudRepository<User, Long>, QueryDslPredicateExecutor<User> {
}
Predicate predicate = QUser.user.firstname.equalsIgnoreCase("dave")
.and(user.lastname.startsWithIgnoreCase("mathews"));
userRepository.findAll(predicate);
要使用 QueryDSL 添加给你 pom.xml:
<dependencies>
//..
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>4.1.4</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>4.1.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
然后编译您的项目,您将获得 Q-类 个实体。 更多信息是 here.
TL;DR: 我使用了 Hibernate 的 @Filter 然后创建了一个 Aspect 来拦截方法
定义了具有以下结构的基础 class 实体
OwnerAwareEntity.java
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.ParamDef;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import java.io.Serializable;
@MappedSuperclass
@FilterDef(name = "ownerFilter", parameters = {@ParamDef(name = "ownerRef", type = "long")})
@Filter(name = "ownerFilter", condition = "OWNER_REF = :ownerRef")
public class OwnerAwareEntity implements Serializable{
@Column(name = "OWNER_REF",nullable = true)
private Long ownerRef;
}
我们在此实体上设置了过滤器。 hibernate @Filter 允许我们设置要附加到 select where 子句的条件。
接下来,为 OwnerAwareEntity 类型的实体定义了一个基础存储库
OwnerAwareRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;
@NoRepositoryBean
public interface OwnerAwareRepository<T, ID extends java.io.Serializable> extends JpaRepository<T, ID> {
}
创建了一个方面,它将拦截存储库中扩展 OwnerAwareRepository 的所有方法
OwnerFilterAdvisor.java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.hibernate.Filter;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Aspect
@Component
@Slf4j
public class OwnerFilterAdvisor {
@PersistenceContext
private EntityManager entityManager;
@Pointcut("execution(public * com.xyz.app.repository.OwnerAwareRepository+.*(..))")
protected void ownerAwareRepositoryMethod(){
}
@Around(value = "ownerAwareRepositoryMethod()")
public Object enableOwnerFilter(ProceedingJoinPoint joinPoint) throws Throwable{
// Variable holding the session
Session session = null;
try {
// Get the Session from the entityManager in current persistence context
session = entityManager.unwrap(Session.class);
// Enable the filter
Filter filter = session.enableFilter("ownerFilter");
// Set the parameter from the session
filter.setParameter("ownerRef", getSessionOwnerRef());
} catch (Exception ex) {
// Log the error
log.error("Error enabling ownerFilter : Reason -" +ex.getMessage());
}
// Proceed with the joint point
Object obj = joinPoint.proceed();
// If session was available
if ( session != null ) {
// Disable the filter
session.disableFilter("ownerFilter");
}
// Return
return obj;
}
private Long getSessionOwnerRef() {
// Logic to return the ownerRef from current session
}
}
顾问程序设置为拦截 classes 中扩展 OwnerAwareRepository 的所有方法。在拦截时,当前的休眠会话是从 entityManager(当前持久性上下文的)获得的,并且使用 "ownerRef" 的参数值启用过滤器。
还创建了一个配置文件来扫描顾问
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = {"com.xyz.app.advisors"})
public class AOPConfig {
}
这些文件就位后,您需要为需要所有者感知的实体完成以下操作
- 该实体需要扩展 OwnerAwareEntity
- 实体存储库class需要扩展OwnerAwareRepository
依赖关系
此设置要求 spring aop 在依赖项中。您可以将以下内容添加到 pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
优势
- 适用于所有 select 查询(findBy 方法、findAll 等)
- @查询方法也被拦截
- 实现简单
注意事项
- 删除或更新的where子句不受
影响 这个过滤器。 - 如果存储库包含 save/update/delete 方法并且如果
方法没有被标记为@Transactional,那么拦截器将给出
错误(您可以在这些
中捕获并让方法正常进行 例)