简体   繁体   中英

Spring 4 @Configuration order for setting up JPA

I had a working Jersey 2.2 applisaction with Spring 4 in which I was using Eclipselink as JPA implementation.

The application config calss looks as follows:

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

The JPA configuration class:

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

Now, this works well. On startup, application config class is being loaded FIRST so "PropertySourcesPlaceholderConfigurer" gets registered and I can use @Value(...) annotations in jpa config class which gets loaded as SECOND.

Today I have decided that I'll replace Eclipselink with Hibernate because of it's auditing abilities.

To pom.xml I have added:

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

and have changed jpa config class to:

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

Now, to my surprise, load/execution order of application config and jpa config classes has swapped so jpa config is being loaded FIRST and then application config SECONDLY which causes "PropertySourcesPlaceholderConfigurer" not being registered at jpa config class load time, so @value annotations are not working!

I am really interested in knowing why is that so? Why has the execution order swapped?

I know I can trick it by not declaring jpa config calass as @Configuration and just register it as a bean in application config class like:

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

But still, I would like to know, what is happening here?

the reason that the order changed was introduction of PersistenceExceptionTranslationPostProcessor bean into JPAConfig class.

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

Moving this to the VedicaConfig class solves the problem for me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM