H2 DB Initial Set Up scripts are getting called multiple times in JUNIT - spring-boot

Am trying to write JUNIT test cases with H2 DB for my micorservices. I have some .sql files for initial set up of the DB, like creating the schema, tables etc. One issue am seeing here is that the .sql files are getting called multiple times, once per JUNIT test file. Is there any way to handle this scenario? 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;
.....

I know this is a bit old thread, but adding some notes if anyone else having similar issue. I've faced same issue where I was getting unique constraint violation errors when H2 trying to insert data for each test file.
At the end it was the latest H2 version which causes this. If you change the H2 version from 1.4.200 to 1.4.199, it works like a charm. No other changes required to get this working as expected.

I had the same problem. The quick fix I did is, I din configure the datasource bean. Added the relevant spring properties in the properties file which creates the datasource.
Also I used #Sql("classpath:data1.sql") on the first test, so the script ran only once for that testsuite. It worked for me.

Related

How do I configure two databases in Spring Boot?

I am all new to Spring Boot and I have read som documentation about how you create a Spring boot application.
I have created an application in Spring Boot, this application should run a Spring Batch job (I am rewriting an old job in Spring Batch to a stand alone Spring Boot applikation). I have created the structure of the job with steps and so on. All the job does right now is moving files and that works. I work with embedded databases during development, h2, and Spring Boot has generated the whole database for Spring Batch. Really nice :)
So now my problem, in one of the steps I have to fetch and store data in another database. And I don't know (understand) how I should create this database and access the database in the job.
So in my application.properties
spring.h2.console.enabled=true
spring.h2.console.path=/h2
spring.datasource.url=jdbc:h2:mem:springdb
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
persons.h2.console.enabled=true
persons.h2.console.path=/h2
persons.datasource.url=jdbc:h2:mem:persons
persons.datasource.username=sa
persons.datasource.password=
persons.datasource.driverClassName=org.h2.Driver
In test I will change database to a SQL-database on another server.
I have some entitys (example)
public class Person {
private Long id;
private String name;
private String familyname;
private Long birthDate;
public Person () {
}
...with getters and setters
In the configuration I have
#Primary
#Bean
#ConfigurationProperties(prefix = "persons.datasource")
public DataSource personsDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource batchDataSource() {
return DataSourceBuilder.create().build();
}
And the job
#Autowired
PersonItemWriter itemWriter;
#Autowired
PersonItemProcessor itemProcessor;
#Autowired
PersonItemReader workReader;
#Bean(name = "personsImportJob")
public Job personImportJob() {
Step downloadFiles = stepBuilderFactory.get("download-files")
.tasklet(downloadTasklet())
.build();
Step syncDbAndSavePersons = stepBuilderFactory.get("syncandsave-persons")
.<File, Person>chunk(50)
.reader(workReader)
.processor(itemProcessor)
.writer(itemWriter)
.build();
Step deleteFiles = stepBuilderFactory.get("delete-files")
.tasklet(deleteTasklet())
.build();
Job job = jobBuilderFactory.get("personsimport-job")
.incrementer(new RunIdIncrementer())
.flow(downloadFiles)
.next(syncDbAndSavePersons)
.next(deleteFiles)
.end()
.build();
return job;
}
My writer
#Component
public class PersonItemWriter implements ItemWriter<Person> {
private static final Logger LOGGER = LoggerFactory.getLogger(PersonItemWriter.class);
#Override
public void write(List<? extends Person> list) throws Exception {
LOGGER.info("Write Person to db");
}
}
Now this works, the step syncAndSavePersons does not do anything right now, but I want this step to access another database and update posts to the persons-database.
Can I do this without JPA? Because the existing job doesn't use JPA and if I have to use JPA there will be a lot of code changes and I want to avoid this. I just want to move the job with minimum of changes,
If I run my application with this the only database that is created is the database for spring batch. How can I make sure that the other database is also created? Or is that impossible when I am working with h2 embedded databases?
Before I added the second database I didn't have the datasource configuration at all in my code, but the database was created anyway. I think Spring Boot just created the batch datasource. So maybe I won't need that configuration?
UPDATE:
I solved it by removing the properties for the database in te properties file. The only thing I left is:
spring:
datasource:
initialization-mode: never
h2:
console:
enabled: true
I then created a class called EmbeddedDataSourceConfig:
#Profile("default")
#Configuration
public class EmbeddedDataSourceConfig {
#Bean
#Primary
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:/org/springframework/batch/core/schema-h2.sql")
.build();
}
#Bean(name = "personsDataSource")
public DataSource personsDataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder
.setName("persons")
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:persons_schema.sql")
.build();
}
#Bean(name = "personsTransactionManager")
public PlatformTransactionManager personsTransactionManager(
#Qualifier("personsDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
#Bean(name = "personsJdbcTemplate")
public JdbcTemplate personsJdbcTemplate(#Qualifier("personsDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
and in the job I changed to following
#Bean(name = "personsImportJob")
public Job personImportJob(#Qualifier("personsTransactionManager") PlatformTransactionManager personsTransactionManager) {
Step downloadFiles = stepBuilderFactory.get("download-files")
.tasklet(downloadTasklet())
.build();
Step syncDbAndSavePersons = stepBuilderFactory.get("syncandsave-persons")
.<File, Person>chunk(50)
.reader(workReader)
.processor(itemProcessor)
.writer(itemWriter)
.transactionManager(personsTransactionManager)
.build();
...
}
That's it. Now it generates two h2 in-memory databases.

Override Default Datasource getConnection()

I am trying to convert a Spring application (for the most part) to a Spring Boot application. In the app, I have an HTTP basic filter that collects a username and password, this is then passed as variables in a DataSource implementation.
In this DataSource, the getConnection() method is so:
#Override\n public Connection getConnection() throws SQLException {
Statement stmt = null;
try {
ConnectionWrapper connection = this.authenticatedConnection.get();
if (connection == null) {
connection = new ConnectionWrapper(this.dataSource.getConnection());
StringBuilder command;
// The CONNECT command allows indicating a user name, a password
// and a database to initiate a
// new session in the server with a new profile.
command = new StringBuilder("CONNECT USER ").append(this.parameters.get().get(USER_NAME)).append(" PASSWORD ")
.append("'").append(this.parameters.get().get(PASSWORD_NAME)).append("'").append(" DATABASE ")
.append(this.parameters.get().get(DATA_BASE_NAME));
this.authenticatedConnection.set(connection);
stmt = connection.createStatement();
stmt.execute(command.toString());
}
return connection;
} catch (final SQLException e) {...`
(With \n as a new line due to StackOverflow formatting issues)
In Spring, I am able to implement #Autowired Private DataSource dataSource without a problem. In Spring Boot, as I understand it, the Object needs to be a Bean to use #Autowired, but when I add #Bean before this implemented DataSource I get "The annotation #Bean is disallowed for this location"
How can I get it so that I can do a dataSource.getConnection(); and get a connection from the primary DataSource, or be able to Override the methods of the primary DataSource?
The way I see it, there are 4 possible solutions listed here in order of preference:
Create a DataSource that is actually overwriting the spring.datasource' methods.
Get this implementation "Beanified" so I can just #Autowired the dataSource again.
I think I can skip the #Autowired and simply set this.dataSource = [unknown reference to spring.datasource defined in application.properties]
Create another DataSource class ProgrammedDataSource configured with the spring.datasource properties, then set it as this.dataSource = new ProgrammedDataSource();
but my attempts at implementing any of these solutions have produced this question.
I figured it out. I didn't need to make the Bean there, although I am still not sure why I was not allowed to call #Bean before the DataSource, but regardless.
In the application I had:
public class ServiceApplication {
#Bean
#Primary
#ConfigurationProperties("spring.datasource")
public DataSource dataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name="AuthDataSource")
public DataSource authDataSource() {
return new AuthDataSource();
}
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
and in the controller I had:
#Controller
#RequestMapping("/service")
public class ServiceController {
#Autowired
public void MyBean(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = new JdbcTemplate(new AuthDataSource());
} ...
However, since I was calling new AuthDataSource() inside that JdbcTemplate, it was not doing the Autowiring. Now the Controller looks like this and it works:
#Controller
#RequestMapping("/service")
public class ServiceController {
#Autowired
#Qualifier("AuthDataSource")
private DataSource datasource;
private JdbcTemplate jdbcTemplate;
#Autowired
public void MyBean(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = new JdbcTemplate(this.dataSource);
} ...

SpringBoot web services multiple data sources only one working

I have developed two webservices using Spring Boot framework and I have them in the same project. Each webservice use a different DB, say ws1 uses Oracle1 and ws2 uses Oracle2. I have defined a DataBaseConfig with the beans definition but when I run the app, always works one webservice ( and it's always the same ).
DataBaseConfig
#Configuration
public class DataBaseConfig {
#Bean(name = "ora1")
#ConfigurationProperties(prefix="spring.datasource")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();}
#Bean(name = "ora2")
#ConfigurationProperties(prefix="spring.secondDatasource")
public DataSource sqliteDataSource() {
return DataSourceBuilder.create().build();}
#Bean(name = "clients")
#Autowired
#ConfigurationProperties(prefix = "spring.datasource")
#Qualifier("datasource")
public JdbcTemplate slaveJdbcTemplate(DataSource datasource) {
return new JdbcTemplate(datasource); }
#Bean(name = "places")
#Autowired
#Primary
#ConfigurationProperties(prefix = "spring.secondDatasource")
#Qualifier("secondDatasource")
public JdbcTemplate masterJdbcTemplate(DataSource secondDatasource) {
return new JdbcTemplate(secondDatasource);}
}
I have the services definition with the sql statements and the definition
#Service
public class ClientsService {
#Autowired
#Qualifier("clients")
private JdbcTemplate jdbcTemplate;
and the other service
#Service
public class PlacesService {
#Autowired
#Qualifier("places")
private JdbcTemplate jdbcTemplate;
Then in each controller I have de mapping #RequestMapping. When I run the app I have no connection-related errors and if I separate the webservices in 2 projects, each works fine.
You have a few things going wrong here, including some unnecessary annotations. See below, note the location of #Qualifier and the qualifier name:
#Bean(name = "clients")
public JdbcTemplate slaveJdbcTemplate(#Qualifier("ora1") DataSource datasource) {
return new JdbcTemplate(datasource);
}
#Bean(name = "places")
#Primary
public JdbcTemplate masterJdbcTemplate(#Qualifier("ora2") DataSource secondDatasource) {
return new JdbcTemplate(secondDatasource);
}
Instead of resolving by bean name, which is a bad idea IMO because it's not typesafe, why don't you use constructor injection and create the services in the configuration class (ditch #Service annotation). Create the DataSource and JdbcTemplate beans as usual, don't give them any names (the default is the method name), and also create new PlacesService(placesJdbcTemplate()). The result is a lot simpler code.
This is assuming you want both databases active at runtime. If not, use #Profile.

Spring/Hibernate #Transactional on method prevents update SQL

Spring v4.2.5 Release
Hibernate v5.1.0.Final
I have a Junit test method which performs a load, updates a property and calls saveOrUpdate(bean).
It's behaving oddly in that adding #Transactional to the method signature prevents the update SQL from being performed (No SQL generated in log).
Remove the #Transactional and the update SQL is generated and the database updated.
#Configuration
#EnableTransactionManagement
#PropertySource(
{
"classpath:jdbc.properties",
"classpath:hibernate.properties"
})
#ComponentScan(value = "com.savant.test.spring.donorservice.core.dao")
public class ApplicationContext {
#Bean(destroyMethod = "close")
#Autowired
public DataSource dataSource() {
// Hikari is a connection pool manager.
HikariDataSource dataSource = new HikariDataSource();
dataSource.setUsername(env.getProperty("jdbc.username"));
dataSource.setPassword(env.getProperty("jdbc.password"));
dataSource.setJdbcUrl(env.getProperty("jdbc.url"));
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setIsolateInternalQueries(true);
System.out.println(dataSource);
dataSource.setConnectionTestQuery("SELECT count(*) from system.onerow");
dataSource.setMaximumPoolSize(3);
dataSource.setAutoCommit(false);
return dataSource;
}
#Bean
#Autowired
public LocalSessionFactoryBean sessionFactory(DataSource datasouce) {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(datasouce);
sessionFactory.setPackagesToScan(package_to_scan);
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
private Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.put(hibernate_dialect, env.getProperty(hibernate_dialect));
hibernateProperties.put(hibernate_current_session_context_class, env.getProperty(hibernate_current_session_context_class));
hibernateProperties.put(hibernate_connection_autocommit, env.getProperty(hibernate_connection_autocommit));
hibernateProperties.put(hibernate_format_sql, env.getProperty(hibernate_format_sql));
hibernateProperties.put(hibernate_hbm2ddl_auto, env.getProperty(hibernate_hbm2ddl_auto));
hibernateProperties.put(hibernate_show_sql, env.getProperty(hibernate_show_sql));
// hibernateProperties.put(hibernate_connection_provider_class, env.getProperty(hibernate_connection_provider_class));
return hibernateProperties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager txManager = new HibernateTransactionManager(sessionFactory);
return txManager;
}
The entities have been auto-generated using Netbeans 'Entity classes from Database'.
The main Entity has
A one-to-one relationship with FetchType.EAGER
A one-to-many relationship with FetchType.EAGER (it was LAZY - read below).
The test method looks like this.
#Test
#Transactional
public void c_testUpdateAddress1() {
System.out.println("findById");
String id = donorId;
Donor donor = donorDao.findById(id);
donor.setAbogrp(" O");
for (DonorAddress da : donor.getDonorAddressCollection()) {
da.setAddr1("Updated line");
System.out.println(da.getDonorAddressPK().getAddrtype() + " " + da.getAddr1());
}
System.out.println("Update");
Donor savedDonor = donorDao.save(donor);
}
Without #Transactional The update SQL is generated and the database
is updated.
With #Transactional The update SQL is not generated, does not appear
in the log. There are no exceptions, stepping over the Save method
in my Dao implementation everything appears fine. The bean passed in
has the correct values (updated field values), the bean returned has
the updated field values - just no SQL generated.
#Override
public Donor save(Donor bean) {
getSession().saveOrUpdate(bean);
return bean;
}
The reason I need #Transactional is to allow the address to the LAZY.
Without #Transactional I can't access the address as LAZY due to exception "failed to lazily initialize a collection of role: could not initialize proxy - no Session"
Which is as expected.
A transaction is started as soon as a #Transactional method is detected and committed as soon as that method call ends. Which in the case of a test is after the end of the test method. So during your tests you will not see the SQL.
Also when using #Transactional on a Spring based test it will by default do a rollback instead of a commit. See here in the reference guide on the default and how to change it.
Answer was provided by M Deinum as a comment

Best way to test JPA Spring 3.1

I have written several JPA Repositories, Services and support classes for my Spring 3.1.1/JPA/Hibernate 4 web app. However, I really want to write some unit and integration tests for it (I know, you are supposed to write those first). I am using JavaConfig rather than XML, so I am wondering the best way to test. Here is the particular problem I am trying to solve:
I have a #Configuration that declares DataSource, JpaTransactionManager, LocalContainerEntityManagerFactoryBean, and all my respositories. Obviously I don't want to start all that up for an integration test, so I thought I could use the EmbeddedDatabase and H2 to create an in memory database, populate with values, and then use my Repositories against it. However, the documentation I have seen hasn't helped me put this together. I have this:
#RunWith( SpringJUnit4ClassRunner.class )
public class TestMenuService {
private EmbeddedDatabase database;
#Autowired
private MenuRepository menuRepository;
#Before
public void setUp() throws Exception {
database = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).setName("myschema")
.addScript("classpath:schema.sql").build();
menuRepository = new MenuRepository();
Assert.assertNotNull(database);
}
But the menuRepository does not get instantiated, so I tried creating a test version of my #Configuration
#Configuration } )
#ComponentScan( basePackages = { "com.mycompany.service"} )
#EnableTransactionManagement
#ImportResource( "classpath:applicationContext.xml" )
#PropertySource( "classpath:test-application.properties" )
public class TestEdmConfiguration {
#Bean
MenuRepository menuRepository() {
return new MenuRepository();
}
My test-applicationContext.xml
<jpa:repositories base-package="com.mycompany.servce.repository"/>
My test-application.properties:
db.driver=org.h2.Driver
db.username=sa
db.password=
db.url=jdbc:h2:mem:myschema
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.format_sql=true
hibernate.hbm2ddl.auto=create
hibernate.ejb.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
hibernate.show_sql=true
But this requires that I create all the datasources, etc mentioned above. It seems like I am just duplicating all the support beans for this one.
Is there a way to have the reposository and embeddeddatabase isolated to my test without all the other dependencies?
If you want to test your repository in a full integration test I would image that you need everything else to be setup i.e. EntityManagerFactory, PlatformTransactionManager etc.
Since you are using Spring 3.1 I would suggest that you achieve this using bean profiles.
I would create two profiles one for tests and one for the application, each of which supplies a datasource.
#Configuration
#Profile("test")
public class EmbeddedDataSource {
#Bean
public DataSource dataSource() {
// Return a H2 datasource
}
}
#Configuration
#Profile("application")
public class ApplicationDataSource {
#Bean
public DataSource dataSource() {
// Return a normal datasource
}
}
The you can create a test which starts up the spring context as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = { MyConfigClass.class })
#ActiveProfiles(profiles = {"test"})
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public class TestRepository {
}
In here you can specify the profiles where are active for the test.

Resources