【问题标题】:Unable to enable hibernate filter in spring EntityManager using spring aop无法使用spring aop在spring EntityManager中启用休眠过滤器
【发布时间】:2019-08-26 00:58:00
【问题描述】:

我正在尝试通过 spring EntityManager 启用休眠过滤器,方法是切入使用自定义注释 @TenantAware 注释的服务实现方法,并将 @Around 建议添加到该方法。我想启用自定义过滤器,它在扩展BaseEntity 的所有实体上添加微分器where tenant_id = :tenantId。因此,我创建了自定义注释并在需要它的@Transactional 方法上使用它。它成功拦截了该方法,但是当我记录它们时变量值显示为空,也没有设置过滤器。

该项目是一个 spring-boot 2 应用程序,我正在使用 spring aop 来创建方面。我使用 Hibernate 5 作为 JPA 实现提供者。

SimpleJpaRepository.class 的加载时间编织是不可能的,因为它没有公开 noarg 构造函数。

这是我的TenantFilterAdvisor 课程。

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;

    }

}

服务接口和实现类的相关部分是

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));
        }
    }
}

仓库接口是

@Repository
@Transactional(readOnly = true)
public interface InventoryRepository extends BaseRepository<Inventory, Long> {  
}

BaseRepository 接口扩展了 JpaRepository。

而切面配置类是

@Configuration
@ComponentScan(basePackages = {"org.foo.bar.advisors"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AOPConfig {
}

最后,由其他类继承的相关 MappedSuperClass 的过滤器定义为

@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;

}

如果您需要详细信息,这里是自定义注释类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface TenantAware {
}

我需要在会话中启用休眠过滤器,并在后续连接点完成执行后禁用它。但事实并非如此。我错过了什么?

【问题讨论】:

  • 当然他们不是记录器。您的日志消息中缺少 {} 占位符。
  • 谢谢,我想我明白了。但是实体管理器上没有启用过滤器呢?
  • 你是如何获取数据的。你只显示服务而不是使用实体管理器或存储库的实际实现。
  • 我有一个扩展 spring JpaRepository 接口的存储库接口。我依靠 spring SimpleJpaRepository 来提供实现。否则,我将不得不实现我自己的存储库实现,这超出了目的。如果需要,我可以在此处发布存储库界面,但正如我所说,它是一个空界面。
  • findById 调用 entityManager.find 并且过滤器在这种情况下不起作用。它们仅在查询时起作用。

标签: spring-boot spring-data-jpa aop spring-aop hibernate-filters


【解决方案1】:

正如Hibernate Reference Guide 中所述,过滤器仅适用于实体查询,不适用于直接获取。在您的代码中,您正在通过findById 进行直接提取,它转换为entityManager.find,因此是直接提取。

您可以覆盖 Spring JPA 存储库并将 findById 重新实现为实体查询而不是直接获取,以解决此问题。

【讨论】:

  • 感谢您指出这一点!我希望有一种简单的方法来做如此微不足道的事情。现在我想我必须为所有方法提供自己的实现才能应用该过滤器。
【解决方案2】:

没有 AOP 的另一种(并且被证明有效)方法是使用 TransactionManagerCustomizers

@Configuration
public class HibernateFilterConfig {
    @Bean
    @ConditionalOnMissingBean
    public PlatformTransactionManager transactionManager(
            ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
        JpaTransactionManager transactionManager = new JpaTransactionManager() {
            @Override
            @NonNull
            protected EntityManager createEntityManagerForTransaction() {
                final EntityManager entityManager = super.createEntityManagerForTransaction();
                Session session = entityManager.unwrap(Session.class);
                session.enableFilter("tenantFilter").setParameter("tenantId", new Long(10));
                return entityManager;
            }
        };
        transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));

        return transactionManager;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-19
    • 2015-12-01
    • 2014-11-16
    • 1970-01-01
    • 1970-01-01
    • 2011-03-14
    • 1970-01-01
    相关资源
    最近更新 更多