I got Spring Java Config style configuration with 2 datasources:
#Configuration
#EnableTransactionManagement
public class DBConfig {
// main db
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:schema.sql")
.addScript("classpath:data.sql")
.build();
}
//db for test
#Bean(name = "testDataSource")
public DataSource testDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
#Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
But when I autowire that datasources in my Test class and run him: i got the same result:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = DBConfig.class)
#Transactional
public class JdbcTest {
#Autowired
private JdbcTemplate jdbcTemplate;
#Autowired
#Qualifier("testDataSource")
private DataSource testDataSource;
#Test
public void findRealDb() {
String rowCount = jdbcTemplate.queryForObject("select message from messages", String.class);
System.out.println("real db " + rowCount);
}
#Test
public void findTestDb() {
String rowCount = (new JdbcTemplate(testDataSource).queryForObject("select message from messages", String.class));
System.out.println("test db " + rowCount);
}
}
So as result method findTestDb() logs same rowCount string as findRealDb() , but as you see them use different datasources to build jdbcTemplate.
The test code will be autowiring by type. I'm surprised you don't get a non-unique bean exception.
The trouble is you have two beans, one of which is qualified and one of which isn't.
You are much better off using Spring profiles and assigning the test datasource to a test profile and the production datasource to a default profile, then setting the active profile for your test case to be test.
Here's an example:
http://fahdshariff.blogspot.co.uk/2012/09/spring-3-javaconfig-unit-testing-using.html
note that you can put the #Profile annotation on an individual bean or a configuration class.
Related
I have two databases I need instances of at the same time, they are running on a oracle weblogic server rather than locally. In a configurator class, I have specified the sources and tested the connection and both DBs pull data properly. (When used one at a time, or with one as #Primary)
#Configuration
public class appDatabaseConfiguration {
#Bean
#Primary
public DataSource dataSourceA() throws Exception{
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
JndiTemplate corpdwJndiTemplate = new JndiTemplate();
return dataSourceLookup.getDataSource("<DBAProperties>");
}
#Bean
public DataSource dataSourceB() throws Exception{
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
JndiTemplate jndiTemplate = new JndiTemplate();
return dataSourceLookup.getDataSource("<DBBProperties>");
}
}
However, I am unable to use #Qualifier to tell the instantiation which bean in the configuration to select-
#Service
public class DaoImpl implements Dao{
#Autowired
#Qualifier("dataSourceA")
private JdbcTemplate dataSourceA;
#Autowired
#Qualifier("dataSourceB")
private JdbcTemplate dataSourceB;
public String getData() {
resultsA = dataSourceA.queryForObject("SELECT COUNT(*) FROM TABLE", String.class);
resultsB = dataSourceB.queryForObject("SELECT COUNT(*) FROM TABLE", String.class);
return resultsA + resultsB;
}
}
When #Qualifier is not used:
DataSourceA Query will succeed, but DataSourceB will fail (Table or View doesn't exist error)- because it is pulling the #Primary bean for both instantiations
When #Qualifier is used:
Application errors with-
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
'org.springframework.jdbc.core.JdbcTemplate' available: expected at least 1 bean which
qualifies as autowire candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=dataSourceA)}
I have tried adding specific names to the bean like #Bean("dataSourceA"), #Bean(name="dataSourceB") rather than relying on the function name and several other syntax changes but no results. Does anyone have some insight here?
Note: Even when one datasource bean is commented out, and the other has only a name and not #primary it still throws the same autowire bean error, so I don't believe that the annotation is functioning properly in my project (or I'm using it wrong).
You'll have to create two JdbcTemplates each configured with a different datasource and autowire with the qualifier of the template, for example with
#Bean
#Primary
public JdbcTemplate jdbcTemp1(#Qualifier("dataSourceA") DataSource ds) {
return new JdbcTemplate(ds);
}
#Bean
public JdbcTemplate jdbcTemp2(#Qualifier("dataSourceB") DataSource ds) {
return new JdbcTemplate(ds);
}
...
#Autowired
#Qualifier("jdbcTemp2")
private JdbcTemplate jdbcTemplate;
It's because the type of your beans defined in the #Configuration is DataSource and in your #Service you inject beans of type JdbcTemplate.
To make it work, you should create two more beans of type JdbcTemplate using your two DataSource like this :
#Configuration
public class appDatabaseConfiguration {
#Bean
#Primary
public DataSource dataSourceA() throws Exception{
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
JndiTemplate corpdwJndiTemplate = new JndiTemplate();
return dataSourceLookup.getDataSource("<DBAProperties>");
}
#Bean
public DataSource dataSourceB() throws Exception{
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
JndiTemplate jndiTemplate = new JndiTemplate();
return dataSourceLookup.getDataSource("<DBBProperties>");
}
#Bean(name="jdbcTemplateA")
public JdbcTemplate jdbcTemplateA() throws Exception {
return new JdbcTemplate(dataSourceA());
}
#Bean(name="jdbcTemplateB")
public JdbcTemplate jdbcTemplateB() throws Exception {
return new JdbcTemplate(dataSourceB());
}
}
And then, inject your beans using #Qualifier("jdbcTemplateA") or #Qualifier("jdbcTemplateB")
I am facing some issues while writing integration tests for Spring Batch jobs. The main problem is that an exception is thrown whenever a transaction is started inside the batch job.
Well, first things first. Imagine this is the step of a simple job. A Tasklet for the sake of simplicity. Of course, it is used in a proper batch config (MyBatchConfig) which I also omit for brevity.
#Component
public class SimpleTask implements Tasklet {
private final MyRepository myRepository;
public SimpleTask(MyRepository myRepository) {
this.myRepository = myRepository;
}
#Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
myRepository.deleteAll(); // or maybe saveAll() or some other #Transactional method
return RepeatStatus.FINISHED;
}
}
MyRepository is a very unspecial CrudRepository.
Now, to test that job I use the following test class.
#SpringBatchTest
#EnableAutoConfiguration
#SpringJUnitConfig(classes = {
H2DataSourceConfig.class, // <-- this is a configuration bean for an in-memory testing database
MyBatchConfig.class
})
public class MyBatchJobTest {
#Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
#Autowired
private JobRepositoryTestUtils jobRepositoryTestUtils;
#Autowired
private MyRepository myRepository;
#Test
public void testJob() throws Exception {
var testItems = List.of(
new MyTestItem(1),
new MyTestItem(2),
new MyTestItem(3)
);
myRepository.saveAll(testItems); // <--- works perfectly well
jobLauncherTestUtils.launchJob();
}
}
When it comes to the tasklet execution and more precisely to the deleteAll() method call this exception is fired:
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder#68f48807] for key [org.springframework.jdbc.datasource.DriverManagerDataSource#49a6f486] bound to thread [SimpleAsyncTaskExecutor-1]
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:448)
...
Do you have any ideas why this is happening?
As a workaround I currently mock the repository with #MockBean and back it with an ArrayList but this is not what the inventor intended, I guess.
Any advice?
Kind regards
Update 1.1 (includes solution)
The mentioned data source configuration class is
#Configuration
#EnableJpaRepositories(
basePackages = {"my.project.persistence.repository"},
entityManagerFactoryRef = "myTestEntityManagerFactory",
transactionManagerRef = "myTestTransactionManager"
)
#EnableTransactionManagement
public class H2DataSourceConfig {
#Bean
public DataSource myTestDataSource() {
var dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1");
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean myTestEntityManagerFactory() {
var emFactory = new LocalContainerEntityManagerFactoryBean();
var adapter = new HibernateJpaVendorAdapter();
adapter.setDatabasePlatform("org.hibernate.dialect.H2Dialect");
adapter.setGenerateDdl(true);
emFactory.setDataSource(myTestDataSource());
emFactory.setPackagesToScan("my.project.persistence.model");
emFactory.setJpaVendorAdapter(adapter);
return emFactory;
}
#Bean
public PlatformTransactionManager myTestTransactionManager() {
return new JpaTransactionManager(myTestEntityManagerFactory().getObject());
}
#Bean
public BatchConfigurer testBatchConfigurer() {
return new DefaultBatchConfigurer() {
#Override
public PlatformTransactionManager getTransactionManager() {
return myTestTransactionManager();
}
};
}
}
By default, when you declare a datasource in your application context, Spring Batch will use a DataSourceTransactionManager to drive step transactions, but this transaction manager knows nothing about your JPA context.
If you want to use another transaction manager, you need to override BatchConfigurer#getTransactionManager and return the transaction manager you want to use to drive step transactions. In your case, you are only declaring a transaction manager bean in the application context which is not enough. Here a quick example:
#Bean
public BatchConfigurer batchConfigurer() {
return new DefaultBatchConfigurer() {
#Override
public PlatformTransactionManager getTransactionManager() {
return new JpaTransactionManager(myTestEntityManagerFactory().getObject());
}
};
}
For more details, please refer to the reference documentation.
I am using H2 DB for my JUnit testing and getting this error in my JUNIT test cases -
INFO [extShutdownHook] o.s.j.d.e.H2EmbeddedDatabaseConfigurer o.s.j.d.e.AbstractEmbeddedDatabaseConfigurer.shutdown(AbstractEmbeddedDatabaseConfigurer.java:53) - ||||||||||||||--|Could not shut down embedded database: org.h2.jdbc.JdbcSQLNonTransientConnectionException: The database is open in exclusive mode; can not open additional connections [90135-200]
My DB Set up file looks like this -
#Configuration
#ComponentScan(basePackages={"com.sample.repository"})
public class SampleDBConfig {
JdbcTemplate jdbcTemplate = null;
DataSource dataSource = null;
#Bean
public JdbcTemplate jdbcTemplate()
{
if(jdbcTemplate == null){
LOGGER.info("JdbcTemplate is null, so calling to create ");
jdbcTemplate = new JdbcTemplate(getDataSource());
}else{
LOGGER.info("JdbcTemplate is already set");
}
return jdbcTemplate;
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
DataSource getDataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
if(dataSource == null){
dataSource = builder
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:folder/initial-setup.sql")
.build();
}
return dataSource;
}
}
One of my JUNIT test class looks like this -
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SampleDBConfig.class)
public class XyxImplTest{
#Autowired
ClassInTestImpl classInTestImpl ;
#Test
public void testMethod(){
.......
}
}
And one of the classes am testing are like this -
#Component
#Transactional(rollbackFor = Exception.class)
public class ClassInTestImpl implements ClassInTest {
#Autowired
private JdbcTemplate jdbcTemplate;
.....
Am using SpringJUnit4ClassRunner.class for my JUNIT tests and invoking this DB set up class from each JUNIT test class using #ContextConfiguration(classes = SampleDBConfig.class). I do not want to execute the JUNIT tests sequentially, is there any way we can resolve this issue?
I am trying to use Spring JDBCTemplate class to access database.
As a first tutorial, I have used xml spring configuration file and everything works as expected.
Now, I am trying to use #Configuration and created DataSource and JdbcTemplate instances through #Bean annotation within this file. But, I get a NullPointerException at int result = template.update(sql);
I am sure I have done a silly mistake. Was wondering what it could be.
The code is as follows.
#Configuration
public class SpringJDBCAnnotation {
#Autowired
static JdbcTemplate template;
#Autowired
static DataSource dataSource;
#Bean
DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/organization");
ds.setUsername("root");
ds.setPassword("test");
return ds;
}
#Bean
JdbcTemplate template() {
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(dataSource);
return template;
}
public static void main(String[] args) {
String sql = "insert into employee values(1, 'Tom', 'Cruise')";
int result = template.update(sql);
System.out.println("# of records inserted : " + result);
}
}
Application Context needs to be obtained whenever we need to create beans or autowire them.
Since this is Java based configuration, it is retrieved as follows.
ApplicationContext context = new AnnotationConfigApplicationContext(SpringJDBCAnnotation.class);
and the beans needs to be accessed from the context as usual.
JdbcTemplate template = context.getBean(JdbcTemplate.class);
I'm trying to write a test for one of my services. The service uses autowired repository which uses jdbcTemplate to access the DB. The problem is that the test actually puts data in the real DB.
Here is my test class:
#SpringApplicationConfiguration(Application.class)
#SpringBootTest(classes = { UserServiceImpl.class, UserService.class })
#RunWith(SpringJUnit4ClassRunner.class)
public class UserServiceTest {
#Autowired UserService userService;
#Test
public void test() {
final String fName = " xxxxxx ";
User user = new User();
user.setFirstName(fName);
user.setLastName(fName);
user.setEmail(fName);
user.setLogin(fName);
user.setPhone(fName);
userService.create(user);
user = userService.getUserByLogin(fName).get();
assertEquals(fName, user.getLogin());
}
}
Is there anything i can do to prevent the userService to work with real DB and somehow just make a simulation?
Best option is to use different DataSource bean in tests. You application would be tested against some in memory DB (typically H2). Just create this test configuration somewhere in your src/test/java/yourRootPackage:
#TestConfiguration
public class DataSourceConfig {
#Bean
#Primary
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.build();
}
}