2017-04-07 2 views
0

Ich hatte eine funktionierende Jersey 2.2 Applikation mit Spring 4, in der ich Eclipselink als JPA-Implementierung verwendete.Spring 4 @Konfigurationsreihenfolge zum Einrichten von JPA

Die Anwendung Config calss sieht wie folgt aus:

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

Die PPV Konfigurationsklasse:

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

Nun, das funktioniert gut. Beim Start wird die Anwendungskonfigurationsklasse FIRST geladen, so dass "PropertySourcesPlaceholderConfigurer" registriert wird und ich @Value (...) Annotationen in der jpa config-Klasse verwenden kann, die als SECOND geladen wird.

Heute habe ich entschieden, dass ich Eclipselink wegen seiner Auditing-Fähigkeiten durch Hibernate ersetzen werde.

Um pom.xml Ich habe hinzugefügt:

<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> 

und haben JPA Config-Klasse geändert:

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

Nun zu meiner Überraschung, Last/Ausführungs Reihenfolge der Anwendung config und JPA Config Klassen haben ausgetauscht, so dass jpa config zuerst geladen wird und dann die Anwendungskonfiguration SECONDD, was dazu führt, dass "PropertySourcesPlaceholderConfigurer" nicht bei der Ladezeit der jpa config Klasse registriert wird, also funktionieren @value Annotationen nicht!

Ich bin wirklich interessiert zu wissen, warum ist das so? Warum wurde die Ausführungsreihenfolge getauscht?

Ich weiß, dass ich es nicht erklären JPA Config calass als @Configuration Trick kann und es einfach wie eine Bohne in Anwendung Config-Klasse registrieren wie:

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

Aber noch, ich möchte wissen, was ist passiert hier?

+0

Es gibt keine Informationen darüber, wie die Spring-Anwendung bootstrappt wird. Normalerweise liest Feder alle Konfigurationen, erstellt einen Abhängigkeitsbaum und instanziiert zuerst die Blattknoten. Wenn es keinen Bezug zwischen Ihren zwei Konfigurationen gibt, gibt es nichts, um Spring zu sagen, in welcher Reihenfolge es instanziiert werden soll (Sie sollten also die Reihenfolge zufällig betrachten). Es gibt spezielle Regeln für PropertyConfiguration, zum Beispiel, wenn Sie einen 'PropertyPlaceholderConfigurer' haben, muss die vorhandene Definition eine statische Methode sein. Also würde ich @PropertySource immer in die Bootstrapped-Konfiguration setzen. –

+0

naja, im Grunde hast du meine Frage nicht beantwortet und keine Lösung geliefert. Ich habe zwei Teile Code, die sich anders verhalten. Ich muss sicherstellen, dass die Klasse, die RecourceConfig lädt, zuerst geladen wird, da hier die App-Konfiguration stattfindet. – greengold

+0

Normalerweise würde ich nur den Spring-Code debuggen, um herauszufinden, warum das passiert. Aus irgendeinem Grund bewirkt die Abhängigkeitsauflösung in Spring, dass Ihre 'JPAConfig' vor der Config verarbeitet wird, die' ResourceConfig' erweitert, normalerweise würde dies auf eine Abhängigkeit zwischen den beiden zurückzuführen sein, oder wenn sie beide Blattkonfigurationen sind, ist die Reihenfolge 'zufällig' ". Persönlich bin ich eher neugierig, welches Problem Jersey löst, das Spring MVC nicht, und warum das Auditing in EclipseLink nicht gut genug ist? –

Antwort

0

Der Grund, dass die Reihenfolge geändert wurde, war die Einführung von PersistenceExceptionTranslationPostProcessor Bean in JPAConfig-Klasse.

Das Verschieben auf die VedicaConfig-Klasse löst das Problem für mich.