2017-04-07 1 views
0

J'avais une application Java 2.2 en cours d'exécution avec Spring 4 dans laquelle j'utilisais Eclipselink comme implémentation JPA.Spring 4 @Configuration pour la configuration JPA

Le calss de configuration d'application se présente comme suit:

@Configuration 
@ComponentScan(value = "com.nws.vedica", lazyInit = true) 
@PropertySource({"classpath:swagger.properties", "classpath:vedica.properties"}) 
@ApplicationPath("/api") 

public class VedicaConfig extends ResourceConfig { 

public VedicaConfig() { 
    packages("com.nws.vedica"); 
    property(ServletProperties.FILTER_FORWARD_ON_404, true); 
    register(MultiPartFeature.class); 
    register(JacksonFeature.class); 
    register(ValidationFeature.class); 
    register(ValidationConfigurationContextResolver.class); 
    register(PropertySourcesPlaceholderConfigurer.class); 

    register(ApiListingResource.class); 
    register(SwaggerSerializers.class); 
} 
} 

La classe de configuration JPA:

@Configuration 
@EnableTransactionManagement 
public class JPAConfig { 

private Map<String, String> properties; 

@Value("${db.url}") 
private String dbConnectionURL; 

@Value("${db.user}") 
private String dbUser; 

@Value("${db.pass}") 
private String dbPassword; 

@PostConstruct 
public void init() { 
    properties = new HashMap<>(); 
    properties.put("javax.persistence.jdbc.url", dbConnectionURL); 
    properties.put("javax.persistence.jdbc.user", dbUser); 
    properties.put("javax.persistence.jdbc.password", dbPassword); 
    properties.put("javax.persistence.jdbc.driver", "org.postgresql.Driver"); 
    properties.put("javax.persistence.target-database", "PostgreSQL"); 
    properties.put("eclipselink.cache.shared.default", "true"); 
    properties.put("eclipselink.ddl-generation", "none"); 
    properties.put("eclipselink.logging.level.sql", "fine"); 
    properties.put("eclipselink.logging.parameters", "true"); 
    properties.put("eclipselink.deploy-on-startup", "true"); 
    properties.put("eclipselink.ddl-generation.output-mode", "database"); 
} 

@Bean 
public JpaTransactionManager jpaTransMan(){ 
    JpaTransactionManager jtManager = new JpaTransactionManager(
      getEntityManagerFactoryBean().getObject()); 
    return jtManager; 
} 

@Bean 
public LocalEntityManagerFactoryBean getEntityManagerFactoryBean() { 
    LocalEntityManagerFactoryBean lemfb = new LocalEntityManagerFactoryBean(); 
    lemfb.setJpaPropertyMap(properties); 
    lemfb.setPersistenceUnitName(Vedantas.PU_NAME); 
    lemfb.setPersistenceProviderClass(org.eclipse.persistence.jpa.PersistenceProvider.class); 
    return lemfb; 
} 
} 

Maintenant, cela fonctionne bien. Au démarrage, la classe de configuration d'application est en cours de chargement FIRST alors "PropertySourcesPlaceholderConfigurer" est enregistré et je peux utiliser les annotations @Value (...) dans la classe jpa config qui est chargée en SECOND.

Aujourd'hui, j'ai décidé de remplacer Eclipselink par Hibernate en raison de ses capacités d'audit.

Pour pom.xml J'ai ajouté:

<dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-core</artifactId> 
     <version>5.2.9.Final</version> 
    </dependency> 

    <dependency> 
     <groupId>org.hibernate.javax.persistence</groupId> 
     <artifactId>hibernate-jpa-2.1-api</artifactId> 
     <version>1.0.0.Final</version> 
    </dependency> 


    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-envers</artifactId> 
     <version>5.2.9.Final</version> 
    </dependency> 

et ont changé la classe de configuration JPA à:

@Configuration 
@EnableTransactionManagement 
public class JPAConfig { 

private Map<String, String> properties; 

@Value("${db.url}") 
private String dbConnectionURL; 

@Value("${db.user}") 
private String dbUser; 

@Value("${db.pass}") 
private String dbPassword; 

@PostConstruct 
public void init() { 
    properties = new HashMap<>(); 
    properties.put("javax.persistence.jdbc.url", dbConnectionURL); 
    properties.put("javax.persistence.jdbc.user", dbUser); 
    properties.put("javax.persistence.jdbc.password", dbPassword); 
    properties.put("javax.persistence.jdbc.driver", "org.postgresql.Driver"); 
    properties.put("javax.persistence.target-database", "PostgreSQL"); 
    properties.put("hibernate.hbm2ddl.auto", "create"); 
    properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect"); 
} 

@Bean 
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){ 
    JpaTransactionManager transactionManager = new JpaTransactionManager(); 
    transactionManager.setEntityManagerFactory(emf); 

    return transactionManager; 
} 

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

@Bean 
public LocalContainerEntityManagerFactoryBean entityManagerFactory() { 
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); 
    em.setPackagesToScan(new String[]{"com.nws.vedica.model"}); 
    em.setPersistenceUnitName(Vedantas.PU_NAME); 

    JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); 
    em.setJpaVendorAdapter(vendorAdapter); 
    em.setJpaPropertyMap(properties); 

    return em; 
} 
} 

Maintenant, à ma grande surprise, charge/ordre d'exécution de la configuration de l'application et configuration JPA les classes ont permuté, donc jpa config est chargé FIRST et ensuite l'application config SECONDLY qui fait que "PropertySourcesPlaceholderConfigurer" n'est pas enregistré au moment du chargement de la classe jpa config, donc les annotations @value ne fonctionnent pas!

Je suis vraiment intéressé de savoir pourquoi c'est ainsi? Pourquoi l'ordre d'exécution a-t-il été échangé?

Je sais que je peux le tromper en ne déclarant pas calass config JPA comme @Configuration et juste l'enregistrer comme un haricot dans la classe de configuration d'applications comme:

@Bean 
    public JPAConfig setUpJpaHibernate() { 
     return new JPAConfig(); 
    } 

Mais encore, je voudrais savoir, quelle est qui se passe ici?

+0

Il n'y a aucune information sur la façon dont l'application Spring est amorcée? Typiquement, spring lit toutes les configurations, construit un arbre de dépendance et instancie les nœuds feuilles en premier. S'il n'y a pas de référence entre vos deux configurations, il n'y a rien à dire à Spring dans quel ordre il devrait les instancier (donc vous devriez considérer l'ordre aléatoire). Il y a des règles spéciales pour ProperyConfiguration, par exemple si vous avez un 'PropertyPlaceholderConfigurer', la définition de 'been' doit être une méthode statique. Donc, je mettrais toujours @PropertySource dans la configuration amorcée. –

+0

Eh bien, fondamentalement, vous n'avez pas répondu à ma question ni fourni de solution. J'ai deux morceaux de code qui se comportent différemment. Je dois m'assurer d'abord que la classe qui charge RecourceConfig se charge, car c'est là que l'application config a lieu. – greengold

+0

Normalement, je voudrais juste déboguer le code de printemps pour comprendre pourquoi cela se produit. Pour une raison quelconque, la résolution des dépendances dans Spring provoque le traitement de votre 'JPAConfig' avant la config qui étend' ResourceConfig', normalement cela serait dû à une dépendance entre les deux, ou si elles sont toutes deux des configurations de feuilles, l'ordre est aléatoire '. Personnellement, je suis plus curieux de savoir quel problème Jersey résout, que Spring MVC ne fait pas, et pourquoi l'audit dans EclipseLink n'est pas assez bon? –

Répondre

0

La raison pour laquelle l'ordre a été modifié était l'introduction du bean PersistenceExceptionTranslationPostProcessor dans la classe JPAConfig. Déplacer ceci vers la classe VedicaConfig résout le problème pour moi.