简体   繁体   中英

Spring Boot with multiple datasources and external configuration, Spring JPA

i have a simple Spring Boot application with 2 external jars. Each jar uses Spring JPA and is completely configured to work standalone.

Jar1

JPA Repository

@Repository
public interface QuarterRepository extends JpaRepository<Quarter, Long>{


}

Entity

@Entity
public class Quarter implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;...

Configurations

@Configuration
@Import({RepositoryConfig.class})
public class OnlineJpaAppConfig{

    @Bean(name = "onlinejpaAppConfigConfigurer")
    public static PropertyPlaceholderConfigurer getPropertyPlaceholderConfigurer()    {        
        PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
        ppc.setLocation(new ClassPathResource("/de/comp/onlinejpa/application.properties"));
        ppc.setIgnoreUnresolvablePlaceholders(true);
        return ppc;
    }
}  


@Configuration
@EnableJpaRepositories(basePackages = {"de.comp.onlinejpa.repository"},
        entityManagerFactoryRef = "onlinejpaentityManagerFactory",
        transactionManagerRef = "onlinejpaTransactionManager")
@EnableTransactionManagement
public class RepositoryConfig {

    @Value("${jdbc.driverClassName}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Value("${hibernate.dialect}")
    private String hibernateDialect;
    @Value("${hibernate.show_sql}")
    private String hibernateShowSql;
    @Value("${hibernate.hbm2ddl.auto}")
    private String hibernateHbm2ddlAuto;

    private static final String QUALIFIER = "onlinejpa";

    @Bean(name = "onlinejpaentityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan(new String[]{"de.comp.onlinejpa.entity"});
        em.setPersistenceUnitName("onlinejpa");
        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(additionalProperties());

        return em;
    }

    @Bean(name = "onlinejpaDataSource")
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean(name = "onlinejpaTransactionManager")
    public PlatformTransactionManager transactionManager(@Qualifier(value = "onlinejpaentityManagerFactory") EntityManagerFactory onlinejpaentityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(onlinejpaentityManagerFactory);

        return transactionManager;
    }

    @Bean(name = "onlinejpaExceptionTranslation")
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    Properties additionalProperties() {

        Properties properties = new Properties();
        properties.setProperty("hibernate.hbm2ddl.auto", hibernateHbm2ddlAuto);
        properties.setProperty("hibernate.dialect", hibernateDialect);
        return properties;
    }
}

The second jar looks almost the same as Jar1, except the bean and the package names

@EnableJpaRepositories(basePackages = {"de.comp.aisjpa.repository"}, 
        entityManagerFactoryRef = "aisjpaentityManagerFactory",
        transactionManagerRef = "aisjpatransactionManager")
@EnableTransactionManagement
public class RepositoryConfig {

    @Value("${jdbc.driverClassName}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Value("${hibernate.dialect}")
    private String hibernateDialect;
    @Value("${hibernate.show_sql}")
    private String hibernateShowSql;
    @Value("${hibernate.hbm2ddl.auto}")
    private String hibernateHbm2ddlAuto;

     private static final String QUALIFIER = "aisjpa";


    @Bean(name = "aisjpaentityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(aisdataSource());
        em.setPackagesToScan(new String[]{"de.comp.aisjpa.entity"});
        em.setPersistenceUnitName("ais");
        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(additionalProperties());

        return em;
    }



    @Bean(name = "aisjpadatasource")
    public DataSource aisdataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }


    @Bean(name = "aisjpatransactionManager")
    public PlatformTransactionManager transactionManager(@Qualifier(value = "aisjpaentityManagerFactory") EntityManagerFactory aisjpaentityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(aisjpaentityManagerFactory);

        return transactionManager;
    }


    @Bean(name = "aisjpaexceptionTranslation")
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    Properties additionalProperties() {
        Properties properties = new Properties();
        properties.setProperty("hibernate.hbm2ddl.auto", hibernateHbm2ddlAuto);
        properties.setProperty("hibernate.dialect", hibernateDialect);
        return properties;
    }
}

Running this Main class in Jar1 gives the expected results.. ( The same as for Jar2)

public class App {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(OnlineJpaAppConfig.class);

        QuarterRepository quarterRepository = ctx.getBean("quarterRepository", QuarterRepository.class);

        List<Quarter> quarters = quarterRepository.findAll();

        for (Quarter quarter : quarters) {

            System.out.println(quarter.getQuarter());

        }
    }
}

This is the startup class of my Spring Boot application

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
        HibernateJpaAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class } )
@Import({ KVLdapAppConfig.class, OnlineJpaAppConfig.class,AISAppConfig.class})

public class ServerApplication {

    public static void main(String[] args) {

        SpringApplication.run(ServerApplication.class, args);
    }

}

The server is starting without problems, but there are some problems during configuring the jpa stuff. The configuration for persistence unit ais should use an Oracle dialect and driver. It seems that it picks up the configuration of Jar1 .

Building JPA container EntityManagerFactory for persistence unit 'onlinejpa'
HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
HHH000412: Hibernate Core {4.2.0.Final}
HHH000206: hibernate.properties not found
HHH000021: Bytecode provider name : javassist
HHH000204: Processing PersistenceUnitInfo [


HHH000130: Instantiating explicit connection provider: org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider
HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
HHH000397: Using ASTQueryTranslatorFactory
Loaded JDBC driver: com.mysql.jdbc.Driver
Building JPA container EntityManagerFactory for persistence unit 'ais'
HHH000204: Processing PersistenceUnitInfo [


HHH000130: Instantiating explicit connection provider: org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider
HHH000400: Using dialect: **org.hibernate.dialect.MySQL5Dialect**
HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
HHH000397: Using ASTQueryTranslatorFactory
Loaded JDBC driver: com.mysql.jdbc.Driver

The next problem is the database access. Spring doesnt know which transaction manager it should use

: No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: aisjpatransactionManager,onlinejpaTransactionManager

Setting one tx manager as @Primary solves the issue above, but ends with table not found in the other lib, cause of the misconfigured tx manager / persistence unit.

Perhaps my problem is the creating of LocalContainerEntityManagerFactoryBean for each individual jar. Instead i should use the following code to setup my db connection, but this would break my individual lib.

public LocalContainerEntityManagerFactoryBean internalEntityManagerFactory(
        EntityManagerFactoryBuilder builder) {
    Map<String, Object> properties = new HashMap<String, Object>();
    properties.put("hibernate.hbm2ddl.auto", "create");
    return builder
            .dataSource(internalDataSource())
            .packages("package here")
            .persistenceUnit("onlinjpa")
            .properties(properties)
            .build();
}

How can i make each individual jar to work inside a Spring Boot application.

Here's a link to set up multiple datasources with Sping Boot, but i dont want to configure my libs datasource inside the Spring Boot application.

Multiple data source and schema creation in Spring Boot

The problem was the injecting of the wrong property values, probably caused by the static call of the PropertyPlaceholderConfigurer in each jar.

Either use

    @PropertySource("classpath:/de/comp/aisjpa/application.properties")
    public class RepositoryConfig

    @Autowired
    private Environment env;

    dataSource.setUrl(env.getRequiredProperty("jdbc.url"));
    ....

Or leave the static call to the PropertyPlaceholderConfigurer , but prefix the properties in your application.properties, so that each property value is unique in your whole application

From

jdbc.driverClassName=oracle.jdbc.OracleDriver

To

jar1.jdbc.driverClassName=oracle.jdbc.OracleDriver

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