使用Spring AOP配置Hibernate会话

时间:2015-10-04 14:15:36

标签: java spring hibernate spring-aop

我有一个Spring Framework 4应用程序,它使用Hibernate 4.3.8作为JPA提供程序。我想使用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 {
    // ...
}

3 个答案:

答案 0 :(得分: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

根据documentation

proxyTargetClass = true配置
  

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

但是,因为您尝试子类化的类是final,根据Java Language Specification

有一些问题
  

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

答案 1 :(得分:1)

你的方面看起来不错。

在您的实体上添加 @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注释中设置任意数量的参数。

希望它对你有所帮助。此致,André。

答案 2 :(得分:0)

面临着确切的问题。 我能够通过使用EntityManager解析它并使用其解包方法获取会话信息,而不是使用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;
 }

希望这会有所帮助。