I have a web application where I have 3 jndi resources defined in the web.xml
1 for the database and 2 for dyna cache
How can I convert it into Java configuration in Spring boot.
Following is the sample resource ref configuration in the application
<resource-ref>
<description>Resource reference for database</description>
<res-ref-name>jdbc/dbname</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<resource-ref id="cache1">
<description>cache1 description</description>
<res-ref-name>cache/cache1</res-ref-name>
<res-type>com.ibm.websphere.cache.DistributedMap</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<resource-ref id="cache2">
<description>cache2 description</description>
<res-ref-name>cache/cache2</res-ref-name>
<res-type>com.ibm.websphere.cache.DistributedMap</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
Thanks
Multiple Databases in Spring Boot
More on / Font: baeldung
Spring Boot can simplify the configuration above.
By default, Spring Boot will instantiate its default DataSource with the configuration properties prefixed by spring.datasource.*:
spring.datasource.jdbcUrl = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
We now want to keep using the same way to configure the second DataSource, but with a different property namespace:
spring.second-datasource.jdbcUrl = [url]
spring.second-datasource.username = [username]
spring.second-datasource.password = [password]
Because we want the Spring Boot autoconfiguration to pick up those different properties (and instantiate two different DataSources), we'll define two configuration classes similar to the previous sections:
#Configuration
#PropertySource({"classpath:persistence-multiple-db-boot.properties"})
#EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.user",
entityManagerFactoryRef = "userEntityManager",
transactionManagerRef = "userTransactionManager")
public class PersistenceUserAutoConfiguration {
#Primary
#Bean
#ConfigurationProperties(prefix="spring.datasource")
public DataSource userDataSource() {
return DataSourceBuilder.create().build();
}
// userEntityManager bean
// userTransactionManager bean
}
.
#Configuration
#PropertySource({"classpath:persistence-multiple-db-boot.properties"})
#EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.product",
entityManagerFactoryRef = "productEntityManager",
transactionManagerRef = "productTransactionManager")
public class PersistenceProductAutoConfiguration {
#Bean
#ConfigurationProperties(prefix="spring.second-datasource")
public DataSource productDataSource() {
return DataSourceBuilder.create().build();
}
// productEntityManager bean
// productTransactionManager bean
}
Now we have defined the data source properties inside persistence-multiple-db-boot.properties according to the Boot autoconfiguration convention.
The interesting part is annotating the data source bean creation method with #ConfigurationProperties. We just need to specify the corresponding config prefix. Inside this method, we're using a DataSourceBuilder, and Spring Boot will automatically take care of the rest.
But how do the configured properties get injected into the DataSource configuration?
When calling the build() method on the DataSourceBuilder, it'll call its private bind() method:
public T build() {
Class<? extends DataSource> type = getType();
DataSource result = BeanUtils.instantiateClass(type);
maybeGetDriverClassName();
bind(result);
return (T) result;
}
This private method performs much of the autoconfiguration magic, binding the resolved configuration to the actual DataSource instance:
private void bind(DataSource result) {
ConfigurationPropertySource source = new
MapConfigurationPropertySource(this.properties);
ConfigurationPropertyNameAliases aliases = new
ConfigurationPropertyNameAliases();
aliases.addAliases("url", "jdbc-url");
aliases.addAliases("username", "user");
Binder binder = new Binder(source.withAliases(aliases));
binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(result));
}
Although we don't have to touch any of this code ourselves, it's still useful to know what's happening under the hood of the Spring Boot autoconfiguration.
Besides this, the Transaction Manager and Entity Manager beans configuration is the same as the standard Spring application.
#Configuration
#EnableTransactionManagement
#ComponentScan("org.example")
#EnableJpaRepositories(basePackages = "org.yours.persistence.dao")
public class PersistenceJNDIConfig {
#Autowired
private Environment env;
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
throws NamingException {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
// rest of entity manager configuration
return em;
}
#Bean
public DataSource dataSource() throws NamingException {
return (DataSource) new JndiTemplate().lookup("jdbc/dbname");
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
// rest of persistence configuration
}
We’re going to use a simple model with the #Entity annotation with a generated id and a name:
#Entity
public class Foo {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
// default getters and setters
}
Let’s define a simple repository:
#Repository
public class FooDao {
#PersistenceContext
private EntityManager entityManager;
public List<Foo> findAll() {
return entityManager
.createQuery("from " + Foo.class.getName()).getResultList();
}
}
And lastly, let’s create a simple service:
#Service
#Transactional
public class FooService {
#Autowired
private FooDao dao;
public List<Foo> findAll() {
return dao.findAll();
}
}
With this, you have everything you need in order to use your JNDI datasource in your Spring application.
Related
I'm currently working on Spring Boot project where I need to connect to more than 2 data source (actually 4).
I found many examples how to connect to 2 DS and it works, but when I add next in the same way it's not working:
...Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class...
Is there any restriction on data sources?
Or is it possible to connect to more than 2 DS?
Create as many datasource you want. No restrictions i am aware of. It is just another bean .Refer here
#first db
spring.datasource.url = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
spring.datasource.driverClassName = oracle.jdbc.OracleDriver
#second db ...
spring.secondDatasource.url = [url]
spring.secondDatasource.username = [username]
spring.secondDatasource.password = [password]
spring.secondDatasource.driverClassName = oracle.jdbc.OracleDriver
#Bean("firstds")
#ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean("secondds")
#ConfigurationProperties(prefix="spring.secondDatasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
Adding to what #Surya said
Step 1: Set up the data base configuration in application.properties file as mentioned above.
Step 2:You need to create a bean.In your case u need to create 2 separate bean pointing to 2 diff data source.
#Bean("firstds")
#ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean("secondds")
#ConfigurationProperties(prefix="spring.secondDatasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
U can create this 2 bean in a single class.
Now wondering how to use this bean!
#Autowired
#Qualifier("firstds")
NamedParameterJdbcTemplate firstConnection;
result = firstConnection.query(Your query goes here)
Above query will always hit first db via the bean you created.
Similarly you can use connection to hit db 2 via the second bean.
Thanks for your help.
Actually I did all configuration right but ... (one small misspell crash all :))
As I was starting including my code I found it, fixed and now all works fine.
But here is my code as example (maybe someone will use it :))
All three entities and repositories (Shadow, Main, Project) are configured exactly in the same way so I put it only once. (the only differences is that DS for Main is as #Primary.
Thanks.
BR
application.properties
#-------------------------- first
spring.datasource.url=jdbc:mysql://
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#-------------------------- second
second.datasource.url=jdbc:mysql://
second.datasource.username=
second.datasource.password=
second.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#-------------------------- third
third.datasource.url=jdbc:mysql://
third.datasource.password=
third.datasource.username=
third.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
Shadow.java
package com.server.shadow.domain;
import ...
#Entity
#Table(name = "shadow")
public class Shadow {
#Id
private String id;
private String shadow;
public Shadow(){}
//getters and setters
}
ShadowRepository.java
package com.server.shadow.repo;
import ...
#Repository
public interface ShadowRepository extends JpaRepository<Shadow,Long> {
}
ShadowDbConf.java
package com.server;
import ...
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "shadowEntityManagerFactory",
transactionManagerRef = "shadowTransactionManager",
basePackages = {"com.server.shadow.repo"}
)
public class ShadowDbConf {
#Bean(name = "shadowDataSource")
#ConfigurationProperties(prefix = "second.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "shadowEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean
shadowEntityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("shadowDataSource") DataSource dataSource
) {
return
builder
.dataSource(dataSource)
.packages("com.server.shadow.domain")
.persistenceUnit("shadow")
.build();
}
#Bean(name = "shadowTransactionManager")
public PlatformTransactionManager shadowTransactionManager(
#Qualifier("shadowEntityManagerFactory") EntityManagerFactory
shadowEntityManagerFactory
) {
return new JpaTransactionManager(shadowEntityManagerFactory);
}
}
MainController.java
package com.server;
import ...
#RestController
public class MainController {
private final ShadowRepository shadowRepository;
private final MainRepository mainRepository;
private final PojectRepository projectRepository;
#Autowired
MainController(ShadowRepository shadowRepository, MainRepository mainRepository,PojectRepository projectRepository) {
this.shadowRepository = shadowRepository;
this.mainRepository = mainRepository;
this.projectRepository = projectRepository;
}
#GetMapping(path = "/shadow")
public #ResponseBody Iterable<Shadow> getShadows(){
return shadowRepository.findAll();
}
}
I have Spring Cloud Task that loads data from SQL Server to Cassandra DB which will be run on Spring Cloud Data Flow.
One of the requirement of Spring Task is to provide relational database to persist metadata like task execution state. But I don't want use either of the above databases for that. Instead, I have to specify third database for persistence. But it seems like Spring Cloud Task flow automatically picks up data source properties of SQL Server from application.properties. How can I specify another db for task state persistence?
My Current properties:
spring.datasource.url=jdbc:sqlserver://iphost;databaseName=dbname
spring.datasource.username=user
spring.datasource.password=password
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.show-sql=false
#spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.ddl-auto=none
spring.data.cassandra.contact-points=ip
spring.data.cassandra.port=9042
spring.data.cassandra.username=username
spring.data.cassandra.password=password
spring.data.cassandra.keyspace-name=mykeyspace
spring.data.cassandra.schema-action=CREATE_IF_NOT_EXISTS
Update: 1
I added below code to point to 3rd database as suggested by Michael Minella. Now Spring Task is able to connect to this DB and persist state. But now my batch job source queries are also connecting to this database. Only thing I changed was to add datasource for task.
spring.task.datasource.url=jdbc:postgresql://host:5432/testdb?stringtype=unspecified
spring.task.datasource.username=user
spring.task.datasource.password=passwrod
spring.task.datasource.driverClassName=org.postgresql.Driver
#Configuration
public class DataSourceConfigs {
#Bean(name = "taskDataSource")
#ConfigurationProperties(prefix="spring.task.datasource")
public DataSource getDataSource() {
return DataSourceBuilder.create().build();
}
}
#Configuration
public class DDTaskConfigurer extends DefaultTaskConfigurer{
#Autowired
public DDTaskConfigurer(#Qualifier("taskDataSource") DataSource dataSource) {
super(dataSource);
}
}
Update #2:
#Component
#StepScope
public class MyItemReader extends RepositoryItemReader<Scan> implements InitializingBean{
#Autowired
private ScanRepository repository;
private Integer lastScanIdPulled = null;
public MyItemReader(Integer _lastIdPulled) {
super();
if(_lastIdPulled == null || _lastIdPulled <=0 ){
lastScanIdPulled = 0;
} else {
lastScanIdPulled = _lastIdPulled;
}
}
#PostConstruct
protected void setUpRepo() {
final Map<String, Sort.Direction> sorts = new HashMap<>();
sorts.put("id", Direction.ASC);
this.setRepository(this.repository);
this.setSort(sorts);
this.setMethodName("findByScanGreaterThanId");
List<Object> methodArgs = new ArrayList<Object>();
System.out.println("lastScanIdpulled >>> " + lastScanIdPulled);
if(lastScanIdPulled == null || lastScanIdPulled <=0 ){
lastScanIdPulled = 0;
}
methodArgs.add(lastScanIdPulled);
this.setArguments(methodArgs);
}
}
#Repository
public interface ScanRepository extends JpaRepository<Scan, Integer> {
#Query("...")
Page<Scan> findAllScan(final Pageable pageable);
#Query("...")
Page<Scan> findByScanGreaterThanId(int id, final Pageable pageable);
}
Update #3:
If I add config datasource for Repository, I now get below exception. Before you mention that one of the datasource needs to be declared Primary. I already tried that.
Caused by: java.lang.IllegalStateException: Expected one datasource and found 2
at org.springframework.cloud.task.batch.configuration.TaskBatchAutoConfiguration$TaskBatchExecutionListenerAutoconfiguration.taskBatchExecutionListener(TaskBatchAutoConfiguration.java:65) ~[spring-cloud-task-batch-1.0.3.RELEASE.jar:1.0.3.RELEASE]
at org.springframework.cloud.task.batch.configuration.TaskBatchAutoConfiguration$TaskBatchExecutionListenerAutoconfiguration$$EnhancerBySpringCGLIB$$baeae6b9.CGLIB$taskBatchExecutionListener$0(<generated>) ~[spring-cloud-task-batch-1.0.3.RELEASE.jar:1.0.3.RELEASE]
at org.springframework.cloud.task.batch.configuration.TaskBatchAutoConfiguration$TaskBatchExecutionListenerAutoconfiguration$$EnhancerBySpringCGLIB$$baeae6b9$$FastClassBySpringCGLIB$$5a898c9.invoke(<generated>) ~[spring-cloud-task-batch-1.0.3.RELEASE.jar:1.0.3.RELEASE]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) ~[spring-core-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:358) ~[spring-context-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.cloud.task.batch.configuration.TaskBatchAutoConfigu
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "myEntityManagerFactory",
basePackages = { "com.company.dd.collector.tool" },
transactionManagerRef = "TransactionManager"
)
public class ToolDbConfig {
#Bean(name = "myEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean
myEntityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("ToolDataSource") DataSource dataSource
) {
return builder
.dataSource(dataSource)
.packages("com.company.dd.collector.tool")
.persistenceUnit("tooldatasource")
.build();
}
#Bean(name = "myTransactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("myEntityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
#Configuration
public class DataSourceConfigs {
#Bean(name = "taskDataSource")
#ConfigurationProperties(prefix="spring.task.datasource")
public DataSource getDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "ToolDataSource")
#ConfigurationProperties(prefix = "tool.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
You need to create a TaskConfigurer to specify the DataSource to be used. You can read about this interface in the documentation here: https://docs.spring.io/spring-cloud-task/1.1.1.RELEASE/reference/htmlsingle/#features-task-configurer
The javadoc can be found here: https://docs.spring.io/spring-cloud-task/docs/current/apidocs/org/springframework/cloud/task/configuration/TaskConfigurer.html
UPDATE 1:
When using more than one DataSource, both Spring Batch and Spring Cloud Task follow the same paradigm in that they both have *Configurer interfaces that need to be used to specify what DataSource to use. For Spring Batch, you use the BatchConfigurer (typically by just extending the DefaultBatchConfigurer) and as noted above, the TaskConfigurer is used in Spring Cloud Task. This is because when there is more than one DataSource, the framework has no way of knowing which one to use.
My application has multiple data sources , so i have created two data source configuration classes based on this URL .
But while running the spring boot application am getting error
Description:
Field userDataRepo in com.cavion.services.UserDataService required a bean named 'entityManagerFactory' that could not be found.
Action:
Consider defining a bean named 'entityManagerFactory' in your configuration.
From this Question on StackOverflow helped me to figure out the issue.i need to specify the entityManagerFactoryRef on my JPA repositories .
But i have many repository classes some of them uses Entitymanager 'A' and some of them uses 'B' . my current spring boot application class is like this
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class })
#EnableTransactionManagement
#EntityScan("com.info.entity")
#ComponentScan({"com.info.services","com.info.restcontroller"})
#EnableJpaRepositories("com.info.repositories")
public class CavionApplication {
public static void main(String[] args) {
SpringApplication.run(CavionApplication.class, args);
}
#Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
};
}}
I have given the EnableJpaRepositories on the spring boot class , so how can i configure multiple EnableJpaRepositories so that i can configure multiple entityManagerFactory ?
Please suggest the best way to setup the multiple data sources .
In order to let spring knows what DataSource is related to what Repository you should define it at the #EnableJpaRepositories annotation. Let's assume that we have two entities, the Servers entity and the Domains entity and each one has its own Repo then each Repository has its own JpaDataSource configuration.
1. Group all the repositories based on the Data Source that they are related to. For example
Repository for Domains entities (package: org.springdemo.multiple.datasources.repository.domains):
package org.springdemo.multiple.datasources.repository.domains;
import org.springdemo.multiple.datasources.domain.domains.Domains;
import org.springframework.data.jpa.repository.JpaRepository;
public interface DomainsRepository extends JpaRepository<Domains,Long> {
}
Repository for Servers entities (package: org.springdemo.multiple.datasources.repository.servers)
package org.springdemo.multiple.datasources.repository.servers;
import org.springdemo.multiple.datasources.domain.servers.Servers;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ServersRepository extends JpaRepository<Servers,Long> {
}
2. For each JPA Data Soruce you need to define a configuration, in this example I show how to configure two different DataSources
Domains Jpa Configuration: the relationship between the Data Source and the repository is defined in the basePackages value, that is the reason why is necessary to group the repositories in different packages depending on the entity manager that each repo will use.
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "domainsEntityManager",
transactionManagerRef = "domainsTransactionManager",
basePackages = {"org.springdemo.multiple.datasources.repository.domains"}
)
public class DomainsConfig {
Servers Data Source Configuration: as you can see the basePackages value has the package name of the Servers Repository , and also the values of entityManagerFactoryRef and transactionManagerRef are different in order to let spring separate each entityManager.
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "serversEntityManager",
transactionManagerRef = "serversTransactionManager",
basePackages = {"org.springdemo.multiple.datasources.repository.servers"}
)
public class ServersConfig {
3. Set one Datasource as primary
In order to avoid the error message: Parameter 0 of constructor in org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration required a single bean, but 2 were found: just set one of the datasource as #Primary, in this example I select the Servers Datasource as primary:
#Bean("serversDataSourceProperties")
#Primary
#ConfigurationProperties("app.datasource.servers")
public DataSourceProperties serversDataSourceProperties(){
return new DataSourceProperties();
}
#Bean("serversDataSource")
#Primary
#ConfigurationProperties("app.datasource.servers")
public DataSource serversDataSource(#Qualifier("serversDataSourceProperties") DataSourceProperties serversDataSourceProperties) {
return serversDataSourceProperties().initializeDataSourceBuilder().build();
}
If you need more information please see the full example for each configuration:
Servers JPA Configuration
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "serversEntityManager",
transactionManagerRef = "serversTransactionManager",
basePackages = {"org.springdemo.multiple.datasources.repository.servers"}
)
public class ServersConfig {
#Bean(name = "serversEntityManager")
public LocalContainerEntityManagerFactoryBean getServersEntityManager(EntityManagerFactoryBuilder builder,
#Qualifier("serversDataSource") DataSource serversDataSource){
return builder
.dataSource(serversDataSource)
.packages("org.springdemo.multiple.datasources.domain.servers")
.persistenceUnit("servers")
.properties(additionalJpaProperties())
.build();
}
Map<String,?> additionalJpaProperties(){
Map<String,String> map = new HashMap<>();
map.put("hibernate.hbm2ddl.auto", "create");
map.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
map.put("hibernate.show_sql", "true");
return map;
}
#Bean("serversDataSourceProperties")
#Primary
#ConfigurationProperties("app.datasource.servers")
public DataSourceProperties serversDataSourceProperties(){
return new DataSourceProperties();
}
#Bean("serversDataSource")
#Primary
#ConfigurationProperties("app.datasource.servers")
public DataSource serversDataSource(#Qualifier("serversDataSourceProperties") DataSourceProperties serversDataSourceProperties) {
return serversDataSourceProperties().initializeDataSourceBuilder().build();
}
#Bean(name = "serversTransactionManager")
public JpaTransactionManager transactionManager(#Qualifier("serversEntityManager") EntityManagerFactory serversEntityManager){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(serversEntityManager);
return transactionManager;
}
}
Domains JPA Configuration
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "domainsEntityManager",
transactionManagerRef = "domainsTransactionManager",
basePackages = {"org.springdemo.multiple.datasources.repository.domains"}
)
public class DomainsConfig {
#Bean(name = "domainsEntityManager")
public LocalContainerEntityManagerFactoryBean getdomainsEntityManager(EntityManagerFactoryBuilder builder
,#Qualifier("domainsDataSource") DataSource domainsDataSource){
return builder
.dataSource(domainsDataSource)
.packages("org.springdemo.multiple.datasources.domain.domains")
.persistenceUnit("domains")
.properties(additionalJpaProperties())
.build();
}
Map<String,?> additionalJpaProperties(){
Map<String,String> map = new HashMap<>();
map.put("hibernate.hbm2ddl.auto", "create");
map.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
map.put("hibernate.show_sql", "true");
return map;
}
#Bean("domainsDataSourceProperties")
#ConfigurationProperties("app.datasource.domains")
public DataSourceProperties domainsDataSourceProperties(){
return new DataSourceProperties();
}
#Bean("domainsDataSource")
public DataSource domainsDataSource(#Qualifier("domainsDataSourceProperties") DataSourceProperties domainsDataSourceProperties) {
return domainsDataSourceProperties.initializeDataSourceBuilder().build();
}
#Bean(name = "domainsTransactionManager")
public JpaTransactionManager transactionManager(#Qualifier("domainsEntityManager") EntityManagerFactory domainsEntityManager){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(domainsEntityManager);
return transactionManager;
}
}
In order to separate each datasource I put the configuration in the application.properties file, like this:
app.datasource.domains.url=jdbc:h2:mem:~/test
app.datasource.domains.driver-class-name=org.h2.Driver
app.datasource.servers.driver-class-name=com.mysql.jdbc.Driver
app.datasource.servers.url=jdbc:mysql://localhost:3306/v?autoReconnect=true&useSSL=false
app.datasource.servers.username=myuser
app.datasource.servers.password=mypass
If you need more information please see the following documentation:
Spring Documentation: howto-two-datasources
A similar example of how configure two different databases: github example
The answered provided by #Daniel C. is correct. Small correction/observation from my side.
#Primary is not required if you don't want to mark any datasource as
default one, otherwise necessary.
If you are defining any of the EntityManagerFactoryBean with #Bean name as entityManagerFactory then it's better to mark it #Primary to avoid conflict.
#ConfigurationProperties("app.datasource.servers")
can be marked at class level instead of defining at method level.
Better to return HikariDataSource as datasource if you using Spring
Boot 2.x or higher version as it has been changed.
Make sure you define exact property for jdbc-url which is being used by
HikariDataSource to refer JDBC Connection URL.
I just added a module aware multi database aware library for mysql in github.Some application properties need to be added and you are done .
Documentation and other details could be found at :-
https://github.com/yatharthamishra0419/spring-boot-data-multimodule-mysql
I am trying to configure hibernate-ogm for Cassandra on spring boot, but there are no dataSource are transparent provided to entityManager and below error with run:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration required a bean of type 'javax.sql.DataSource' that could not be found.
- Bean method 'dataSource' not loaded because #ConditionalOnProperty (spring.datasource.jndi-name) did not find property 'jndi-name'
- Bean method 'dataSource' not loaded because #ConditionalOnBean (types: org.springframework.boot.jta.XADataSourceWrapper; SearchStrategy: all) did not find any beans
Action:
Consider revisiting the conditions above or defining a bean of type 'javax.sql.DataSource' in your configuration.
Blow a workflow for case:
start with disable auto configuration for related jpa on spring:
#Configuration
#ComponentScan
#EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class GsDataApplication {
public static void main(String[] args) {
SpringApplication.run(GsDataApplication.class, args);
}
}
and project dependencies are:
dependencies {
compile project(':shared')
compile('joda-time:joda-time:2.9.4')
//compile('org.hibernate:hibernate-core:5.2.2.Final')
compile('org.hibernate.ogm:hibernate-ogm-cassandra:5.0.1.Final')
compile('org.hibernate:hibernate-search-orm:5.5.4.Final')
compile('javax.transaction:jta:1.1')
compile('org.springframework.data:spring-data-jpa:1.10.4.RELEASE')
compile('org.springframework.boot:spring-boot-starter-social-facebook')
compile('org.springframework.boot:spring-boot-starter-social-twitter')
}
Persistence configuration:
#Configuration
#EnableJpaRepositories(basePackages = {
"com.gs.gsData.identity"
})
#EnableTransactionManagement
class PersistenceContext {
#Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(Environment env) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("com.gs.gsData.domain");
Properties jpaProperties = new Properties();
jpaProperties.put("javax.persistence.transactionType", "resource_local");
jpaProperties.put("hibernate.ogm.datastore.provider", "cassandra_experimental");
jpaProperties.put("hibernate.ogm.datastore.host", "127.0.0.1");
jpaProperties.put("hibernate.ogm.datastore.port", "9042");
jpaProperties.put("hibernate.ogm.datastore.database", "db-name");
jpaProperties.put("hibernate.ogm.datastore.username", "cassandra");
jpaProperties.put("hibernate.ogm.datastore.password", "password");
jpaProperties.put("hibernate.ogm.datastore.create_database", "true");
entityManagerFactoryBean.setPersistenceProviderClass(HibernateOgmPersistence.class);
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
Entity sample
#Entity
#Data
#AllArgsConstructor
#Table(name = "`User`")
public class User extends AbstractTiming{
#Id #GeneratedValue
private Long id;
private String firstName, lastName, description;
public User(){}
}
ADO
#Repository
public interface UserDAO extends JpaRepository<User, Long> {
User findByFirstName(String firstName);
List<User> findByLastName(String lastName);
}
So in the end, is there way to create DataSource to reference Hibernate OGM?
or any other actions to avoid direct dataSource provider. Any help is greatly appreciated!
Hibernate OGM stores the connection in its own class: the org.hibernate.ogm.datastore.spi.DatastoreProvider.
So at start up it does not deal with DataSources, not sure if there is a way to create a DataSource from the cassandra connection and add it in the JNDI for springboot to find it.
The reason for this is that at the moment there is no standard among NoSQL datastores defining the connection details.
Just recently started looking into Spring and specifically its latest features, like Java config etc.
I have this somewhat strange issue:
Java config Snippet:
#Configuration
#ImportResource({"classpath*:application-context.xml","classpath:ApplicationContext_Output.xml"})
#Import(SpringJavaConfig.class)
#ComponentScan(excludeFilters={#ComponentScan.Filter(org.springframework.stereotype.Controller.class)},basePackages = " com.xx.xx.x2.beans")
public class ApplicationContextConfig extends WebMvcConfigurationSupport {
private static final Log log = LogFactory.getLog(ApplicationContextConfig.class);
#Autowired
private Environment env;
#Autowired
private IExtendedDataSourceConfig dsconfig;
#PostConstruct
public void initApp() {
...
}
#Bean(name="transactionManagerOracle")
#Lazy
public DataSourceTransactionManager transactionManagerOracle() {
return new DataSourceTransactionManager(dsconfig.oracleDataSource());
}
IExtendedDataSourceConfig has two implementations which are based on spring active profile one or the other in instantiated. For this example let say this is the implementation :
#Configuration
#PropertySources(value = {
#PropertySource("classpath:MYUI.properties")})
#Profile("dev")
public class MYDataSourceConfig implements IExtendedDataSourceConfig {
private static final Log log = LogFactory.getLog(MYDataSourceConfig.class);
#Resource
#Autowired
private Environment env;
public MYDataSourceConfig() {
log.info("creating dev datasource");
}
#Bean
public DataSource oracleDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUrl(env.getProperty("oracle.url"));
dataSource.setUsername(env.getProperty("oracle.user"));
dataSource.setPassword(env.getProperty("oracle.pass"));
return dataSource;
}
The problem is that when transactionManagerOracle bean is called, (even if I try to mark it as lazy) dsconfig variable value appears to be null.
I guess #beans are processed first and then all Autowires, is there a fix for this? How do I either tell spring to inject dsconfig variable before creating beans, or somehow create #beans after dsconfig is injected?
You can just specify DataSource as method parameter for the transaction manager bean. Spring will then automatically inject the datasource, which is configured in the active profile:
#Bean(name="transactionManagerOracle")
#Lazy
public DataSourceTransactionManager transactionManagerOracle(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
If you still want to do this through the configuration class, specify that as parameter:
public DataSourceTransactionManager transactionManagerOracle(IExtendedDataSourceConfig dsconfig) {}
In both ways you declare a direct dependency to another bean, and Spring will make sure, that the dependent bean exists and will be injected.