I am trying to develop tests for my application (I found out about the tests very late...) and I am stuck and the basic configuration. I have googled through many examples and none of them satisfied me and frankly left me a bit confused.
What I am trying to achieve is to load an import.sql on start of the test (which is a dump file from existing MySQL schema) and load it into H2 database.
Here is the hibernate config file:
#Configuration
#EnableTransactionManagement
#ComponentScan({ "kamienica.feature" })
public class HibernateTestConfiguration {
#Autowired
ApplicationContext applicationContext;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "kamienica" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean(name = "dataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;"
+ "INIT=CREATE SCHEMA IF NOT EXISTS kamienica;DB_CLOSE_ON_EXIT=FALSE");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.put("hibernate.hbm2ddl.auto", "update");
// this is where I tried to load script the first time:
// properties.put("hibernate.hbm2ddl.import_files", "kamienica.sql");
return properties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
Everytime I start a test I get a message that:
org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata INFO:
HHH000262: Table not found: apartment
And I get empty/null values when trying to retrieve anything
I have tried to load sql file in the hibernate config (via hibernate properties) as well as in superclass which all my test classes are planned to extend:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { HibernateTestConfiguration.class })
public class BaseTest {
private EmbeddedDatabase db;
#Autowired
private SessionFactory sessionFactory;
//second attempt to load sql file
#Before
public void setUp() throws Exception {
db = new EmbeddedDatabaseBuilder().addScript("import.sql").build();
}
#After
public void tearDown() throws Exception {
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
}
How can I load sql file and prepare the H2 database to perform the tests?
I hope this spring boot approach will help you. First create a resources directory (classpath for springboot)in the src/test directory at the root of your project.
In this directory, you will start placing your fixture SQL data files named say data.sql .
Then, create a application.properties file on the same level (same directory see screenshot). This file should be populated as shown here:
spring.datasource.url = jdbc:h2:~/test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
#spring.datasource.url: jdbc:mysql://localhost:3306/yourDB
#spring.datasource.username = root
#spring.datasource.password =
# Hibernate
hibernate.show_sql: true
#hibernate.dialect: org.hibernate.dialect.MySQL5Dialec
spring.jpa.hibernate.ddl-auto=none
Screenshot:
Now your tester method.
#RunWith(SpringJUnit4ClassRunner.class)
....
#Autowired
private DataSource ds; //your application.properties
#Autowired
private WebApplicationContext context;
private static boolean loadDataFixtures = true;
private MockMvc mockMvc;
....
#Before
public void setupMockMvc() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Before
public void loadDataFixtures() {
if (loadDataFixtures) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(context.getResource("classpath:/data.sql"));
DatabasePopulatorUtils.execute(populator, ds);
loadDataFixtures = false;
}
}
#Test
public void yourmethod() {
assertEquals(3, repository.count()); //example
}
Without any output or the complete stacktrace, the only I can suggest you is:
You aren't showing any #Test method. How are you getting that error?
Is your file import.sql in src/test/resources folder? (note the test path)
Is your sql script well formated? Have you tried to run once exported? Could you post the part of the sql script wich creates the apartment table ?
If all are true, maybe the problem is not about loading the sql but how it's used, or the content of the script, or the name of the tables, etc...
After a long 'investigation' I have concluded that the problem was hidden somewhere in he DBUnit, TestNG setup.
I decided to keep it simple and switched to JUnit tests.
In case others might have similar problems here is the config file that works for me:
#Configuration
#EnableTransactionManagement
#ComponentScan({ "kamienica.feature" })
public class JUnitConfig {
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "kamienica" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean(name = "dataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:kamienica;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.put("hibernate.hbm2ddl.auto", "create");
return properties;
}
#Bean
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
All is needed now is to insert import.sql file in the resources folder.
I also found out that each insert statements must be in one line no matter how long it is, otherwise it won't be loaded.
Finally a simple test class:
#ContextConfiguration(classes = { JUnitConfig.class })
#RunWith(SpringJUnit4ClassRunner.class)
public class ApartmentServiceTest extends AbstractServiceTest{
#Autowired
ApartmentService service;
#Test
public void getList() {
List<Apartment> list = service.getList();
System.out.println(list.toString());
assertEquals(5, list.size());
}
}
Related
I want to use two database schemas: one with static data (which cannot be changed), the other with dynamic data (which will change constantly during the execution of the Spring application).
In application.properties I have prescribed the following:
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/XXXXXX
spring.datasource.username=root
spring.datasource.password=root
spring.second-datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/YYYYYY
spring.second-datasource.username=root
spring.second-datasource.password=root
But what should I do now with the spring.jpa.hibernate.ddl-auto parameter?
For one scheme I want to install:
spring.jpa.hibernate.ddl-auto=create
And for the other (where the data is always static):
spring.jpa.hibernate.ddl-auto=update
or
spring.jpa.hibernate.ddl-auto=none
You have an issue that you need two different datasources. You should define them as 2 different beans and autowire them seperately. For clarity it's advisable to also use 2 different properties files.
You can create a Configuration class and use specific properties file to populate fields like so:
#Configuration
#PropertySource("classpath:db.properties")
public class Config {
#Value("${db.driverClassName}")
String driverClassName;
#Value("${db.url}")
String url;
#Value("${db.username}")
String username;
#Value("${db.password}")
String password;
#Bean("datasourceId")
public DataSource dataSource() {
var dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(
{ "com.foo.bar" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
private final Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty(
"hibernate.hbm2ddl.auto", "create-drop");
return hibernateProperties;
}
}
Here is the application.properties of my Spring Boot 2.5 :
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME}
spring.datasource.username=${MYSQL_USER:root}
spring.datasource.password=${MYSQL_PASSWORD:root}
I migrated my application from Spring MVC 4.3 and I don't use JPA on the latest version of Spring Boot.
So I configured a #Beandatasource like this:
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(env.getRequiredProperty("spring.datasource.url"));
dataSource.setUsername(env.getRequiredProperty("spring.datasource.username"));
dataSource.setPassword(env.getRequiredProperty("spring.datasource.password"));
return dataSource;
}
However, this does not work because instead of having the value of the environment variable, I have the name of the environment variable, i.e. ${MYSQL_USER:root} for example.
My question is what is Spring's recommended way here to set up environment variables much like Laravel's .env in my datasource() and also in the application.properties if I decide to use JPA later on?
The reason and the important point: I don't want to push my credentials on git
EDIT :
HibernateUtil :
static {
try {
Properties applicationProps = new Properties();
File file = ResourceUtils.getFile("classpath:application.properties");
InputStream input = new FileInputStream(file);
applicationProps.load(input);
Properties properties = new ApplicationConf().hibernateProperties();
// Configure datasource
properties.setProperty("hibernate.connection.driver_class", applicationProps.getProperty("spring.datasource.driver-class-name"));
properties.setProperty("hibernate.connection.url", applicationProps.getProperty("spring.datasource.url"));
properties.setProperty("hibernate.connection.username", applicationProps.getProperty("spring.datasource.username"));
properties.setProperty("hibernate.connection.password", applicationProps.getProperty("spring.datasource.password"));
properties.setProperty("hibernate.current_session_context_class", "thread");
properties.setProperty("hibernate.jdbc.batch_size", Integer.toString(BATCH_SIZE));
// Override some properties
properties.setProperty("hibernate.format_sql", "false");
properties.setProperty("hibernate.show_sql", "false");
} catch {}
...
}
Best regards,
You can use the #Value annotation to pull the properties and also their defaults.
#Value("${spring.datasource.username}")
private String userName;
#Bean
public DataSource dataSource() {
...
dataSource.setUsername(userName);
...
return dataSource;
}
You can also omit the default from the application.properties file and specify a default value for the property using Value annotation -
#Value("${spring.datasource.username:root}")
private String userName;
#Bean
public DataSource dataSource() {
...
dataSource.setUsername(userName);
...
return dataSource;
}
Or can even be injected in the method-
#Bean
public DataSource dataSource(#Value("${spring.datasource.username:root}") String userName) {
...
dataSource.setUsername(userName);
...
return dataSource;
}
I was trying to insert data into database in my cucumber tests module in spring-boot application.
When start spring app with test profile (mvn spring-boot:run -Dspring-boot.run.profiles=test) it start up the application and run properly. The issue is during cucumber test execution when try to setup the datasource (as pointed out ** line in the code below) it comes as null. So should I setup the datasource again? If so how.
It's not cucumber test related issue, The issue is I can't access the datasource which have set in the main app.
Below is the code
#ContextConfiguration(classes = MainApp.class, loader = SpringBootContextLoader.class)
#ActiveProfiles("test")
#Configuration
#PropertySource({"classpath:create-sql.xml"})
public class TestHelper {
#Value("${CreateSql}")
private String CreateSql;
#Autowired
private SqlQueryBuilder sqlQueryBuilder;
#Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
#Autowired
private UserPreferenceFormatter formatter;
#Autowired
private DataSource dataSource;
public static void getDataList() throws IOException {
MapSqlParameterSource sqlParamSource = new MapSqlParameterSource();
sqlQueryBuilder = new SqlQueryBuilder();
jdbcTemplate = new NamedParameterJdbcTemplate(dataSource); ****
String parsedSql = sqlQueryBuilder.parseSql(CreateSql,null,null,null);
List<DataSummary> dataSummaries = jdbcTemplate.query(parsedSql, sqlParamSource, new DataSummaryRowMapper(null,formatter));
}
application-test.yml file under resources folder with all spring datasources within test module
app-db-url: jdbc:oracle:....
app-db-user: USERNAME
spring:
datasource:
password: PWD
I went through below solution as well
Solution-1
Solution-2
Deployment module app-config.yml
....
data:
# Database
app-db-url : ##app-db-url##
app-db-user: ##app-db-user##
......
It looks like you are missing code that defines that DataSource bean.
You should have something like this:
#Configuration
public class DataSourceConfig {
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:mem:test");
dataSourceBuilder.username("SA");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
}
or something like that:
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.username("SA");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
and the rest of the propertied can go into a property file.
I want to use 2 or more jdbcTemplate in my project using application.properties.I try but got runtime exception.
########## My application.properties:-
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ccm_new
spring.datasource.username=test
spring.datasource.password=test
spring.oracledatasource.url=jdbc:oracle:thin:#localhost:1521:mastera
spring.oracledatasource.password=test
spring.oracledatasource.username=test
spring.oracledatasource.driver-class-name=oracle.jdbc.driver.OracleDriver
#Bean(name = "dsMaster") ############
#Primary
#ConfigurationProperties(prefix="spring.oracledatasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcMaster") #############
public JdbcTemplate masterJdbcTemplate(#Qualifier("dsMaster") DataSource dsMaster)
{
return new JdbcTemplate(dsMaster);
}
################I use the mysql connection normally but on use of oracle connection i got
org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: Cannot create JDBC driver of class '' for connect URL 'null'
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:81)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:371)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:446)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:456)
at enter code here
I got it where i am wrong,I want to make mysql connection through application.properties without #bean configuration.If you want to take 2 or more connection you just need to define all the datasource with their #ConfigurationProperties(prefix="spring.mysqldatasource") different prifix other than "spring.datasource".prifix " spring.datasource" use only when we need to make connection from only one database.Here is the final working code example:-
application.properties
spring.mysqldatasource.driver-class-name=com.mysql.jdbc.Driver
spring.mysqldatasource.url=jdbc:mysql://localhost:3306/ccm_new
spring.mysqldatasource.username=test
spring.mysqldatasource.password=test
spring.mysqldatasource.dbcp2.initial-size=5
spring.mysqldatasource.dbcp2.max-total=15
spring.mysqldatasource.dbcp2.pool-prepared-statements=true
spring.oracledatasource.url=jdbc:oracle:thin:#localhost:1521:mastera
spring.oracledatasource.password=test
spring.oracledatasource.username=test
spring.oracledatasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.oracledatasource.dbcp2.initial-size=5
spring.oracledatasource.dbcp2.max-total=15
spring.oracledatasource.dbcp2.pool-prepared-statements=true
#Configuration
public class PrototypeUtility {
#Bean(name = "dsMaster")
#Primary
#ConfigurationProperties(prefix="spring.oracledatasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcMaster")
public JdbcTemplate masterJdbcTemplate(#Qualifier("dsMaster") DataSource dsMaster) {
return new JdbcTemplate(dsMaster);
}
#Bean(name = "dsMasterMysql")
#ConfigurationProperties(prefix="spring.mysqldatasource")
public DataSource primaryDataSourceMysql() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcMasterMysql")
public JdbcTemplate masterMysqlJdbcTemplate(#Qualifier("dsMasterMysql") DataSource dsMasterMysql) {
return new JdbcTemplate(dsMasterMysql);
}
}
and then i autowired the both connection :-
#Autowired
private JdbcTemplate jdbcMasterMysql;
#Autowired
public JdbcTemplate jdbcMaster;
This code run successfully for me .
If any one have doubt,Don't hesitate to ask.
I got it where i am wrong,I want to make mysql connection through application.properties without #bean configuration.If you want to take 2 or more connection you just need to define all the datasource with their #ConfigurationProperties(prefix="spring.mysqldatasource") different prifix other than "spring.datasource".prifix " spring.datasource" use only when we need to make connection from only one database.Here is the final working code example:-
application.properties
spring.mysqldatasource.driver-class-name=com.mysql.jdbc.Driver
spring.mysqldatasource.url=jdbc:mysql://localhost:3306/ccm_new
spring.mysqldatasource.username=test
spring.mysqldatasource.password=test
spring.mysqldatasource.dbcp2.initial-size=5
spring.mysqldatasource.dbcp2.max-total=15
spring.mysqldatasource.dbcp2.pool-prepared-statements=true
spring.oracledatasource.url=jdbc:oracle:thin:#localhost:1521:mastera
spring.oracledatasource.password=test
spring.oracledatasource.username=test
spring.oracledatasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.oracledatasource.dbcp2.initial-size=5
spring.oracledatasource.dbcp2.max-total=15
spring.oracledatasource.dbcp2.pool-prepared-statements=true
#Configuration
public class PrototypeUtility {
#Bean(name = "dsMaster")
#Primary
#ConfigurationProperties(prefix="spring.oracledatasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcMaster")
public JdbcTemplate masterJdbcTemplate(#Qualifier("dsMaster") DataSource dsMaster) {
return new JdbcTemplate(dsMaster);
}
#Bean(name = "dsMasterMysql")
#ConfigurationProperties(prefix="spring.mysqldatasource")
public DataSource primaryDataSourceMysql() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcMasterMysql")
public JdbcTemplate masterMysqlJdbcTemplate(#Qualifier("dsMasterMysql") DataSource dsMasterMysql) {
return new JdbcTemplate(dsMasterMysql);
}
}
I am New to HSQLDB and working with Spring Application with JavaConfig. I want my example to setup a in memory database(HSQLDB) and insert one row.
I think I have my stuff all in order but I don't know where to create the database and the tables.
Below is my main.app code
public class MainApp
{
private static final Logger LOGGER = getLogger(MainApp.class);
#Autowired
protected MessageService mService;
public static void main(String[] args)
{
ApplicationContext context = new AnnotationConfigApplicationContext(HelloWorldConfig.class);
HelloWorld helloWorld = context.getBean(HelloWorld.class);
LOGGER.debug("Message from HelloWorld Bean: " + helloWorld.getMessage());
/**
* I removed the following line... we are now using log4j
*/
//System.out.println(helloWorld.getMessage());
helloWorld.setMessage("I am in Staten Island, New York");
/**
* I removed the following line... we are now using log4j
*/
//System.out.println(helloWorld.getMessage());
LOGGER.debug("Message from HelloWorld Bean: " + helloWorld.getMessage());
}
and here is my DatabaseConfig.class
public class DatabaseConfig
{
private static final Logger LOGGER = getLogger(DatabaseConfig.class);
#Autowired
Environment env;
#Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.HSQL).build();
}
#Bean
public SessionFactory sessionFactory()
{
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setHibernateProperties(getHibernateProperties());
factoryBean.setPackagesToScan(new String[]{"com.xxxx.model"});
try
{
factoryBean.afterPropertiesSet();
} catch (IOException e)
{
LOGGER.error(e.getMessage());
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return factoryBean.getObject();
}
#Bean
public Properties getHibernateProperties()
{
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
hibernateProperties.setProperty("hibernate.use_sql_comments", env.getProperty("hibernate.use_sql_comments"));
hibernateProperties.setProperty("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));
hibernateProperties.setProperty("javax.persistence.validation.mode", env.getProperty("javax.persistence.validation.mode"));
//Audit History flags
hibernateProperties.setProperty("org.hibernate.envers.store_data_at_delete", env.getProperty("org.hibernate.envers.store_data_at_delete"));
hibernateProperties.setProperty("org.hibernate.envers.global_with_modified_flag", env.getProperty("org.hibernate.envers.global_with_modified_flag"));
return hibernateProperties;
}
#Bean
public HibernateTransactionManager hibernateTransactionManager()
{
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sessionFactory());
htm.afterPropertiesSet();
return htm;
}
but I don't know where the database is create and how do I insert my table before my project runs?
You can download the source code at the following:
https://github.com/JohnathanMarkSmith/HelloSpringJavaBasedJavaConfig/issues/1
This has been asked before, take a look at Startup script to create a schema in HSQLDB