2010-06-28 9 views
1

J'utilise la persistance gérée par conteneur EJB3 dans le cas où un EntityManager est injecté via l'annotation @PersistenceContext. Le contexte persistant peut alors être propagé aux EJB imbriqués. Les transactions sont également gérées par le contaner (glassfish).EJB3 Configuration programmatique CMP + de Hibernate AnnotationConfiguration

Habituellement, je laisserais persistence.xml dans le répertoire META-INF et le conteneur déterminerait quel fournisseur utiliser et comment configurer EntityManagerFactory (basé sur les propriétés spécifiques d'hibernate).

Mon problème est que je dois me connecter au processus de configuration EntityManagerFactory. En particulier, j'ai besoin de changer les valeurs discriminantes dans certaines classes persistantes avant que l'EntityManagerFactory ne soit configuré (figé pour toute modification).

C'est ainsi que je le fais avec Spring, mais j'ai besoin de faire similaire avec EJB3 CMP pur (ou peut-être avec l'aide de Spring).

public class AnnotationSessionFactoryBean extends org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean { 
    /** Log4j logging instance. */ 
    protected static Logger log = Logger.getLogger(AnnotationSessionFactoryBean.class); 

    //some data preloaded from the database using jdbc 
    private Map<String, DatabaseConfiguration> configs; 

    @Override 
    protected void postProcessAnnotationConfiguration(AnnotationConfiguration config) throws HibernateException { 
     //Load and process dynamic Mappings. 
     Iterator classMappingsIter = config.getClassMappings(); 
     while(classMappingsIter.hasNext()) { 
      PersistentClass persistentClass = (PersistentClass) classMappingsIter.next(); 

      String discriminatorValue = persistentClass.getDiscriminatorValue(); 
      if(discriminatorValue != null) { 
       log.debug("DiscriminatorValue before [" + discriminatorValue + "]"); 
       //here I replace discriminator values. 
       //The Discriminator values are coded in the annotations 
       //as names (words). These words need to be replaced with ids 
       //previously loaded from the database using jdbc. 
       //The names are constant in all environments, however the ids are 
       //are different.  
       discriminatorValue = StringUtil.replacePlaceholders(discriminatorValue, configs); 
       persistentClass.setDiscriminatorValue(discriminatorValue); 
       log.debug("DiscriminatorValue after [" + discriminatorValue + "]"); 
      } 


     } 
     super.postProcessAnnotationConfiguration(config); 
    } 

    /** 
    * @return the configs 
    */ 
    public Map<String, DatabaseConfiguration> getConfigs() { 
     return configs; 
    } 

    /** 
    * @param configs the configs to set 
    */ 
    public void setConfigs(Map<String, DatabaseConfiguration> configs) { 
     this.configs = configs; 
    } 


} 

Merci à l'avance, Anton

Répondre

0

Vous pouvez passer outre les annotations de métadonnées en fournissant un fichier de mappage XML (voir le chapitre 10 XML descripteur dans la spécification JPA 1.0).

Bien sûr, ce n'est pas dynamique (sauf si vous générez le fichier de mappage XML en utilisant par exemple FreeMarker et alimentez les valeurs à partir de la base de données).

+0

Pascal, Je connais les descripteurs XML. J'ai déjà traversé cette solution. Mais c'est juste moche. J'ai tout dans la classe Java (y compris le discriminateur). Je pense que la configuration de PersistentClasses par programme est une meilleure idée que de conserver des tas de fichiers. Une autre raison pour laquelle je dois utiliser une configuration programmatique est de définir le type de collection MultiMap sur certaines collections de cartes OneToMany. J'ai trouvé que c'est la seule façon de le faire lorsque vous utilisez des annotations. En fait, il est facile de faire avec XML hibernate natif, mais pas avec des annotations ni avec des descripteurs XML. – Anton

+0

Comme je l'ai dit, je peux faire tout ce qui précède avec Spring. Cependant, l'EJB3 ne semble pas avoir un tel intercepteur/rappel pour se connecter au processus de configuration. – Anton

1

Je pense avoir trouvé la solution. La classe org.hibernate.ejb.HibernatePersistence peut être surchargée.

public class HibernatePersistenceCustom extends org.hibernate.ejb.HibernatePersistence { 
    /** Log4j logging instance. */ 
    protected static Logger log = Logger.getLogger(HibernatePersistenceCustom.class); 

    @Override 
    public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) { 
     Ejb3Configuration cfg = new Ejb3Configuration(); 
     //here you can configure it 
     doCustomConfiguration(cfg); 
     Ejb3Configuration configured = cfg.configure(info, map); 
     return configured != null ? configured.buildEntityManagerFactory() : null; 
    } 

    ... 
    //other methods can also be overridden if required. 


    public void doCustomConfiguration(Ejb3Configuration config) { 
     //Load and process dynamic Mappings. 
     Iterator classMappingsIter = config.getClassMappings(); 
     while(classMappingsIter.hasNext()) { 
      PersistentClass persistentClass = (PersistentClass) classMappingsIter.next(); 

      String discriminatorValue = persistentClass.getDiscriminatorValue(); 
      if(discriminatorValue != null) { 
       log.debug("DiscriminatorValue before [" + discriminatorValue + "]"); 
       //here I replace discriminator values. 
       //The Discriminator values are coded in the annotations 
       //as names (words). These words need to be replaced with ids 
       //previously loaded from the database using jdbc. 
       //The names are constant in all environments, however the ids are 
       //are different.  
       discriminatorValue = StringUtil.replacePlaceholders(discriminatorValue, configs); 
       persistentClass.setDiscriminatorValue(discriminatorValue); 
       log.debug("DiscriminatorValue after [" + discriminatorValue + "]"); 
      } 


     } 

    } 
} 

puis à persistence.xml au lieu de mettre org.hibernate.ejb.HibernatePersistence com.mydomain.persistence.HibernatePersistenceCustom

<?xml version="1.0" encoding="UTF-8"?> 
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> 
    <persistence-unit name="mypersistenceunit" transaction-type="JTA"> 
    <provider>com.mydomain.persistence.HibernatePersistenceCustom</provider> 
    <jta-data-source>jdbc/mydatasource</jta-data-source> 
    <properties> 
     <property name="hibernate.show_sql" value="false"/> 
     <property name="hibernate.format_sql" value="false"/> 
     <property name="hibernate.use_sql_comments" value="false"/> 
     <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.SunONETransactionManagerLookup"/> 
     <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/> 
    </properties> 
    </persistence-unit> 
</persistence> 

Je n'ai pas encore testé, mais je pense que cela va fonctionner.

Merci

+0

+1 Intéressant. Ça a marché? –

+0

Pas comme je m'y attendais. Ma solution n'a pas fonctionné. Puis j'ai trouvé une solution similaire ici (http://netbeans.dzone.com/news/netbeans-jpa-part-two?page=0,2) où le fichier META-INF/service/javax.persistence.spi.PersistenceProvider devait être changé. La classe HibernatePersistence d'origine a dû être remplacée par ma sous-classe. Cependant cette solution n'a pas fonctionné non plus en raison de l'original hibernate-entitymanager dans classpath je pense. J'ai donc fini par appliquer ce patch http://opensource.atlassian.com/projects/hibernate/browse/HHH-4189 qui est similaire aux deux solutions ci-dessus mais plus générique. – Anton

+0

Si cela ne vous dérange pas pouvez-vous s'il vous plaît voter pour ce patch? – Anton