I want auto testing my spring api. Now I have about 7-8 interesting situation and make:
deploy in postgres 7 db (test1, 2, ....)
application.properties write db test1.
run test and go to 2.
This is hard. What is easy way?
I want have one db for test (clear db, only tables structure)
before test write in db fixtures (special file with data, example - users, reports and other)
run test
after test delete fixture and have clear db again.
Can I do it by spring? Early I programming in python/django and use like way.
I think one way to solve that would be via custom properties files and #Configuration classes.
You would create for each Test the property file e.g. test1-dbA.properties
jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
jdbc.username=db1
jdbc.password=..
Next you would define the DataSource bean that loads the properties from the test1-dbA.properties
#Configuration
#EnableJpaRepositories(basePackages = "org.baeldung.repository")
#PropertySource("test1-dbA.properties")
#EnableTransactionManagement
public class DB1Config {
#Autowired
private Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.user"));
dataSource.setPassword(env.getProperty("jdbc.pass"));
return dataSource;
}
// ...
}
In the JUnit Test you would load the configuration class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {Application.class, DB1Config.class})
public class db1Test{
// ...
}
This and other options can be looked up here: https://www.baeldung.com/spring-testing-separate-data-source and https://www.baeldung.com/spring-jpa-test-in-memory-database
Related
I´m working on some legacy code, where we have 2 different datasources for our application.
There is one class that defines the configuration for both, and I don't want to separate them into two classes because things will probably break. This is the configuration class
#Configuration
public class DataSourceConfig {
#Bean(name="dataSource")
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name="merchantsDataSource")
#ConfigurationProperties(prefix = "merchants.datasource")
public DataSource merchantsDataSource() {
return DataSourceBuilder.create().build();
}
}
Being structured this way, I don't think I can use #EnableJpaRepositories as suggested in many places to be able to define Repositories from different Data Sources.
So my question is: is there a way that I can create a Repository for the non-primary Data Source, without having to refactor all legacy code to use new classes?
I am currently working on a project which is using Redis mainly for caching purposes, I am using Oracle as the main database and Spring Data JPA to handle the database layer in my project. I need to know how to use #Transactional annotation support to handle transactions in Redis. I have already referred to lots of tutorials and documentation regarding this scenario. In most of those tutorials, there is always the same set of source codes available. But still, I didn't have a clear idea about the implementation. Because in my application there is already a data source available which I configured through property file. (Oracle database) So I doubt the implementation of the dataSource bean. And I couldn't understand the usage of transactionManager bean too. How should I implement this properly please give a detailed explanation.
Source code which I found on the internet.
#Configuration
#EnableTransactionManagement // 1
public class RedisConfig {
#Bean
public StringRedisTemplate stringRedisTemplate(LettuceConnectionFactory redisConnectionFactory) {
// Configure redisTemplate
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
/ / Open transaction support
stringRedisTemplate.setEnableTransactionSupport(true); // 2
return stringRedisTemplate;
}
#Bean
public PlatformTransactionManager transactionManager() throws SQLException {
return new DataSourceTransactionManager(dataSource()); // 3
}
#Bean
public DataSource dataSource() throws SQLException {
// ...
}
}
Updated :
Currently configured datasource properties in apppication.properties file.
# OracleDB connection settings
spring.datasource.url=jdbc:oracle:thin:#192.168.20.108:1521:orcl
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# HikariCP settings
spring.datasource.hikari.minimumIdle=5
spring.datasource.hikari.maximumPoolSize=20
spring.datasource.hikari.idleTimeout=30000
spring.datasource.hikari.maxLifetime=2000000
spring.datasource.hikari.connectionTimeout=30000
spring.datasource.hikari.poolName=redis-sample-pool
Resource about the Redis Transactions handling
There're two ways to configure DataSource
Using the config file
spring.datasource.url= jdbc:oracle:thin:#//localhost
spring.datasource.username=test
spring.datasource.password=test1234
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
#hibernate config dialect for JPA, choose dialect based on the db version
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
You can create manually the same by reading these properties, with the help of DataSourceBuilder
#Bean
public DataSource dataSource(){
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("oracle.jdbc.OracleDriver");
dataSourceBuilder.url("jdbc:h2:mem:test");
dataSourceBuilder.username("test");
dataSourceBuilder.password("test1234");
return dataSourceBuilder.build();
}
I've created a pretty standard Spring Boot 2.0 app with Services and Repositories to access a database.
I had to set the standard Spring Boot application properties to get it to work, such as:
spring.datasource.url, spring.jpa.database, etc.
However, in order to prevent my properties from overwriting other properties in similar apps hosted in the same place, I need to rename these properties, such as:
myApp.spring.datasource.url, myApp.spring.jpa.database, etc.
Some of these properties will be set by environmental variables instead of the application.properties file.
However, I can't see any way to override those variables in my app.
The standard approach is to use #Value to configure those variables. However, the Spring Boot 2.0 setup for services looks up all these properties "behind the scenes," so that doesn't appear to be an option here.
Is there any way to configure my app to read all those myApp.common.property.name properties and treat them as common.property.name?
Thank you.
Yes, the standard way is to use #Value. But thw work doesn't stop there, you need to create DataSource and EntityManager with these values.
Springboot will create DataSource, Entitymanager and some other components bey looking into default properties(spring.xxx) from the file(hence Spring boot is opinionated). But when you change these names to non default values, then you need to create these components / beans yourself.
Instead of using #Value you could also use #Configurationproperties. #Value also works but you might need to declare like 6 or 7 values with #Value. If you wish to use #ConfigurationProperties, make sure you have #EnableConfigurationProperties annotation added somewhere in your project.
Here is a code snippet. You need to tune it to your project
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactory",
basePackages = { "com.xxx.yyy.repo" }
)
public class SomeDbConfig {
#Primary // Use this if you have multiple datasources or else no use
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "myApp.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.xxx.yyy.domain")
.persistenceUnit("somename")
.build();
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
I have datasource defined in my application.properties as Oracle database and that's ok, the controller & repository work fine, I can persist entities and fetch database records.
I have integration tests written. Before I connected my app with database I created some objects and persisted them in #PostConstruct method - and that was ok. But now, when I connected everything with database, my tests try to fetch records from the table which is pretty large.
I think that's due to my application.properties file in which defined datasource is Oracle database.
spring.datasource.driverClassName=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:#blabla
spring.datasource.username=blabla
spring.datasource.password=blabla
spring.jpa.hibernate.ddl-auto=none
spring.jpa.generate-ddl=false
I want to carry on tests using some in-memory HSQL, not real database. But I still want my controller and repository to use the real one. How can I do it? Is there any possibility to add second datasource in application.properties? Or maybe an easier solution?
In Spring 4 you have #Profile annotation so you can use its advantage.
Create another like application-test.properties file with it's own properties.
Create configuration for your tests:
#Configuration
#Profile("test")
public class TestConfiguration {
....
}
Then annotate your class with #ActiveProfiles annotation:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestConfiguration.class)
#ActiveProfiles("test")
public class TestRepository {
...
}
There are many ways to achieve this, one way is to use Spring profiles. The test profile would use a different properties file than the production one.
Spring has org.springframework.mock.jndi.SimpleNamingContextBuilder package that allows you to bind the dataSource programatically.
Sample Test class that I use:
public class MockJNDIContext {
#BeforeClass
public static void setUpClass() throws Exception {
SimpleNamingContextBuilder builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
OracleConnectionPoolDataSource ds = new OracleConnectionPoolDataSource();//Use your own Connection Pool DS here as you require
ds.setURL("jdbc:oracle:thin:#10.xxx.xxx.xx:9999:SID");
ds.setUser("USER");
ds.setPassword("PASSWORD");
//Bind it to the JNDI you need
builder.bind("java:comp/env/jdbc/MY/APP", ds);
}
#Test
public void test(){
System.out.println("JNDI Initialized");
}
}
Suite Class:
#RunWith(Suite.class)
#Suite.SuiteClasses({
MockJNDIContext.class,
InitializeTestConfig.class
})
public class ServiceSuite{
}
May be this helps? If you are trying to load from one more application.props, use one more class (InitializeTestConfig.class) that initializes and passes the DS args and add to suite as mentioned above and try?
I am just learning how to use springboot as a java backend framework and I currently have applications.properties configured to use 1 database.
I am thinking of adding an additional database to store different information instead of saving everything on a single database so I was wondering how (if possible) can i do that?
My application.properties file contains data like this:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://database:3306...
Any ideas?
You can create two datasources, one bean of them mark as #Primary
#Bean
#ConfigurationProperties(prefix = "datasource.mysql")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#ConfigurationProperties(prefix = "datasource.postgres")
#Bean
#Primary
public DataSource postgresDataSource() {
return DataSourceBuilder.create().
build();
}
Your application.properties should looks like this:
datasource.mysql.url=jdbc:mysql://localhost:3306/mysql_demo
datasource.mysql.username=root
datasource.mysql.password=root
datasource.mysql.driverClassName=com.mysql.jdbc.Driver
datasource.postgres.url=jdbc:postgresql://localhost:5432/postgres_demo
datasource.postgres.username=postgres
datasource.postgres.password=postgres
datasource.postgres.driverClassName=org.postgresql.Driver