I'm migrating a web application built with Spring 3 to a Spring 4 Boot based app.
I'd like to replace all xml and properties with a #Configuration class.
Original spring xml config
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="configLocation" value="classpath:quartz.properties"/>
<property name="applicationContextSchedulerContextKey">
<value>applicationContext</value>
</property>
</bean>
Original quartz.properties
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.dataSource=psqldatasource
org.quartz.dataSource.psqldatasource.driver=${db.driver}
org.quartz.dataSource.psqldatasource.URL=${db.url}
org.quartz.dataSource.psqldatasource.user=${db.usr}
org.quartz.dataSource.psqldatasource.password=${db.pwd}
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
I've managed to replace spring xml with the following code, but it still use an external quartz.properties to configure most of the quartz features, including datasource.
#Bean
public Scheduler configureScheduler() throws SchedulerException {
StdSchedulerFactory f = new StdSchedulerFactory();
f.initialize(this.getClass().getClassLoader().getResourceAsStream("quartz.properties"));
return f.getScheduler();
}
Note that this leads to the following output during app boot :
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.
Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is not clustered.
I've managed to achieve a full Java Spring configuration using the following code :
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan("com.myapp")
public class WebAppConfig extends WebMvcConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private ApplicationContext applicationContext;
#Bean
public SchedulerFactoryBean configureScheduler() {
SchedulerFactoryBean f = new SchedulerFactoryBean();
f.setDataSource(dataSource);
f.setJobFactory(new SpringBeanJobFactory());
f.setAutoStartup(false);
Properties properties = new Properties();
properties.setProperty("org.quartz.threadPool.threadCount", "3");
properties.setProperty("org.quartz.jobStore.useProperties", "false");
properties.setProperty("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
f.setQuartzProperties(properties);
f.setApplicationContext(applicationContext);
f.setApplicationContextSchedulerContextKey("applicationContext");
return f;
}
}
This produce the following output during app boot
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
Using job-store 'org.springframework.scheduling.quartz.LocalDataSourceJobStore' - which supports persistence. and is not clustered.
To my purpose this is satisfying ; although i'm not sure with the different job-store implementation (was JobStoreTX and now LocalDataSourceJobStore). I'm wondering this is related to the use of the spring autowired datasource.
Related
I have 3 DataSource beans
<bean id="dataSource1" name="dataSource1" autowire="byName" .....>
<bean id="dataSource2" name="dataSource2" autowire="byName" .....>
<bean id="dataSource3" name="dataSource3" autowire="byName" .....>
I have set autowire "byName" in the beans in xml
I am creating 3 JdbcTemplates
#Autowired
#Qualifier("dataSource1")
BasicDataSource dataSource1;
#Autowired
#Qualifier("dataSource2")
BasicDataSource dataSource2;
#Autowired
#Qualifier("dataSource3")
BasicDataSource dataSource3;
#Autowired
#Bean(name="jdbcTemplate1")
public JdbcTemplate createJdbcTemplate(){
return new JdbcTemplate((DateSource)dataSource1)
}
#Autowired
#Bean(name="jdbcTemplate2")
public JdbcTemplate createJdbcTemplate(){
return new JdbcTemplate((DateSource)dataSource2)
}
#Autowired
#Bean(name="jdbcTemplate3")
public JdbcTemplate createJdbcTemplate(){
return new JdbcTemplate((DateSource)dataSource3)
}
StackTrace :
Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2017-11-16 23:50:04.562 ERROR 15180 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
APPLICATION FAILED TO START
Description:
Field dataSource1 in com.MyClass required a single bean, but 3 were found:
- dataSource1: defined in class path resource [datasource.xml]
- dataSource2: defined in class path resource [datasource.xml]
- dataSource3: defined in class path resource [datasource.xml]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
When I add EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) everything works fine.
Can anyone explain why do I need to exclude the AutoConfiguration despite autowiring-byName.
You don't need to ignore the auto-configuration, you just need to mark one of the datasources as #Primary, as auto-configurations might try to get one by type. This leads to the error you have.
Also, feel free to check the dedicated section in the reference documentation about dealing with multiple datasources; there are better ways to configure multiple datasources and still leverage Spring Boot features and infrastructure.
I have a project with XML based spring configuration and want to define some new beans but in java class based configuration.
How to implement this so that i can also refer beans of java configuration in my XML config file.
You can import your xml config into java configuration, like so:
#Configuration
#ImportResource("classpath:pl/rav/springtest/resources/app.xml")
public class AppConfig {
#Bean(name="myMessageService")
MessageService mockMessageService() {
return new MessageServiceImpl();
}
}
When you want to refer from xml to the bean you just point to its name:
<property name="msgSrv">
<ref bean="myMessageService"/>
</property>
Then use ApplicationContext based on your java configuration.
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
As you started with xml config, you may be interested in other way around (importing java config into xml config) which I think was explained here
I am working in spring hibernate project where I am connecting to multiple database. These database details are loaded in a properties file and is imported into hibernate.xml file. Each key was mapped as below.
dbName= dbHost
Mapping as below :
<bean id="dbId" parent="abstractDataSource">
<property name="url" value="${dbName}" />
</bean>
But now i need to include a condition such that the property key to be mapped to multiple values like below
E.g. dbName= statusFlag,dbHost,dbBackupHostName
I need to modify the hibernate configuration file such that using the keyName, corresponding values should be retrieved and splitted with comma separated.
Then I need to check for the statusFlag. If it is true, then i should set the dbValue to 'dbHost', else if the flag is false, it should be set to 'dbBackupHostName'.
Please help me how to implement this condition check in hibernate configuration file.
Also please let me know if this approach is fine to proceed or am i making it too difficult to implement?
Waiting for some valueable answers..
~~~Suriya
To use complex logic in bean wiring and creation in spring you can use Java Configuration
It should work for Spring 3. Sample pseudo code is below.
#Configuration
public class MyCustomConfig {
#Value
String statusFlag;
#Value
String dbHost;
#Value
String dbBackupHostName;
#Bean
public BasicDataSource datasource () {
BasicDataSource datasource = new BasicDataSource();
//Do some custom processing to create datasource
...
return datasource;//return after creation
}
}
Use PropertyPlaceholderConfigurer to load properties as #PropertySource is not available in 3.0.5.
If you can change the spring version to 3.1 which shouldn't make much difference then you can use #Profile along with #PropertySource.
If the logic is not too complex and you can separate set of properties that have to be active at a time. (Local db, Dev db, Prod db, Custom etc.) Then you can try using #Profile.
#Profile annotated bean is created only if that profile is active. A profile can be activated by setting spring.profiles.active.
So to activate the Dev profile we can set in the properties files
spring.profiles.active=dev
//activating 2 profiles
spring.profiles.active=dev,mvc
Below is the sample pseudo code.
#Configuration
#PropertySource("bootstrap.properties")//set spring.profiles.active in it
public class MyCustomConfig {
#Profile("profile1")
public BasicDataSource datasource1 () {
//config using ${profile1.dbName} etc
...
}
#Profile("profile2")
public BasicDataSource datasource2 () {
//config using ${profile2.dbName} etc
...
}
#Profile("profile3")
public BasicDataSource datasource3 () {
//config using ${profile3.dbName} etc
...
}
}
I have spring based multimodule application. And in my DAO module the DB (embedded derby) is started and created by the class the implements ApplicationListener.
Problem that in the logs the huge stacktrace from Spring which say that there is no db(couldn't get connection).
Still, my application works without any problems. This stacktrace appeared before the ApplicationListener invoked and the db is created. Actually, I see it only when I am starting the application the first time on the machine, because the db created only this time, than it just used.
So my question is whow to avoid this exception in logs? Maybe there is spring or hibenate setup not connect to the db before the application context fully loaded? Or invoke the code that creates db by some other listener?
Well here is the way I do : the ROOT context contains the datasource, the dao, the service and the transaction manager. In XML config, the declaration of the database is :
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:url="jdbc:derby:/path/to/database;create=TRUE"
p:username="user" p:password="pwd"
p:driverClassName="org.apache.derby.jdbc.EmbeddedDriver"/>
it can then be used to declare a session factory for hibernate and an associated DAO as :
<bean class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
id="sessionFactory" p:dataSource-ref="datasource">
<!-- hibernate config -->
...
</bean>
<bean class="org.springframework.orm.hibernate4.HibernateTransactionManager"
name="transactionManager" p:sessionFactory-ref="sessionFactory"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="myDao" class="... .myDaoImpl" p:sessionFactory-ref="sessionFactory" .../>
That way all is created by spring, that ensures that the creation order is correct. Of course the same is possible in Java config with the same logic.
I suppose you are fetching some data from database from inside spring beans that are being created. Perhaps thru #PostConstruct or other way. Remember that until spring context is fully loaded some beans can have injected uninitialized beans.
So do not use DB, do not call any DAOs until you are sure that spring context is fully initialized.
To do such initial calls to DAOs try such patter that guarantees spring context completness:
#Component
public class SpringContextMonitor implements ApplicationListener<ApplicationEvent> {
#Autowired
private SomeDao dao;
...
#Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
onStart((ContextRefreshedEvent) event);
}
}
private void onStart(ContextRefreshedEvent event) {
// do your initialization here
dao.getSomething();
dao2.getSomething();
...
}
...
}
The onStart method in above example is place where you are sure that all beans are fully initialized
I have two datasources and two entityManagerFactory instance. I was trying to use the new feature of 3.1 (Bootstrapping JPA entityManagerFactory without persistence.xml by using packagesToScan property).
In order to use the right entity manager factory instance, i have to distinguish using Persistence unit name and defining the PU name in persistence.xml is stopping the spring package scanning feature.
How to give the PU name while using packagesToScan feature?
My question is more duplicate of Is there a way to give persistenceUnitName for Spring's LocalContainerEntityManagerFactoryBean without persistence.xml?
I couldn't find the answer or comment on the above post. So reposting as new question.
Yes you can. Here's an example that uses annotation configuration for Spring
I found it best to organise each datasource into a different package.
My package structure is:
datasource
|__ converters <-- holds any custom attribute converters for JPA
|__ default <-- for default datasource
| |__ model <-- contains entities for default datasource
| |__ repository <-- contains repositories for default datasource
|__ anotherdatasource <-- for second datasource
|__ model <-- contains entities for second datasource
|__ repository <-- contains repositories for second datasource
Pick one of the datasources as the default and create a configuration class for it along the lines of...
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", basePackages = { "com.example.datasource.default.repository" })
public class JpaDefaultDatasourceConfig {
#Primary
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, #Qualifier("dataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("com.example.datasource.default.model", "com.example.datasource.converters").persistenceUnit("mydefault").build();
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(#Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Then for each subsequent datasource create another configuration class along the lines of... (Note: the use of packages to separate entity/repository scanning and the naming convention used throughout)
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "anotherEntityManagerFactory", transactionManagerRef = "anotherTransactionManager", basePackages = { "com.example.datasource.anotherdatasource.repository" })
public class JpaAnotherDatasourceConfig {
#Bean(name = "anotherDataSource")
#ConfigurationProperties(prefix = "another.datasource")
public DataSource anotherDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "anotherEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean anotherEntityManagerFactory(EntityManagerFactoryBuilder builder, #Qualifier("anotherDataSource") DataSource anotherDataSource) {
return builder.dataSource(anotherDataSource).packages("com.example.datasource.anotherdatasource.model", "com.example.datasource.converters").persistenceUnit("anotherName").build();
}
#Bean(name = "anotherTransactionManager")
public PlatformTransactionManager anotherTransactionManager(#Qualifier("anotherEntityManagerFactory") EntityManagerFactory anotherEntityManagerFactory) {
return new JpaTransactionManager(anotherEntityManagerFactory);
}
}
You can configure each datasource using the configuration prefix. For the two examples above you could configure them using
application.yml
## JPA configuration
# This is the configuration for default datasource created by spring
spring:
datasource:
url: jdbc:mysql://localhost/default
username: foo
password: bar
driverClassName: com.mysql.jdbc.Driver
test-on-borrow: true
test-while-idle: true
validation-query: select 1;
# maxActive: 1
# This is the configuration for an additional datasource which we will create ourselves
another:
datasource:
url: jdbc:mysql://localhost/another
username: foo
password: bar
driverClassName: com.mysql.jdbc.Driver
test-on-borrow: true
test-while-idle: true
validation-query: select 1;
You don't necessarily need to worry now about the names of the persistence units (although we did name them) because we've carefully separated entity managers to only look at their entities/repositories you can simply inject the applicable repository and use it without having to worry about it getting the wrong datasource. If you do need the persistence unit you can just ask for it by name #PersistenceUnit(name = "anotherDatasource")
If I understand your question correctly, you would like to set the name of the persistenceUnit backing an EntityManagerFactory, when defined without a persistence.xml?
When you declare the entityManagerFactory, there is a persistenceUnitName property that you can set. For example:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
<property name="dataSource" ref="dataSource"/>
<property name="persistenceUnitName" value="yourPersistenceUnitName"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="packagesToScan">
<list>
<value>..</value>
...
</list>
</property>
</bean>