Javers with multiple EntityManagerFactory - javers

We are just getting started with JaVers in a Spring Boot application.
This application as two EntityManagerFactory beans:
#Primary
#Bean(name = "entityManagerFactory")
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, Environment env) {
And
#Bean(name = "secondaryEntityManagerFactory")
For auditing, we are just concerned with the #Primary entity manager factory. When we start up the application, it fails on initialization because of multiple entity manager factory beans.
Is there a way to tell JaVers just to be concerned with the #Primary factory?
Thanks!
Dave

JaVers Spring Boot starter uses EntityManagerFactory only in startup phase to determine SQL dialect. In runtime, SQL connections are obtained from the ConnectionProvider bean:
#Bean
#ConditionalOnMissingBean
public ConnectionProvider jpaConnectionProvider() {
return new JpaHibernateConnectionProvider();
}
You can override this bean with an implementation, which will provide connections to secondary database.

Related

spring boot with multiple databases

I'm trying to write an application that accesses data from two sources. I'm using Spring Boot 2.3.2. I've looked at several sources for info about how to configure the app: the Spring documentation talks about setting up multiple datasources, but does not explain how to link up JPA repositories. This Baeldung article goes a lot further, but I'm looking to take advantage of autoconfiguration in Spring.
So far, I've created a separate package, added a config class (along with model and repositories), and included this package in scanBasePackages so that it's picked up. Since I'll have more than one datasource, I've added this to my #SpringBootApplication:
#Bean
#Primary
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
This successfully loads up my Spring app using the standard spring config values. The two databases are on different servers, but should share characteristics (other than url and credentials).
So, my auxiliary configuration file looks like this
#Configuration
#EnableAutoConfiguration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "orgEntityManagerFactory",
transactionManagerRef = "orgTransactionManager",
basePackages = {
"pacage2.repositories"
}
)
public class DataSourceConfiguration {
// added because of this answer: https://stackoverflow.com/a/51305724/167889
#Bean
public EntityManagerFactoryBuilder entityManagerFactoryBuilder() {
return new EntityManagerFactoryBuilder(new HibernateJpaVendorAdapter(), new HashMap<>(), null);
}
#Bean
#ConfigurationProperties(prefix = "external.datasource")
public DataSourceProperties orgDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
public HikariDataSource orgDataSource(#Qualifier("orgDataSourceProperties") DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().type(HikariDataSource.class)
.build();
}
#Bean
public LocalContainerEntityManagerFactoryBean orgEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("orgDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("package2.model")
.build();
}
#Bean
public PlatformTransactionManager orgTransactionManager(
#Qualifier("orgEntityManagerFactory") EntityManagerFactory entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Now, the error I'm getting right now is Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set. However, I have that value in my config and it's applied by the Spring auto config. I believe it needs to be set in the EntityManagerFactoryBuilder and by creating my own, the autoconfig isn't getting applied.
How can I have my cake and eat it too? I'd like to leverage as much of the robust autoconfiguration that Spring provides to setup datasources and wire them to the appropriate repositories. Effectively, all that I want to change is the url and credentials, and I can separate the entities and repositories into a completely separate package for easy scanning.

Need to configure my JPA layer to use a TransactionManager (Spring Cloud Task + Batch register a PlatformTransactionManager unexpectedly)

I am using Spring Cloud Task + Batch in a project.
I plan to use different datasources for business data and Spring audit data on the task. So I configured something like:
#Bean
public TaskConfigurer taskConfigurer() {
return new DefaultTaskConfigurer(this.singletonNotExposedSpringDatasource());
}
#Bean
public BatchConfigurer batchConfigurer() {
return new DefaultBatchConfigurer(this.singletonNotExposedSpringDatasource());
}
whereas main datasource is autoconfigured through JpaBaseConfiguration.
The problem comes when SimpleBatchConfiguration+DefaultBatchConfigurer expose a PlatformTransactionManager bean, since JpaBaseConfiguration has a #ConditionalOnMissingBean on PlatformTransactionManager. Therefore Batch's PlatformTransactionManager, binded to the spring.datasource takes place.
So far, this seems to be caused because this bug
So I tried to emulate what JpaBaseConfiguration does, defining my own PlatformTransactionManager over my biz datasource/entityManager.
#Primary
#Bean
public PlatformTransactionManager appTransactionManager(final LocalContainerEntityManagerFactoryBean appEntityManager) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(appEntityManager.getObject());
this.appTransactionManager = transactionManager;
return transactionManager;
}
Note I have to define it with a name other than transactionManager, otherwise Spring finds 2 beans and complains (unregardless of #Primary!)
But now it comes the funny part. When running the tests, everything runs smooth, tests finish and DDLs are properly created for both business and Batch/Task's databases, database reads work flawlessly, but business data is not persisted in my testing database, so final assertThats fail when counting. If I #Autowire in my test PlatformTransactionManager or ÈntityManager, everything indicates they are the proper ones. But if I debug within entityRepository.save, and execute org.springframework.transaction.interceptor.TransactionAspectSupport.currentTransactionStatus(), it seems the DatasourceTransactionManager from Batch's configuration is overriding, so my custom exposed PlatformTransactionManager is not being used.
So I guess it is not a problem of my PlatformManager being the primary, but that something is configuring my JPA layer TransactionInterceptor to use the non primary but transactionManager named bean of Batch.
I also tried with making my #Configuration implement TransactionManagementConfigurer and override PlatformTransactionManager annotationDrivenTransactionManager() but still no luck
Thus, I guess what I am asking is whether there is a way to configure the primary TransactionManager for the JPA Layer.
The problem comes when SimpleBatchConfiguration+DefaultBatchConfigurer expose a PlatformTransactionManager bean,
As you mentioned, this is indeed what was reported in BATCH-2788. The solution we are exploring is to expose the transaction manager bean only if Spring Batch creates it.
In the meantime you can set the property spring.main.allow-bean-definition-overriding=true to allow bean definition overriding and set the transaction manager you want Spring Batch to use with BatchConfigurer#getTransactionManager. In your case, it would be something like:
#Bean
public BatchConfigurer batchConfigurer() {
return new DefaultBatchConfigurer(this.singletonNotExposedSpringDatasource()) {
#Override
public PlatformTransactionManager getTransactionManager() {
return new MyTransactionManager();
}
};
}
Hope this helps.

transaction working without #EnableTransactionManagement

I am using spring boot with mybatis. I am not using spring data or anything but on my service if i use annotation like
#Transactional(readOnly = true, propagation = Propagation.REQUIRED, rollbackFor= {Exception.class})
it is handling transaction.I have not specified anywhere to enable transaction management. How spring boot enable this. How is this possible ?
By default Spring Boot does initiate the transaction autoconfigration if database source properties does exist.
See the DataSourceTransactionManagerAutoConfiguration code.
#Configuration
#ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
#AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
#EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration
// other code
#Bean
#ConditionalOnMissingBean(PlatformTransactionManager.class)
public DataSourceTransactionManager transactionManager(
DataSourceProperties properties) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(
this.dataSource);
if (this.transactionManagerCustomizers != null) {
this.transactionManagerCustomizers.customize(transactionManager);
}
return transactionManager;
}
The datasource properties are: url, driverClassName, jndiName etc.

How do you autowire/inject your datasource in Spring-boot?

I have been working with Spring boot for a bit now, and the datasource is always configured in your application.properties in every example I have seen, kind of like this:
# DataSource configuration
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/abcdef
spring.datasource.username=******
spring.datasource.password=******
However, lately I have been trying to integrate Spring Social, and the examples I have seen configure it in java in a config file like this:
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.url"));
dataSource.setUsername(env.getProperty("db.username"));
dataSource.setPassword(env.getProperty("db.password"));
return dataSource;
}
This allows for the datasource object to later be injected or autowired into the social config as seen here for example.
My question is, do I need to configure a datasource bean like this to be able to later inject the datasource, or will Spring-boot handle that for me?
Not a Spring (or Boot) expert by any means, but Spring Boot will auto-provide a Bean of type DataSource if the properties are there and there's a requirement for it. To use it you just #Autowire it.
Try this . If there are multiple #Configuration in springboot , You can import the the other config(DataSourceConfig) into your main AppConfig.
And then Using #PropertySource pull in the db url,username,password etc
https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch04s03.html
#Configuration
#Import(DataSourceConfig.class)
#PropertySource("classpath:application.properties")
public class SpringbatchConfig {
#Autowired
DataSourceConfig dataSourceConfig;
#Bean
public void myService myService() {
return new myServiceImpl(dataSourceConfig.dataSource());
}
}

Spring Boot 1.2.5.RELEASE & Spring Security 4.0.2.RELEASE - How to load configuration file before security context initialization?

I'm facing a problem with Spring: I'm migrating from Spring Security ver. 3.2.7.RELEASE to 4.0.2.RELEASE. Everything was working fine in older version, however a problem occured when it came to loading DataSource.
Let me describe the architecture:
Application is secured with both SAML and LDAP mechanisms (SAML configuration is pretty similar to config given here: https://github.com/vdenotaris/spring-boot-security-saml-sample/blob/master/src/main/java/com/vdenotaris/spring/boot/security/saml/web/config/WebSecurityConfig.java).
They both need to connect to database in order to get some required data. We use MyBatis with Spring Mybatis to get needed data. That's, where the problem begins.
My DAO configuration class looks like this:
#Configuration
#EnableConfigurationProperties
#MapperScan(basePackages = { "pl.myapp" })
public class DaoConfiguration {
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#Primary
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
#Bean
#Primary
public SqlSessionFactoryBean sqlSessionFactoryBean() {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
// some stuff happens here
return sqlSessionFactoryBean;
}
#Bean
#Primary
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
#Bean
#ConfigurationProperties(prefix = "liquibase.datasource")
#ConditionalOnProperty(name="liquibase.enabled")
public DataSource liquibaseDataSource() {
DataSource liquiDataSource = DataSourceBuilder.create().build();
return liquiDataSource;
}
}
In previous version it worked like a charm, but now it has a problem loading mappers, resulting in Bean creation exception on FactoryBean type check: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someMapper' defined in file [<filename>]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
over and over again (it's not my problem, it's a known Spring/MyBatis bug).
I did some debugging and discovered something interesting: it looks like DaoConfiguration is not treated like a configuration here! I mean: if I add
#Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
return sqlSessionFactoryBean().getObject();
}
to this config, "normal" call of #Bean annotated method should result in calling proper interceptor, here it lacks this funcionality.
My prediction is that: this config class has not been properly wrapped yet and Spring Security already needs beans produced by it.
Is there any solution to properly load this configuration before Spring Security is initialized? Or am I just wrong and missing something (maybe not so) obvious?

Resources