2015-10-04 14 views
6

Ho un'applicazione Spring Framework 4 che utilizza Hibernate 4.3.8 come provider JPA. Voglio usare i filtri Hibernate, e quindi ho bisogno di abilitarli. Voglio farlo a livello globale nell'applicazione, che sto cercando di fare con Spring AOP. L'idea è che posso scrivere un aspetto che abiliti i filtri ogni volta che una sessione viene creata/recuperata, come nella domanda this e this.Configurazione della sessione di sospensione con Spring AOP

Ho aggiunto le dipendenze spring-aop e aspectjweaver al mio progetto (utilizzando Maven). Ho aggiunto il seguente aspetto.

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

Il mio problema è che i consigli di cui sopra (enableFilters) non viene mai richiamato; né il testo è stampato, né il mio filtro è abilitato. Ho verificato che il mio aspetto viene rilevato e che AOP funziona nel mio progetto cambiando il pointcut in una delle mie classi. Ho anche provato a cambiare il pointcut a execution(* org.hibernate.SessionFactory.openSession(..)), ma senza risultato.

Ho il sospetto che ciò sia causato da come ho configurato Hibernate, perché non configuro esplicitamente uno SessionFactory; piuttosto, ho creato uno EntityManagerFactory. Ecco la mia configurazione.

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

Fondamentalmente non sono sicuro di quale collegamento utilizzare con la configurazione di cui sopra. Ho provato a scherzare con LocalContainerEntityManagerFactoryBean.setLoadTimeWeaver(), ma non riuscivo a capirlo. Non so se ho anche bisogno di configurarlo comunque.

In sostanza, il mio setup AOP funziona per le mie classi personalizzate. Suppongo che il problema sia che la tessitura non è configurata con Hibernate o qualcosa del genere (non ho molta familiarità con questa parte di tutto questo) o che la sessione non è ottenuta attraverso il metodo SessionFactory.getCurrentSession() a causa della mia configurazione. Ho provato a verificare che il mio consiglio abbia funzionato anche con Hibernate cambiando il mio pointcut su execution(* org.hibernate.Hibernate.isInitialized(..)) e invocando manualmente Hibernate.isInitialized(null) nel mio codice, ma questo non ha attivato neanche il consiglio, quindi questo potrebbe essere il problema. Ho provato ciò che è stato suggerito in this post to enable Hibernate weaving, ma non ho potuto farlo fare alcuna differenza.

Ho anche provato a impostare il mio collegamento a execution(* org.springframework.orm.hibernate4.SessionHolder.getSession(..)) e execution(* org.springframework.orm.jpa.vendor.HibernateJpaDialect.getSession(..)), ma anche senza fortuna.

Quindi, non sono sicuro di dove andare dopo. Come posso ottenere una sospensione dell'oggettodi Hibernate dal mio consiglio in modo da poter abilitare i filtri Hibernate? Grazie in anticipo!

EDIT: Solo nel caso, io ho @EnableAspectJAutoProxy presente nella mia configurazione:

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

Forse un '@ComponentScan (" org.hibernate ")' aiuta? – Ruben

+0

@Ruben No, non fa differenza. – Andy0708

+0

Si sta intercettando il 'SessioNFactory', ma si sta usando un semplice JPA (con hibernate come implementatore). Ovviamente questo non inizierà mai, non c'è 'SessionFactory' nella tua configurazione e quindi il tuo aspetto non combacia con niente. Scrivi invece il punto tagliato per "EntityManagerFactory.getEntityManager". –

risposta

1

forse è solo il fatto che si sta dichiarando la pointcut utilizzando l'interfaccia org.hibernate.SessionFactory come argomento di esecuzione ...

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

il modo corretto è quello di definire l'esecuzione del pointcut come implementatio ns di tale interfaccia e la notazione di ciò è solo un po 'diverso, vedere il segno

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

anche una notazione alternativa + ...

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

Si dovrebbe anche dare un'occhiata a aspectj-cheat-sheet

Per quanto riguarda java.lang.IllegalArgumentException: Cannot subclass final class. La classe a cui è destinato è l'implementazione concreta di org.hibernate.SessionFactory ovvero org.hibernate.internal.SessionFactoryImpl che risulta essere definitiva, public final class SessionFactoryImpl.

Una configurazione proxyTargetClass = true secondo l'documentation

Indicare se (CGLIB) proxy sottoclasse basata devono essere creati in contrasto proxy interfaccia basata su Java standard.

Ma dal momento che la classe si sta cercando di sottoclasse è final c'è un po 'di un problema secondo il Java Language Specification

Si tratta di un errore di compilazione se il nome di una classe finale appare in la clausola extends (§8.1.4) di un'altra dichiarazione di classe; questo implica che una classe finale non può avere sottoclassi.

+0

Grazie per la risposta! Sfortunatamente, ho provato i punti di riferimento che hai menzionato, ma non hanno funzionato per me. – Andy0708

+0

Hai provato a utilizzare 'LocalEntityManagerFactoryBean' invece di' LocalContainerEntityManagerFactoryBean '? Non sono esperto in questo campo, ma so che è il primo a produrre una EntityManagerFactory gestita dall'applicazione e non una EntityManagerFactory gestita dal container che è prodotta da quest'ultimo ... E vogliamo davvero che Spring gestisca questo in modo tale che a funziona :) – Filip

+0

Ora capisco che aop è bello e tutto questo, ma dovresti davvero dare un'occhiata alle seguenti possibili soluzioni che coinvolgono anche la gestione delle sessioni: [org.springframework.orm.hibernate4.support.OpenSessionInterceptor] (http: // docs.spring.io/spring/docs/current/javadoc-api/org/springframework/orm/hibernate4/support/OpenSessionInterceptor.html) o [org.hibernate.SessionEventListener] (https://docs.jboss.org/hibernate /orm/4.3/javadocs/org/hibernate/SessionEventListener.html) – Filip

1

La tua classe di aspetto sembra buona.

Aggiungi @Filter e @FilterDef sulle proprie entità:

  • @Filter: Aggiunge filtro a un ente o un ente di destinazione.
  • @FilterDef: Definisce il nome e i parametri di definizione del filtro per impostare i valori durante l'abilitazione del filtro.

Esempio:

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

In configurazione, il filtro è attivato, ma non ha parametri.

Esempio di prova:

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

Naturalmente, è possibile impostare tutti i parametri di cui hai bisogno in @FilterDef annotazione.

Sperando che ti aiuti. Cordiali saluti, André.

+0

Ho già dichiarato il filtro all'interno della mia entità, ma il problema è che il filtro non è mai abilitato sulla sessione. Questo dovrebbe accadere nel mio aspetto, ma il filtro non è mai abilitato perché quel codice non viene eseguito.Il mio filtro non ha bisogno di un parametro, quindi non ne ho aggiunto nessuno. – Andy0708

0

Era di fronte al problema esatto. Sono stato in grado di risolverlo utilizzando EntityManager e ottenere le informazioni sulla sessione utilizzando il metodo unwrap piuttosto che ottenere le informazioni sulla sessione utilizzando pointcut attorno alla chiamata getCurrentSession di sessionFactory.

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

Spero che questo aiuti.

Problemi correlati