简洁版本
我正在寻找一种将存储库类的所有findBy方法附加特定条件的方法
完整版
假设我有一个Product实体和一个Customer实体。它们都扩展了OwnerAwareEntity,并且它们都继承了ownerRef字段,该字段标识实体的所有者(它可以是商人或合作伙伴)。我想在运行时修改Product和Customer的findBy方法,以便将它们附加有ownerRef的附加条件。可以从用户会话中识别ownerRef值。
例
提供公共ownerRef字段的父实体类
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
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时进行此追加。
TL; DR: 我使用了Hibernate的@Filter,然后创建了一个Aspect来拦截方法
定义具有以下结构的基类实体
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> { }
创建了一个Aspect将拦截扩展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 } }
顾问程序设置为拦截扩展OwnerAwareRepository的类中的所有方法。在侦听时,从(当前持久性上下文的)entityManager获取当前的hibernate会话,并使用参数值“ ownerRef”启用过滤器。
还创建一个配置文件以扫描顾问程序
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages = {"com.xyz.app.advisors"}) public class AOPConfig { }
这些文件放置到位后,您需要对需要了解所有者的实体执行以下操作
依存关系
此设置要求spring aop处于依赖关系中。您可以将以下内容添加到pom.xml中
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
优点
注意事项
删除或更新的where子句不受 此过滤器的影响。
如果存储库包含一个save / update / delete方法,并且该 方法未标记为@Transactional,则拦截器将给出 错误信息(在这种 情况下,您可以捕获并让方法正常进行)