Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configuring Hibernate session with Spring AOP

I have a Spring Framework 4 application that uses Hibernate 4.3.8 as the JPA provider. I want to use Hibernate filters, and therefore I need to enable them. I want to do this globally in the application, which I am trying to do with Spring AOP. The idea is that I can write an aspect that enables filters every time a session is created/fetched, like in this and this question.

I have added the spring-aop and aspectjweaver dependencies to my project (using Maven). I have added the following aspect.

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

My problem is that the above advice (enableFilters) is never invoked; neither the text is printed, nor is my filter enabled. I have verified that my aspect is detected and that AOP works in my project by changing the pointcut to one of my own classes. I have also tried to change the pointcut to execution(* org.hibernate.SessionFactory.openSession(..)), but with no result.

I suspect that this is caused by how I set up Hibernate, because I don't configure a SessionFactory explicitly; rather, I set up an EntityManagerFactory. Here is my configuration.

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

Basically I am not sure which pointcut to use with the above configuration. I have tried to mess around with LocalContainerEntityManagerFactoryBean.setLoadTimeWeaver(), but I couldn't figure it out. I don't know if I even need to configure that anyways.

Essentially, my AOP setup works for my own custom classes. I guess either the problem is that weaving is not configured with Hibernate or something (I am very unfamiliar with this part of it all) or that the session is not obtained through the SessionFactory.getCurrentSession() method due to my setup. I tried to verify that my advice even worked with Hibernate by changing my pointcut to execution(* org.hibernate.Hibernate.isInitialized(..)) and manually invoking Hibernate.isInitialized(null) in my code, but this did not trigger the advice either, so this might be the problem. I tried what was suggested in this post to enable Hibernate weaving, but I couldn't get it to make any difference.

I also tried to set my pointcut to execution(* org.springframework.orm.hibernate4.SessionHolder.getSession(..)) and execution(* org.springframework.orm.jpa.vendor.HibernateJpaDialect.getSession(..)), but also without any luck.

So, I am not sure where to go next. How can I get a hold of Hibernate's Session object from my advice such that I can enable Hibernate filters? Thank you in advance!

EDIT: Just in case, I do have @EnableAspectJAutoProxy present in my configuration:

@Configuration
@ComponentScan(basePackages = { ... })
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
    // ...
}
like image 557
ba0708 Avatar asked Oct 04 '15 14:10

ba0708


2 Answers

Maybe it's just the fact that you're declaring the pointcut by using the org.hibernate.SessionFactory interface as the execution argument...

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

The proper way is to define the pointcut's execution as implementations of that interface and the notation for that is just a bit different, see the + sign

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

Also an alternative notation...

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

You should also have a look at aspectj-cheat-sheet

Regarding java.lang.IllegalArgumentException: Cannot subclass final class. The class that is targeted is the concrete implementation of org.hibernate.SessionFactory namely org.hibernate.internal.SessionFactoryImpl which happens to be final, public final class SessionFactoryImpl.

A proxyTargetClass = true configuration according to the documentation

Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to standard Java interface-based proxies.

But since the class you're trying to subclass is final there's a bit of a problem according to the Java Language Specification

It is a compile-time error if the name of a final class appears in the extends clause (§8.1.4) of another class declaration; this implies that a final class cannot have any subclasses.

like image 53
Filip Avatar answered Sep 22 '22 01:09

Filip


Your aspect class seems good.

Add @Filter and @FilterDef on your entities :

  • @Filter: Adds filter to an entity or a target entity.
  • @FilterDef: Defines filter definition name and parameters to set values while enabling filter.

Example :

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

In your configuration, the filter is enabled, but has no parameters.

Example to test:

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

Of course, you can set as many parameters as you need in @FilterDef annotation.

Hoping it helps you. Regards, André.

like image 30
André Blaszczyk Avatar answered Sep 23 '22 01:09

André Blaszczyk