Reputation: 709
I need an advice.
I have base Maven project where I declare all my repositories, entities and persistence classes. They are all configured XML-less. Each database and all it's related classes are divided on packages. Everything is annotated on this project, as in: @Entity, @Repository, @Configuration, @Primary, etc... It looks like this:
Right now, beans on the radiusfttx.domain package are annotated as @Primary. This project is basically it, it does nothing on it's own.
Then, when I create a new project that needs to access any of the databases, I just import the first project with Maven and, on the new project initialization, I change it like this (it's Kotlin but I'm pretty sure you can get the idea):
@SpringBootApplication
@ComponentScan(basePackages = [
"com.entre.rest.boot", <-- my main class is here
"com.entre.databases.mailserver.domain", <-- repos from the other project
"com.entre.databases.integrator.domain" ])
Since everything is already annotated, Spring-boot's autoconfiguration picks up the classes and creates the beans without a problem. Well, at least until I try to commit something to the database.
Because only one persistence can have the @Primary annotation, if I don't add that persistence to @ComponentScan everything just doesn't work. As you can see in the example above, I didn't add "radiusfttx.domain" to @ComponentScan and the @Primary is there.
So my question is: Is there a way that I can set @Primary on the fly? Being annotated and hardcoded does not work for what I'm trying to do here. Any ideas?
Thanks in advance.
EDIT 1
As a workaround I created a "dummy" H2 persistence which I tag as @Primary and add to all my projects. This way, all my "real" datasources don't need to be set @Primary. This is far from a solution to this case, but it works. The question remains open in case someone can come up with something better.
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "emfDummy", transactionManagerRef = "tmDummy",
basePackages = ["com.entre.databases.dummy.domain"])
class DummyPersistence {
@Primary
@Bean("dsDummy")
fun dataSource() = HikariDataSource().apply {
username = "sa"
password = "sa"
jdbcUrl = "jdbc:h2:mem:db;DB_CLOSE_DELAY=-1"
driverClassName = "org.h2.Driver"
}
fun hibernateJpaVendorAdapter() = HibernateJpaVendorAdapter().apply {
setDatabasePlatform("org.hibernate.dialect.H2Dialect")
setShowSql(false)
setGenerateDdl(true)
}
@Primary
@Bean("emfDummy")
fun entityManagerFactory() = LocalContainerEntityManagerFactoryBean().apply {
setPackagesToScan("com.entre.databases.dummy.domain")
jpaVendorAdapter = hibernateJpaVendorAdapter()
dataSource = dataSource()
persistenceUnitName = "puDummy"
afterPropertiesSet()
}
@Primary
@Bean("tmDummy")
fun transactionManager() = JpaTransactionManager().apply {
entityManagerFactory = entityManagerFactory().`object`
}
}
Upvotes: 0
Views: 157
Reputation: 709
Thanks for all the answers, you made me look into the right direction.
Although I understand the use of @Qualifier it wasn't enough for my specific project because, as far as I could understand, the annotation would have to be on the "library" project that is imported. My problem is really with the transaction since I can't specify @Primary after I import a specific package in which the persistence configuration is not already tagged as @Primary.
So here is how I solved it.
Instead of just creating repositories on the "library" project I also created the @Service layer there. These @Services I can also annotate with @Transactional and name the transactionManager Spring needs to use for that specific service. So, in a "ServiceDummy" for instance, I can add a @Transactional("tmDummy") and autowire only the @Service on the other application. This way, Spring does not argue about having 2, 3 or more transactionManagers to choose.
Brief example:
package com.entre.databases.dummy.service
@Service
@Transactional("tmDummy")
class DummyService(
val dummyRepository: DummyRepository)
{
fun findDummy(id: Int): Optional<Dummy> {
return dummyRepository.findById(id)
}
}
And the on the other application that is importing the "library":
@SpringBootApplication
@ComponentScan(basePackages = [
"com.entre.rest.boot",
"com.entre.databases.dummy.service",
"com.entre.rest.web"
])
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
Upvotes: 1
Reputation: 1703
You can either use @Qualifier
, or don't mark your component as @Component
at all, instead leave them as simple classes in your lib and set whichever you want as a @Bean
in the client application.
This way you can either have single datasource or, if you need more, you'll be able to add @Primary
on the @Bean
you want.
Regarding profiles, you can set additional profiles besides prod/dev/test, e.g. prod,ds1
prod,ds2
test,tds1
, etc...
Upvotes: 1