I'm trying to enable a hibernate filter through spring EntityManager by tyring to pointcut a service implementation method annotated with custom annotation @TenantAware and add @Around advise to that method. I want to enable custom filter which adds a differentiator where tenant_id = :tenantId on all entities that extend a BaseEntity. Hence I created the custom annotation and using it on @Transactional methods where it is required. It is intercepting the method successfully but the variable values when I log them are showing up empty and neither is the filter being set.
The project is a spring-boot 2 application and I'm using spring aop for creating the aspect. I'm using Hibernate 5 as the JPA implementation provider.
Load time weaving of the SimpleJpaRepository.class is not possible since it does not expose a noarg constructor.
This is my TenantFilterAdvisor class.
package org.foo.bar.advisors;
@Aspect
@Slf4j
@Component
public class TenantFilterAdvisor {
@PersistenceContext
private EntityManager entityManager;
public TenantFilterAdvisor() {
log.debug("###########################################################################");
log.debug("###################### Tenant Advisor Filter Started ######################");
log.debug("###########################################################################");
}
@Pointcut(value = "@annotation(org.foo.bar.TenantAware)")
public void methodAnnotatedWithTenantAware() {
}
@Pointcut(value = "execution(public * * (..))")
public void allPublicMethods() {
}
@Around(value = "methodAnnotatedWithTenantAware() && allPublicMethods()")
public Object enableTenantFilter(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
log.debug("###########################################################################");
log.debug("###################### Before enabling tenant filter ######################");
log.debug("###########################################################################");
if (null != entityManager) {
log.debug("Tenant filter name: ", "tenantFilter");
log.debug("Tenant filter property: ", "tenantId");
log.debug("Setting tenant id to: ", new Long(10));
Session session = entityManager.unwrap(Session.class);
Filter filter = session.enableFilter("tenantFilter");
filter.setParameter("tenantId", new Long(10));
}
Object result = proceedingJoinPoint.proceed();
// Code to disable the hibernate filter goes here.
log.debug("###########################################################################");
log.debug("###################### After disabling tenant filter ######################");
log.debug("###########################################################################");
return result;
}
}
The relevant part of service interface and implementation class is
public interface InventoryService {
Inventory getInventoryById(Long id);
}
@Service
public class InventoryServiceImpl implements InventoryService {
@Autowired
private InventoryRepository repo;
@Override
@Transactional
@TenantAware
public Inventory getInventoryById(Long id) {
LOG.debug("getInventoryById() called with: id = {}", id);
final Optional<Inventory> inventoryOp = repo.findById(id);
if (inventoryOp.isPresent()) {
return inventoryOp.get();
} else {
throw new InventoryNotFoundException(String.format(MESSAGE_INVENTORY_NOT_FOUND_FOR_ID, id));
}
}
}
The repository interface is
@Repository
@Transactional(readOnly = true)
public interface InventoryRepository extends BaseRepository<Inventory, Long> {
}
The BaseRepository interface extends JpaRepository.
And the aspect configuration class is
@Configuration
@ComponentScan(basePackages = {"org.foo.bar.advisors"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AOPConfig {
}
And finally the relevant MappedSuperClass which is inherited by other classes has the filter defined as
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@MappedSuperclass
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@FilterDef(
name = "tenantFilter",
parameters = @ParamDef(name = "tenantId", type = "long")
)
@Filter(name = "tenantFilter", condition = "tenant_id = :tenantId")
public abstract class BaseTransactionalEntity extends BaseEntity {
@Column(name = "tenant_id", nullable = false)
private Long tenantId;
}
Here is the cutom annotation class if you need the detail
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface TenantAware {
}
I need the hibernate filter to be enabled in session and disabled after the proceeding join point completes the execution. But it is not so. What am I missing?
{}placeholder in your log message.JpaRepositoryinterface. I'm relying on springSimpleJpaRepositoryto provide the implementation. Otherwise I'll have to implement my own repository implementation ground up which beats the purpose. If needed I can post the repository interface here but it's an empty interface as I said.findByIdcallsentityManager.findand filters don't work in that case. They only work in case of queries.