【问题标题】:Configuring Hibernate session with Spring AOP使用 Spring AOP 配置 Hibernate 会话
【发布时间】:2016-01-01 06:30:33
【问题描述】:

我有一个使用 Hibernate 4.3.8 作为 JPA 提供程序的 Spring Framework 4 应用程序。我想使用 Hibernate 过滤器,因此我需要启用它们。我想在应用程序中全局执行此操作,我正在尝试使用 Spring AOP 执行此操作。我的想法是,我可以编写一个方面,在每次创建/获取会话时启用过滤器,例如 thisthis 问题。

我已将 spring-aopaspectjweaver 依赖项添加到我的项目中(使用 Maven)。我添加了以下方面。

@Aspect
@Component
public class EnableHibernateFilters {
    @Pointcut("execution(* org.hibernate.SessionFactory.getCurrentSession(..))")
    protected void sessionBeingFetched() {

    }

    @AfterReturning(pointcut = "sessionBeingFetched()", returning = "object")
    public void enableFilters(JoinPoint joinPoint, Object object) {
        System.out.println("!!! Enabling filters !!!"); // Never printed

        Session session = (Session) object;
        session.enableFilter("myFilter");
    }
}

我的问题是上述建议 (enableFilters) 从未被调用过;既没有打印文本,也没有启用我的过滤器。我已经通过将切入点更改为我自己的类之一来验证检测到我的方面并且 AOP 在我的项目中有效。我也尝试将切入点更改为execution(* org.hibernate.SessionFactory.openSession(..)),但没有结果。

我怀疑这是我设置 Hibernate 的方式造成的,因为我没有明确配置 SessionFactory;相反,我设置了一个EntityManagerFactory。这是我的配置。

@Configuration
@EnableTransactionManagement
public class PersistenceConfig {
    @Bean
    public DataSource dataSource() throws NamingException {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/postgres"); // JNDI lookup
    }

    @Bean
    public EntityManagerFactory entityManagerFactory() throws SQLException, NamingException {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(false);
        vendorAdapter.setDatabase(Database.POSTGRESQL);
        vendorAdapter.setShowSql(true);
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPackagesToScan(...);
        factory.setDataSource(this.dataSource());
        factory.afterPropertiesSet();

        return factory.getObject();
    }

    @Bean
    public JpaTransactionManager transactionManager() throws SQLException, NamingException {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(this.entityManagerFactory());

        return transactionManager;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }
}

基本上我不确定在上述配置中使用哪个切入点。我试图弄乱LocalContainerEntityManagerFactoryBean.setLoadTimeWeaver(),但我想不通。我不知道我是否还需要配置它。

基本上,我的 AOP 设置适用于我自己的自定义类。我想问题可能是编织没有配置 Hibernate 或其他东西(我对这部分内容非常不熟悉),或者由于我的设置,会话不是通过 SessionFactory.getCurrentSession() 方法获得的。我试图通过将切入点更改为execution(* org.hibernate.Hibernate.isInitialized(..)) 并在我的代码中手动调用Hibernate.isInitialized(null) 来验证我的建议是否适用于Hibernate,但这也没有触发建议,所以这可能是问题所在。我尝试了this post to enable Hibernate weaving 中的建议,但我无法让它产生任何影响。

我也尝试将我的切入点设置为execution(* org.springframework.orm.hibernate4.SessionHolder.getSession(..))execution(* org.springframework.orm.jpa.vendor.HibernateJpaDialect.getSession(..)),但也没有任何运气。

所以,我不确定下一步该去哪里。如何从我的建议中获取 Hibernate 的 Session 对象,以便启用 Hibernate 过滤器?提前谢谢!

编辑: 以防万一,我的配置中确实存在@EnableAspectJAutoProxy

@Configuration
@ComponentScan(basePackages = { ... })
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
    // ...
}

【问题讨论】:

  • 也许@ComponentScan("org.hibernate") 有帮助?
  • @Ruben 不,这没什么区别。
  • 您正在拦截SessioNFactory,但使用的是普通的JPA(使用hibernate作为实现者)。这显然永远不会开始,您的配置中没有SessionFactory,因此您的方面将不匹配任何内容。改写EntityManagerFactory.getEntityManager 的切入点。
  • @M.Deinum 这是有道理的。我试过了,但无法让它工作。如果我定义了@EnableAspectJAutoProxy(proxyTargetClass = true),我会得到以下异常:java.lang.IllegalArgumentException: Cannot subclass final class。如果我删除注释,那么我的项目会编译,但切入点不起作用。我直接在LocalContainerEntityManagerFactoryBean 上尝试了各种切入点,但到目前为止没有运气。
  • 而你的AppConfigPersistenceConfig 是在同一个上下文中加载的? AOP 仅适用于在同一应用程序上下文中定义的 bean。

标签: java spring hibernate spring-aop


【解决方案1】:

也许这只是你通过使用org.hibernate.SessionFactory 接口作为执行参数来声明切入点的事实......

@Pointcut("execution(* org.hibernate.SessionFactory.getCurrentSession(..))")

正确的方法是将切入点的执行定义为该接口的实现,其表示法略有不同,请参见+ 符号

@Pointcut("execution(* org.hibernate.SessionFactory+.getCurrentSession(..))")

也是另一种表示法...

@Pointcut("within(org.hibernate.SessionFactory+) && execution(* getCurrentSession(..))")

你也应该看看aspectj-cheat-sheet

关于java.lang.IllegalArgumentException: Cannot subclass final class。 目标类是org.hibernate.SessionFactory 的具体实现,即org.hibernate.internal.SessionFactoryImpl,恰好是最终的public final class SessionFactoryImpl

根据documentationproxyTargetClass = true配置

指示是否要创建基于子类 (CGLIB) 的代理,而不是创建标准的基于 Java 接口的代理。

但是由于您尝试子类化的类是final,因此根据Java Language Specification 存在一些问题

如果最终类的名称出现在另一个类声明的扩展子句(第 8.1.4 节)中,则为编译时错误;这意味着最终类不能有任何子类。

【讨论】:

  • 感谢您的回答!不幸的是,我尝试了你提到的切入点,但它们对我不起作用。
  • 您是否尝试过使用LocalEntityManagerFactoryBean 而不是LocalContainerEntityManagerFactoryBean ?我不是这个领域的专家,但我知道它是第一个产生应用程序管理的 EntityManagerFactory 而不是由后者产生的容器管理的 EntityManagerFactory ......我们真的希望 Spring 来管理这个以便 aop工作:)
  • 现在我知道 aop 很酷,但你真的应该看看以下涉及会话管理的可能解决方案:org.springframework.orm.hibernate4.support.OpenSessionInterceptororg.hibernate.SessionEventListener
  • 或者更好的是,看看这篇使用 Hibernate 的 SPI spring-managed-event-listeners-with-jpa 的文章比比皆是。我敢打赌,这可能会为这个 aop 装置提供一个更体面的替代方案 :)
  • 我认为这也应该作为替代品出现OpenSessionInViewFilter
【解决方案2】:

你的方面类看起来不错。

在您的实体上添加 @Filter@FilterDef

  • @Filter: 将过滤器添加到实体或目标实体。
  • @FilterDef:定义过滤器定义名称和参数以在启用过滤器时设置值。

例子:

@Entity
@Table(name="myTable", schema="mySchema")
@FilterDef(name="myFilter", parameters=@ParamDef(name="myAttribute", type="integer"))
@Filter(name="myFilter", condition=":myAttribute <= attribute")
public class MyEntity implements Serializable {
    ...
    @Column(name="attribute")
    private Integer attribute;
    ...
}

在您的配置中,过滤器已启用,但没有参数。

测试示例:

session.enableFilter("myFilter").setParameter("myAttribute", Integer.valueOf(2));

当然,你可以在@FilterDef注解中设置任意数量的参数。

希望对你有所帮助。问候,安德烈。

【讨论】:

  • 我已经在我的实体中声明了过滤器,但问题是过滤器从未在会话中启用。这应该发生在我的方面,但过滤器从未启用,因为该代码未执行。我的过滤器不需要参数,所以我没有添加任何参数。
【解决方案3】:

正面临着确切的问题。 我能够通过使用 EntityManager 并使用其 unwrap 方法获取会话信息来解决它,而不是使用 sessionFactory 的 getCurrentSession 调用周围的切入点获取会话信息。

@PersistenceContext
private EntityManager entityManager;

@Around("myPointcut")
public Object enableGlobalFilter(ProceedingJoinPoint pjp) throws Throwable {
            Session session = entityManager.unwrap(Session.class);
            Filter filter = session.enableFilter("Published_Entity");
            filter.setParameter("is_publishedParam", true);
            Object obj  = pjp.proceed();
            session.disableFilter("Published_Entity");
            return obj;
 }

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-16
    • 1970-01-01
    相关资源
    最近更新 更多