Short Version
I am looking for a way to have all the findBy methods of a repository class appended with a particular condition
Full Version
Let's assume I have a Product entity and Customer entity. Both of them extends the OwnerAwareEntity and they inherit ownerRef field which identifies the owner of the entity( It could be a merchant or a partner ). I want to have the findBy methods of the Product and Customer modified in runtime such that they are appended with an additional condition of the ownerRef. The ownerRef value could be identified from the user session.
Example
The parent entity class that provides the common ownerRef field
public class OwnerAwareEntity implements Serializable {
private String ownerRef;
}
Customer entity extending OwnerAwareEntity
public class Customer extends OwnerAwareEntity {
private String firstname;
private String mobile ;
}
Product entity extending OwnerAwareEntity
public class Product extends OwnerAwareEntity {
private String code;
private String name;
}
Repository class for Product & Customer extending an OwnerAwareRepository
public interface OwnerAwareRepository extends JpaRepository {
}
public interface ProductRepository extends OwnerAwareRepository {
Product findByCode(String code );
}
public interface CustomerRepository extends OwnerAwareRepository {
Customer findByFirstname(String firstname );
}
This, when executed, should result in a query like below
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'
What should be my approach to have this appending of condition achieved?. I only want this appending to be happening when the parent repository is OwnerAwareRepository.
TL;DR: I used @Filter of Hibernate and then created an Aspect to intercept the methods
Defined a base class entity with the following structure
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;
}
We set the filter on this entity. The hibernate @Filter allows us to set a condition to be appended to the select where clause.
Next, defined a base repository for the entity of type 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> {
}
Created an Aspect that will intercept all the methods from the repositories that extend 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
}
}
The advisor is set to intercept all the methods from classes that extends the OwnerAwareRepository. On the interception, the current hibernate Session is obtained from entityManager ( of current persistence context ) and the filter is enabled with the param value of "ownerRef".
Also have a configuration file created to have the advisor scanned
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = {"com.xyz.app.advisors"})
public class AOPConfig {
}
Once these files are in place, you need to have the following things done for the entities that need to be owner aware
Dependencies
This setup requires the spring aop to be in the dependencies. You may add the following to the pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Advantages
Caveats
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With