In a Spring Boot application I'm trying to setup multiple database connections. I've started building the primary datasource, but I'm getting the following error on the mySqlEntityManagerFactory method.
Could not autowire. no beans of EntityManagerFactoryBuilder
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.PersistenceContext;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
#Configuration
#Transactional
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = "digital.sheppard.dao",
entityManagerFactoryRef = "entityManager",
transactionManagerRef = "transactionManager")
public class PrimaryDBConfig {
#Bean(name="dataSource")
#Primary
#ConfigurationProperties(prefix = "primary.datasource.mysql")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#PersistenceContext(unitName = "primary")
#Primary
#Bean(name = "entityManager")
public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(mysqlDataSource()).persistenceUnit("primary").properties(jpaProperties())
.packages("digital.sheppard.model").build();
}
private Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<String, Object>();
props.put("hibernte.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return props;
}
}
How would I autowire the EntityManagerFactoryBuilder?
I'm trying to follow the code on this blog https://raymondhlee.wordpress.com/2015/10/31/configuring-multiple-jpa-entity-managers-in-spring-boot/
Here's the main application class if it's helpful
#Configuration
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
#ComponentScan
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
For me the reason was a wrong import, so I advise everybody to look at the often overlooked import section of your configuration class. There is a Hibernate class named the same as the springframework one. Of course you should choose the spring one...
I think you should remove this code
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
The exception is due to public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
which expects a bean of EntityManagerFactoryBuilder.
I checked the reference link, I am not sure if that code will work.
Typically, one creates an instance of LocalContainerEntityManagerFactoryBean and initializes it as per need. In your case you can do
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(mysqlDataSource());
em.setPersistenceUnitName("primary");
em.setPackagesToScan(new String[] { "digital.sheppard.model" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(jpaProperties());
The persistence unit name should be same as defined in persistence.xml, though the file is now optional when using Spring JPA.
For a non spring version check out https://stackoverflow.com/a/26814642/776548
Also
since you are initializing EntityManagerFactory by yourself, we will have to exclude DataSourceAutoConfiguration.class.
#Primary is only required if you want multiple datasources. If you have only one, consider removing the annotation, and add it when you need to have multiple data sources
Change parameter name builder to entityManagerFactoryBuilder to inject bean present in JpaBaseConfiguration.class
Have you tried to remove your exclusion of 'DataSourceAutoConfiguration' ?
Using '#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})' prevent a lot of beans from beeing created.
If you got a problem when using a datasource and adding this is your solution, maybe it's not the right one.
Know that spring boot detect the presence of certain classes in the classpath.
If you're using maven, it's reading all classes from all dependencies.
So consider let this DataSourceAutoConfiguration.class running;
cheers
It could be, notice that just could be, your main class is not at the top of the "class tree". Spring needs to scan all classes that are a child (according to package convention) starting from the main class.
Maybe you would read https://www.baeldung.com/spring-component-scanning
If your classes aren't been read by spring scan, they will never be into spring context.
Couple of possibilities :
You need to add the #EnableJpaRepositories(basePackages = {"your.pkg.here"}) to the Application . This tells Spring Data to look for your repository classes under the specified package.
For me , removing #Primary on multiple Data sources, which was added by mistake fixed the issue
it was caused by your ide software,set up these options
Related
For regulatory and security reasons I had to split the logic of my Spring boot application into two tools: One for administration with limited number of tables and one for the "real" user application. Therefore I have two MySQL databases instances on the server version 5.7. While the user tool only accesses one database with dozens of tables, the admin tool needs to access entities in both databases.
The tools are both JavaFX and Spring Boot based. Because of this architectural setup I have three maven packages: One for the admin tool and all the admin related entities, services and alike, one for the user tool and all related entities, services etc. only relevant for this user tool, and the third with all the entities that both tools share.
When I run the user tool it generates the tables in the shared database and uses the hibernate ImprovedNamingStrategy based on the configuration in its application.properties file. Hence the columns have an underscore where appropriate.
In the first place the admin tool wouldn't create any database tables at all using spring.jpa.hibernate.ddl-auto, but I had to use spring.jpa.generate-ddl.
Now, when I run the admin tool I would expect that it only creates the tables in the admin database since this datasource is annotated as #Primary. But it also creates columns in the user database with mixed case. Hence I have columns named e.g. "email_address" and "emailAddress" in user database.
I wonder whether any properties are used with my approach? Any ideas how to do it properly?
Please find following some source..
application.properties :
# Employee database
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.jdbcUrl=jdbc:mysql://127.0.0.1/agiletunesdb?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&characterSetResults=utf-8
spring.datasource.username=YYY
spring.datasource.password=XXXXXX
# Account database
security.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
security.datasource.jdbcUrl=jdbc:mysql://127.0.0.1/authenticationdb?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&characterSetResults=utf-8
security.datasource.username=YYY
security.datasource.password=XXXXXX
# create db schema, values are none, validate, update, create, and create-drop.
#spring.jpa.hibernate.ddl-auto=create
spring.jpa.hibernate.ddl-auto=update
#spring.jpa.hibernate.ddl-auto=none
spring.jpa.generate-ddl=true
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
Databases Configurations :
import javax.sql.DataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
#Configuration
public class MultipleDbConfiguration {
/*
* Persistence of admin database
*/
#Bean(name = "securityDB")
#Primary
#ConfigurationProperties(prefix="security.datasource")
public DataSource securityDataSource() {
return DataSourceBuilder.create().build();
}
/*
*
* Persistence of user database
*/
#Bean(name = "organizationDB")
#ConfigurationProperties(prefix="spring.datasource")
public DataSource organizationDataSource() {
return DataSourceBuilder.create().build();
}
}
The user database configuration
import java.util.HashMap;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "organizationEntityManagerFactory",
transactionManagerRef = "organizationTransactionManager",
basePackages = "com.agiletunes.domain.organization"
)
#EnableTransactionManagement
#PropertySources({ #PropertySource("classpath:application.properties") })
public class OrganizationConfig {
#Autowired
private Environment env; // Contains Properties Load by #PropertySources
#Bean(name = "organizationEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean organizationEntityManagerFactory(
EntityManagerFactoryBuilder builder, #Qualifier("organizationDB") DataSource dataSource) {
HashMap<String, Object> properties = new HashMap<>();
properties.put("spring.jpa.properties.hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect"));
properties.put("spring.jpa.hibernate.ddl-auto", env.getProperty("spring.jpa.hibernate.ddl-auto"));
properties.put("spring.jpa.hibernate.naming-strategy", env.getProperty("spring.jpa.hibernate.naming-strategy"));
return builder
.dataSource(dataSource)
.packages("com.agiletunes.domain.organization")
.persistenceUnit("organizationPU")
.properties(properties)
.build();
}
#Bean(name="organizationTransactionManager")
public PlatformTransactionManager secondTransactionManager(#Qualifier("organizationEntityManagerFactory")
EntityManagerFactory organizationEntityManagerFactory) {
return new JpaTransactionManager(organizationEntityManagerFactory);
}
}
The trick was to use configuration classes which would have a
#PropertySources({ #PropertySource("classpath:application.properties") })
annotation. Then, in the method which creates the LocalContainerEntityManagerFactoryBean, you can pull and set values defined in your application.properties file with
properties.put("spring.jpa.hibernate.naming.physical-strategy", env.getProperty("spring.jpa.hibernate.naming.physical-strategy"));
My sample project is Maven based structure, all my application proeprties files under src/main/resources folder. Below is the complete sample code. I am not understanding why my code not able to find profiles properly unless I use #PropertySource annotation.
My actual doubt is: I've configured spring properties pretty well in application.properties file, but yet why it cannot find profile and their respective property files? Unless I am using #PropertySource annotation, iam not getting value for env.getProperty("mysql.url"). I mean Environment class not able to pick up values from profiles property files. WHY?
Iam getting error as below:
Jul 08, 2017 7:54:26 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext#300ffa5d: startup date [Sat Jul 08 19:54:26 IST 2017]; root of context hierarchy
helloBean
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'datasource' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1207)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1084)
at com.oreilly.datasource.Main2.main(Main2.java:15)
DatasourceConfig.java
package com.oreilly.datasource;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;
#Configuration
/*#PropertySource("classpath:/application.properties")
#PropertySource("classpath:/dev/application-dev.properties")
#PropertySource("classpath:/prod/application-prod.properties")*/
public class DatasourceConfig {
#Autowired
private Environment env;
#Bean(name="helloBean")
public String helloWorld() {
System.out.println("helloBean");
return "helloWorld....";
}
#Bean(name="datasource")
#Profile("dev")
public DataSource datasourceForDev(){
BasicDataSource dataSource = new BasicDataSource();
System.out.println(env.getProperty("mysql.url"));
return dataSource;
}
#Bean(name="datasource")
#Profile("prod")
public DataSource datasourceForProd(){
BasicDataSource dataSource = new BasicDataSource();
System.out.println(env.getProperty("mysql.url"));
return dataSource;
}
}
Main2.java
package com.oreilly.datasource;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DatasourceConfig.class);
DataSource dataSource = context.getBean("datasource", DataSource.class);
String helloBean = context.getBean("helloBean", String.class);
}
}
application.properties
spring.profiles.active=prod
spring.config.name=application
spring.config.location=classpath:/application.properties,classpath:/dev/application-dev.properties,classpath:/prod/application-dev.properties
Below is the project folder structure:
Please tell me what went wrong?
Spring is smart, it chooses the application-x.properties (where x is the environment) depending of the value assigned to spring.profiles.active in the application.properties, so you don't have to worry about register all the files in different #PropertySource annotations.
You can get more info here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties
I suggest you remove all the #Profile annotations and let just one datasource that will be variable (depending on the seleced environment from application.properties). You may understand this with the example that i put at the end of this post.
If you want to define a mysql.url for a particular profile (lets say dev), you need to add the "mysql.url" in the application-dev.properties file, and then set the spring.profiles.active value to dev in application.properties.
Then, in your DatasourceConfig.java, you can perform something like this:
#Autowired
private Environment env;
//Takes the mysqlUrl from application-x.properties (where x is the value of spring.profiles.active that comes from application.properties)
#Value("${mysql.url}")
private String mysqlUrl;
#Bean(name="helloBean")...
#Bean(name="datasource")
public DataSource datasource() {
BasicDataSource dataSource = new BasicDataSource();
System.out.println(mySqlUrl); //This value is variable depending of the profile that you're pointing on.
return dataSource;
}
Please let me know it this is useful for you.
I have resolved my issue just by modifying as below:
#PropertySource("classpath:/${spring.profiles.active}/application-${spring.profiles.active}.properties")
Now I am able to pickup application-dev.properties (or) application-prod.properties dynamically.
Note: Environment class requires #PropertySource annotation, otherwise we get null for env.get('someproperty').
#Configuration
#PropertySource("classpath:application.properties")
public class DatasourceConfig {
....
}
put property file in same location as application.property and follow the naming convention application-{profile}.properties like application-dev.properties, application-prod.properties.
'I want properties file to be picked up automatically based upon profile I declared in application.properties.'
:
Run you application with -Dspring.profiles.active=dev/prod . Spring load 1)application.property , 2)pplication-dev/prod.properties file with overides value from application.property
I am studying for Spring Core certification and I am finding some doubts related to how Spring handle transaction.
So I have an exercise in which I find something like this.
In a class named rewards.internal.RewardNetworkImpl I have this method:
#Transactional
public RewardConfirmation rewardAccountFor(Dining dining) {
Account account = accountRepository.findByCreditCard(dining.getCreditCardNumber());
Restaurant restaurant = restaurantRepository.findByMerchantNumber(dining.getMerchantNumber());
MonetaryAmount amount = restaurant.calculateBenefitFor(account, dining);
AccountContribution contribution = account.makeContribution(amount);
accountRepository.updateBeneficiaries(account);
return rewardRepository.confirmReward(contribution, dining);
}
So this method perform some operation on some services classes (that substantially execute some queries on the DB). I want that all these operations are executed into a single transaction (that guaranteed the ACID properties) so I annotate this method with the #Transactional annotation. Ok, I think that this is pretty clear for me.
My Spring project is splitted into 2 main folder (2 directory I think) that contains the same packages). These folder are named respectively src/test/java and src/main/java. The first one contains the classes related to the JUnit test and the second one contains classes related to the proper project that implements the application), this is a screenshot of how is organized my project:
Ok, now in the exercise it say tha I have to define a bean named transactionManager that configures a DataSourceTransactionManager adding this method to the Spring configuration class named rewards.SystemTestConfig that is situated into the src/test/java folder:
#Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
So it is pretty clear for me that this method configure a bean named transactionManager implemented by a DataSourceTransactionManager that use a DataSource object.
My doubt is related to the place where this configuration is putted. Infact it is putted inside the rewards.SystemTestConfig inside the src/test/java folder (the folder that contains the JUnit stuff.
So my doubts are: is this configuration related only for the test environment or is it global to my entire project? (the transactionManager works only for the test environment or is it used also into my concrete application?)
I have another configuration class into the src/main/java folder, this one:
package config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import rewards.RewardNetwork;
import rewards.internal.RewardNetworkImpl;
import rewards.internal.account.AccountRepository;
import rewards.internal.account.JdbcAccountRepository;
import rewards.internal.restaurant.JdbcRestaurantRepository;
import rewards.internal.restaurant.RestaurantRepository;
import rewards.internal.reward.JdbcRewardRepository;
import rewards.internal.reward.RewardRepository;
// TODO-03: Add an annotation to instruct the container to look for the
// #Transactional annotation. Save all work, run RewardNetworkTests. It should pass.
#Configuration
#EnableTransactionManagement
public class RewardsConfig {
#Autowired
DataSource dataSource;
#Bean
public RewardNetwork rewardNetwork(){
return new RewardNetworkImpl(
accountRepository(),
restaurantRepository(),
rewardRepository());
}
#Bean
public AccountRepository accountRepository(){
JdbcAccountRepository repository = new JdbcAccountRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RestaurantRepository restaurantRepository(){
JdbcRestaurantRepository repository = new JdbcRestaurantRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RewardRepository rewardRepository(){
JdbcRewardRepository repository = new JdbcRewardRepository();
repository.setDataSource(dataSource);
return repository;
}
}
As you can see it don't contains an explicit configuration of the transactionManager bean but it is annoted with #EnableTransactionManagement annotation. I know that this annotation defines a Bean Post-Processor that proxies #Transactional beans and so happen:
Target object wrapped in a proxy (using an an Around advice)
Proxy implements the following behavior: Transaction started before entering the method AND Commit at the end of the method AND Rollback if method throws a RuntimeException.
But I can't understand why in the rewards.SystemTestConfig configuration class of the test environment it is explicitly declared the transactionManager bean and in the RewardsConfig class (the class that configure the proper application) it is not declared the transactionManager (I think that this bean is absolutly needed to operate with transaction) but in its place there is the #EnableTransactionManagement annotation.
So my doubts are:
1) Declaring the transactionManager bean into rewards.SystemTestConfig configuration class of the test environment it is global and it work also for the proper application folder (but I don't think).
2) Noting with #EnableTransactionManagement annotation the rewards.SystemTestConfig (that configure the proper application) it is automatically created a transaction manager. But what kind? and especially how do you know which datasource should use?
Can you help me to understand how it work? What am I missing?
I'm new to Java and Spring, coming from C# and the .NET world, so bear with me - what I am attempting to do may be off the mark...
I am attempting to configure Spring DI using Java configuration and annotations, not XML configuration, however I am having a few issues. This is for a standalone application, not a web app. I have worked through the springsource documentationand as far as I can tell my very basic configuration should be correct...but isn't. Please take a look at the code below:
Java Configuration Annotated Class:
package birdalerter.common;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import birdalerter.process.ISightingsProcessor;
import birdalerter.process.SightingsProcessor;
#Configuration
#ComponentScan({"birdalerter.process", "birdalerter.common"})
public class AppConfig {
#Bean
#Scope("prototype")
public ISightingsProcessor sightingsProcessor(){
return new SightingsProcessor();
}
}
Configure Component implementing the ISightingsProcessor interface:
package birdalerter.process;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import org.springframework.stereotype.Component;
import birdalerter.domainobjects.IBirdSighting;
#Component
public class SightingsProcessor implements ISightingsProcessor{
private LinkedBlockingQueue<IBirdSighting> queue;
private List<ISightingVisitor> sightingVisitors = new ArrayList<ISightingVisitor>();
public SightingsProcessor(){
}
...
}
Configure Factory Component:
package birdalerter.process;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
public class ProcessorFactory {
private ISightingsProcessor sightingsProcessor;
#Autowired
#Required
private void setSightingsProcessor(ISightingsProcessor sightingsProcessor){
this.sightingsProcessor = sightingsProcessor;
}
public ISightingsProcessor getSightingsProcessor(){
return this.sightingsProcessor;
}
}
Wire up the AnnotationConfigApplicationContext and test:
#Test
public void testProcessingDI(){
#SuppressWarnings("resource")
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
context.refresh();
ISightingsProcessor processor = new ProcessorFactory().getSightingsProcessor();
System.out.println(processor);
Assert.assertTrue(processor != null);
}
The SightingsProcessor is not being setter injected and the assert is failing as the returned object is null. Hopefully I have missed something very obvious.
Thanks in advance.
Edited in Response to Meriton:
Thanks for the answer Meriton.
Why would Spring not know about the newly created object? Does Spring not maintain dependencies throughout the application lifecycle and inject as appropriate when new objects are created that are configured as beans?
I don't want to directly use context.getBean(ISightingsProcessor.class) if I can help it to be honest, I would like the dependency injected in the setter method without having manual intervention - it just seems cleaner.
I am using the ProcessorFactory as the ISightingsProcessor interface extends Runnable - the implementing object is to be started as a thread. The application will be configurable to have n* threads, with each being started within a loop iteration. I don't think it is possible (I may be wrong, please advise if so) to have #Autowired annotations within method declarations, hence I use the factory to supply a new instance of the injected ISightingsProcessor concrete class.
Yes I've just had a look regarding the #Scope annotation - you are right, that needs moving to the AppConfig #Bean declaration (which I've done in this edit), thanks for that.
ISightingsProcessor processor = new ProcessorFactory().getSightingsProcessor();
This calls the constructor of ProcessorFactory, and then the getter of the instance the constructor created. Spring can not know about that newly created object, and therefore not inject its dependencies. You should ask Spring for the ProcessorFactory instead, for instance with
ProcessorFactory pf = context.getBean(ProcessorFactory.class);
ISightingsProcessor processor = pf.getSightingsProcessor();
That said, I don't know why you need class ProcessorFactory at all. You might just as well get the ISightingsProcessor directly:
ISightingsProcessor processor = context.getBean(ISightingsProcessor.class);
Additionally, "Java Based Configuration" and component scanning are independent ways to declare beans. Currently, you are therefore declaring the ISightingsProcessor twice: Once with the #Bean-annotated factory method, and once with the component scan and the #Component annotation on the class. Doing either of that will do. In fact, doing both might cause one bean definition to override the other.
Oh, and the #Scope annotation is for bean definitions (those you annotate with #Bean or #Component). It will likely be ignored on injection points (#Autowired).
I am using #Configuration annotation for configuration of spring instead of xml file. I am configuring 2 datasources with different session factory and different transaction managers. I am stuck with a problem here for #EnableTransactionManagement annotation. I read in its documentation that,
#EnableTransactionManagement is more flexible; it will fall back to a
by-type lookup for any PlatformTransactionManager bean in the
container. Thus the name can be "txManager", "transactionManager", or
"tm": it simply does not matter.
This means whatever name I give to method, it will always search for the method which returns PlatformTransactionManager object while I have 2 transactionmanagers. Now the problem is, when I test this class, it gives me error:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single bean but found 2
I even tried to have 2 different Configuration classes but in vain. In xml configuration, this was not the case. I registered my both transaction managers with two <tx:annotation-driven transaction-manager="" /> tag and it worked fine. But not able to do same here with annotations.
What should I do if I want to configure 2 datasources with 2 different transaction managers in Spring annotated configuration class?
In your configuration class, use #EnableTransactionManagement annotation.
Define a transaction manager in this class as:
#Bean(name="txName")
public HibernateTransactionManager txName() throws IOException{
HibernateTransactionManager txName= new HibernateTransactionManager();
txName.setSessionFactory(...);
txName.setDataSource(...);
return txName;
}
There on, in your class/method that executes transactional job(s), annotate as follows:
#Transactional("txName")
or
#Transactional(value = "txName")
This is how you would tie a name qualified transaction manager to wherever you need it. You can now have as many transaction managers as you want and use it accordingly wherever you need.
Just in case anyone runs into this problem, I found a solution:
#Configuration
#EnableTransactionManagement
#DependsOn("myTxManager")
#ImportResource("classpath:applicationContext.xml")
public class AppConfig implements TransactionManagementConfigurer {
#Autowired
private PlatformTransactionManager myTxManager;
...
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return this.myTxManager;
}
In this way, you can use a specific txManager defined in an xml configuration.
In case you want to define the txManager used on service-level, you shall remove the #EnableTransactionManagement annotation from the #Configuration class and specify the txManager in the #Transactional annotations, e.g.
#Service
#Transactional(value="myTxManager", readOnly = true)
public class MyServiceImpl implements MyService { ... }
From the java doc
For those that wish to establish a more direct relationship between
#EnableTransactionManagement and the exact transaction manager bean to be used, the
TransactionManagementConfigurer callback interface may be implemented - notice the
implements clause and the #Override-annotated method below:
Your #Configuration class needs to implement TransactionManagementConfigurer interface - implement the annotationDrivenTransactionManager which will return the reference to the transactionManager that should be used.
I am not sure why you are using two TransactionManagers . You could consider using the same TransactionManager for multiple datasource via the AbstractRoutingDataSource . Please refer
http://blog.springsource.org/2007/01/23/dynamic-datasource-routing/
for a sample on its usage.
I have to use JPA and Reactive Mongo in one project. What works at last was:
create a #Configuraition class to explicitly create a JPA transaction manager, like here:
private Environment env;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean dbEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dbDatasource());
em.setPackagesToScan(new String[]{"projectone.mysql"});
em.setPersistenceUnitName("dbEntityManager");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.dialect",env.getProperty("hibernate.dialect"));
properties.put("hibernate.show-sql",env.getProperty("jdbc.show-sql"));
em.setJpaPropertyMap(properties);
return em;
}
#Primary
#Bean
public DataSource dbDatasource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("spring.datasource.driverClassName"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
#Primary
#Bean
public PlatformTransactionManager jpaTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
dbEntityManager().getObject());
return transactionManager;
}
}
Notice that the bean name jpaTransactionManager, which would be the txManager name used in JPA #Transactional.
create MongoConfiguration to explicitly create a Mongo transaction manager(a lot of beans to define)
in #Transactional, call them with name. The default one transactionManger will not work. You have to distinguish, like jpaTransactionManager and reactiveMongoTransactionManger.
#Transactional(value="jpaTransactionManager")
public void xxx() {
...
}
Note that JPA transaction methods cannot Reactor types as return value(Mono/Flux). Spring will force methods returning Mono/Flux to use ReactiveTransactionManager, it will cause confusion.
Some of the other answers imply that using two transaction managers is in some way wrong; however, Spring's XML configuration allows for using multiple transaction managers as stated in the online documentation (below). Unfortunately, there does not seem to be a way to make the #EnableTransactionManagement annotation work in a similar manner. As a result, I simply use an #ImportResource annotation to load an XML file that includes the <tx:annotation-driven/> line. This allows you to get a Java configuration for most things but still make use of #Transactional with an optional Transaction Manager qualifier.
http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/transaction.html
Most Spring applications only need a single transaction manager, but there may be situations where you want multiple independent transaction managers in a single application. The value attribute of the #Transactional annotation can be used to optionally specify the identity of the PlatformTransactionManager to be used. This can either be the bean name or the qualifier value of the transaction manager bean. For example, using the qualifier notation, the following Java code
Try to use chained TransactionalManager
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
#Configuration
public class ChainedDBConfig {
#Bean("chainedTransactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("database1TransactionManager") final PlatformTransactionManager db1PlatformTransactionManager,
#Qualifier("database2TransactionManager") final PlatformTransactionManager db2PlatformTransactionManager) {
return new ChainedTransactionManager(db1PlatformTransactionManager, db2PlatformTransactionManager);
}
}
And place the following annotation on your service class:
#Transactional(transactionManager = "chainedTransactionManager")
public class AggregateMessagesJobIntegrationTest {
...
}
You can also use it inside the integration tests:
#RunWith(SpringRunner.class)
#Transactional(transactionManager = "chainedRawAndAggregatedTransactionManager")
#Rollback
public class ExampleIntegrationTest extends AbstractIntegrationTest {
....
}
and it will do a rollback for both DB transaction managers.