spring boot test application.properties file not found - spring

I have a maven module with one test class DbTest. When running the test, the property file application.properties is not picked up. The line
System.out.println in DvsTestDbConfig displays {database.test} instead of qqqqqqqqq (dummy value for the moment). Any idea why spring doesn't find the property file ?
DbTest
package ch.admin.estv.dvs.test.database;
import ch.admin.estv.dvs.test.database.config.DvsTestDbConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#Import(DvsTestDbConfig.class)
public class DbTest {
#Test
public void givenPartnerToDelete_whenPartnerDelete_thenNoEntryExist() {
System.out.println("test started");
}
}
DvsTestDbConfig
package ch.admin.estv.dvs.test.database.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
#Configuration
#ComponentScan(basePackages = {"ch.admin.estv.dvs.test.database"})
#EnableTransactionManagement
public class DvsTestDbConfig {
#Value("${database.test}")
String databaseTest;
#Autowired
private Environment environment;
#Value("${database.driverClassName}")
String driverClassName;
#Value("${database.url}")
String databaseUrl;
#Value("${database.username}")
String databaseUsername;
#Value("${database.password}")
String databasePassword;
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[]{"ch.admin.estv.dvs.test.database"});
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
System.out.println("************** db property databaseTest : " + databaseTest);
dataSource.setUrl(databaseUrl);
dataSource.setUsername(databaseUsername);
dataSource.setPassword(databasePassword);
dataSource.setDriverClassName("oracle.jdbc.OracleDriver");
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
Properties additionalProperties() {
Properties properties = new Properties();
//properties.setProperty("hibernate.hbm2ddl.auto", "validate");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
properties.setProperty("hibernate.temp.use_jdbc_metadata_defaults", "false");
properties.setProperty("hibernate.jdbc.lob.non_contextual_creation", "true");
properties.setProperty("show_sql", "true");
return properties;
}
}
application.properties
database.test=qqqqqqqqq
database.url=xxxxxxxxxxxxxxxxx
database.username=xxxxxxxxxx
database.password=xxxxxxxxxxx
database.driverClassName=oracle.jdbc.OracleDriver
pom.xml
<parent>
<artifactId>dvs-test</artifactId>
<groupId>ch.admin.estv.dvs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dvs-test-database</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>${ojdbc6.version}</version>
</dependency>
</dependencies>

Add
#PropertySource(value = "classpath:application.properties")
annotation on your DvsTestDbConfig class
Properties resolved through the Environment reside in one or more
"property source" objects, and #Configuration classes may contribute
property sources to the Environment object using the #PropertySource
annotation:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html

Annotate your test class with #SpringBootTest . More details and alternatives: Populating Spring #Value during Unit Test

Related

Spring Boot/Hibernate: Not Found error when I text my code

I am trying to create a project in spring boot and when I go to test my app via Postman I get the following error:
{
"status": 404,
"error": "Not Found",
"message": "",
"path": "/ api / customers-list"
}
I explain in detail what the problems were.
I initially encountered the following error:
APPLICATION FAILED TO START
Description:
Cannot determine embedded database driver class for database type NONE
Action:
If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).
which I solved by simply inserting the following line in application.properties (I don't know if that's a good way, but it seems to have solved anyway):
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
Subsequently I found the error above (ie that of not found). I checked carefully if there was any typo in entering the URL, I checked if I ran the right application and it seems that everything is correct but I am unable to interact with Postman due to Not Found.
How can I solve this problem? I hope someone will help me.
Code:
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
Config.java
package pack_cap_Config;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableTransactionManagement
#EnableAutoConfiguration(exclude = { HibernateJpaAutoConfiguration.class})
#ComponentScans(value = { #ComponentScan("boot.entry"),
#ComponentScan("Model"),
#ComponentScan("Controller"),
#ComponentScan("DAO"),
#ComponentScan("Miscallaneous"),
#ComponentScan("Service")})
public class Config {
#Value("${db.driver}")
private String DB_DRIVER;
#Value("${db.password}")
private String DB_PASSWORD;
#Value("${db.url}")
private String DB_URL;
#Value("${db.username}")
private String DB_USERNAME;
#Value("${hibernate.dialect}")
private String HIBERNATE_DIALECT;
#Value("${hibernate.show_sql}")
private String HIBERNATE_SHOW_SQL;
#Value("${hibernate.hbm2ddl.auto}")
private String HIBERNATE_HBM2DDL_AUTO;
#Value("${entitymanager.packagesToScan}")
private String ENTITYMANAGER_PACKAGES_TO_SCAN;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(ENTITYMANAGER_PACKAGES_TO_SCAN);
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", HIBERNATE_DIALECT);
hibernateProperties.put("hibernate.show_sql", HIBERNATE_SHOW_SQL);
hibernateProperties.put("hibernate.hbm2ddl.auto", HIBERNATE_HBM2DDL_AUTO);
sessionFactory.setHibernateProperties(hibernateProperties);
return sessionFactory;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(DB_DRIVER);
dataSource.setUrl(DB_URL);
dataSource.setUsername(DB_USERNAME);
dataSource.setPassword(DB_PASSWORD);
return dataSource;
}
#Bean
public HibernateTransactionManager transactionManager() {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory().getObject());
return txManager;
}
#Bean
public InternalResourceViewResolver jspViewResolver() {
InternalResourceViewResolver resolver= new InternalResourceViewResolver();
resolver.setPrefix("/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
package pack_cap_Controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import pack_cap_Model.Customer;
import pack_cap_Service.CaP_Service;
#RestController
#CrossOrigin(origins="http://localhost:4200")
#RequestMapping(value="/api")
public class Controller {
#Autowired
private CaP_Service capservice;
#GetMapping("customers-list")
public List<Customer> allcustomers() {
return capservice.getCustomers();
}
application.properties
# Database
db.driver= com.mysql.cj.jdbc.Driver
db.url= jdbc:mysql://localhost:3306/acq
db.username=root
db.password=123456
# Hibernate
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql=true
hibernate.hbm2ddl.auto=update
entitymanager.packagesToScan=pack_cap_Model
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
I solved it, the problem was in the Spring Boot directory structure; I had wrong the structure of the different folders of the project.

Pros and cons of changing autocommit=true in spring boot application?

To me it looks like autocommit is completely overridden with Spring-Hibernate configuration and this property absolutely doesn't play any role in such a configuration but I would like to confirm that somehow.
Spring boot 1.5.10.RELEASE version. Database is PostgreSQL 9.5.6.
Datasource configuration
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManagerFactory;
import java.util.Properties;
#Configuration
public class DataSourceConfig {
private Logger logger = LoggerFactory.getLogger(DataSourceConfig.class);
#Bean(name = "dataSource")
public DriverManagerDataSource dataSource() {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName(driver);
driverManagerDataSource.setUrl(url);
driverManagerDataSource.setUsername(username);
driverManagerDataSource.setPassword(password);
return driverManagerDataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { domainModelNamespace });
em.setJpaProperties(additionalProperties());
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.show_sql", showSql);
properties.setProperty("hibernate.dialect", dialect);
properties.setProperty("hibernate.hbm2ddl.auto", hbm2ddl);
return properties;
}
}
And here is test method
import com.phonebook.IntegrationTest;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import javax.sql.DataSource;
import java.sql.SQLException;
public class DataSourceTesting extends IntegrationTest{
#Autowired
private DataSource dataSource;
Logger logger = LoggerFactory.getLogger(DataSourceTesting.class);
#Test
public void testDataSourceConnectionProperties() throws SQLException {
boolean autoCommit = dataSource.getConnection().getAutoCommit();
Assert.assertFalse(autoCommit);
}
}
The question is what autocommit means in this context? In test it is on
true
as well as in #Transactional method checking it in debug mode.

#Transaction not working on multi datasource Spring Boot + MyBatis application

I'm trying to configure Spring Boot + MyBatis application which should work with several datasources. I tried to do it similar to sample here.
Querying and updating data is working, but when I wrote the unit test, I found that #Transactional is not working. Transactions must work between all databases. It means that, if one method with #Transactional make updates on both databases then everything should rollback in case of exception.
It is a sample application for testing purposes and co-workers. After successful configuring the new applications will be configured and developed in similar manner.
Maven:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.aze.mybatis</groupId>
<artifactId>sample-one</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Below are the configuration classes:
package com.aze.mybatis.sampleone;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
#SpringBootApplication
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
Config to database BSCS (Oracle)
package com.aze.mybatis.sampleone.config;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
#Configuration
#MapperScan(basePackages = "com.aze.mybatis.sampleone.dao", annotationClass = BscsDataSource.class, sqlSessionFactoryRef = BscsDatabaseConfig.SQL_SESSION_FACTORY_NAME)
#EnableTransactionManagement
public class BscsDatabaseConfig {
static final String SQL_SESSION_FACTORY_NAME = "sessionFactoryBscs";
private static final String TX_MANAGER = "txManagerBscs";
#Bean(name = "dataSourceBscs")
#ConfigurationProperties(prefix = "bscs.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = TX_MANAGER)
public PlatformTransactionManager txManagerBscs() {
return new DataSourceTransactionManager(dataSource());
}
#Bean(name = BscsDatabaseConfig.SQL_SESSION_FACTORY_NAME)
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
return sqlSessionFactoryBean.getObject();
}
}
Config to database ONSUBS (Oracle)
package com.aze.mybatis.sampleone.config;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
#Configuration
#MapperScan(basePackages = "com.aze.mybatis.sampleone.dao", annotationClass = OnsubsDataSource.class, sqlSessionFactoryRef = OnsubsDatabaseConfig.SQL_SESSION_FACTORY_NAME)
#EnableTransactionManagement
public class OnsubsDatabaseConfig {
static final String SQL_SESSION_FACTORY_NAME = "sessionFactoryOnsubs";
private static final String TX_MANAGER = "txManagerOnsubs";
#Bean(name = "dataSourceOnsubs")
#Primary
#ConfigurationProperties(prefix = "onsubs.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = TX_MANAGER)
#Primary
public PlatformTransactionManager txManagerOnsubs() {
return new DataSourceTransactionManager(dataSource());
}
#Bean(name = OnsubsDatabaseConfig.SQL_SESSION_FACTORY_NAME)
#Primary
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
return sqlSessionFactoryBean.getObject();
}
}
Annotation for BSCS:
package com.aze.mybatis.sampleone.config;
public #interface BscsDataSource {
}
and ONSUBS:
package com.aze.mybatis.sampleone.config;
public #interface OnsubsDataSource {
}
Mapper interface that should work with ONSUBS:
package com.aze.mybatis.sampleone.dao;
import com.aze.mybatis.sampleone.config.OnsubsDataSource;
import com.aze.mybatis.sampleone.domain.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
#Mapper
#OnsubsDataSource
public interface PaymentDao {
Payment getPaymentById(#Param("paymentId") Integer paymentId);
}
and BSCS:
package com.aze.mybatis.sampleone.dao;
import com.aze.mybatis.sampleone.config.BscsDataSource;
import com.aze.mybatis.sampleone.domain.PostpaidBalance;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
#Mapper
#BscsDataSource
public interface PostpaidCustomerDao {
PostpaidBalance getPostpaidBalance(#Param("customerId") Integer customerId);
// BigDecimal amount may be used as second parameter, but I want to show, how to work with two parameters where second is object
void updateDepositAmount(#Param("customerId") Integer customerId, #Param("balance") PostpaidBalance postpaidBalance);
void updateAzFdlLastModUser(#Param("customerId") Integer customerId, #Param("username") String username);
}
Below is a code with #Transactional
package com.aze.mybatis.sampleone.service;
import com.aze.mybatis.sampleone.dao.PaymentDao;
import com.aze.mybatis.sampleone.dao.PostpaidCustomerDao;
import com.aze.mybatis.sampleone.domain.Payment;
import com.aze.mybatis.sampleone.domain.PostpaidBalance;
import com.aze.mybatis.sampleone.exception.DataNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
#Service
public class PaymentServiceImpl implements PaymentService {
private static final String MIN_DEPOSIT_AMOUNT = "150";
#Autowired
private PaymentDao paymentDao;
#Autowired
private PostpaidCustomerDao postpaidCustomerDao;
#Override
public PostpaidBalance getPostpaidBalance(Integer customerId) {
PostpaidBalance balance = postpaidCustomerDao.getPostpaidBalance(customerId);
if (balance == null) {
throw new DataNotFoundException(String.format("Can't find any balance information for customer with customer_id = %d", customerId));
}
return balance;
}
// Note. By default rolling back on RuntimeException and Error but not on checked exceptions
// If you want to rollback on check exception too then add "rollbackFor = Exception.class"
#Transactional(rollbackFor = Exception.class)
#Override
public void updateDepositAmount(Integer customerId, PostpaidBalance postpaidBalance, String username) {
postpaidCustomerDao.updateDepositAmount(customerId, postpaidBalance);
// In case of #Transactional annotation, you can use method from the same class if it doesn't change data on database
PostpaidBalance balance = getPostpaidBalance(customerId);
// This logic is for showing that how the #Transactional annotation works.
// Because of the exception, the previous transaction will rollback
if (balance.getDeposit().compareTo(new BigDecimal(MIN_DEPOSIT_AMOUNT)) == -1) {
throw new IllegalArgumentException("The customer can not have deposit less than " + MIN_DEPOSIT_AMOUNT);
}
// In case of #Transactional annotation, you must not (!!!) use method from the same (!) class if it changes data on database
// That is why, postpaidCustomerDao.updateAzFdlLastModUser() used here instead of this.updateAzFdlLastModUser()
postpaidCustomerDao.updateAzFdlLastModUser(customerId, username);
// If there is no exception, the transaction will commit
}
}
Below is the unit test code:
package com.aze.mybatis.sampleone.service;
import com.aze.mybatis.sampleone.Application;
import com.aze.mybatis.sampleone.config.BscsDatabaseConfig;
import com.aze.mybatis.sampleone.config.OnsubsDatabaseConfig;
import com.aze.mybatis.sampleone.domain.PostpaidBalance;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.math.BigDecimal;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {Application.class, OnsubsDatabaseConfig.class, BscsDatabaseConfig.class})
#TestPropertySource(locations= "classpath:application.properties")
public class PaymentServiceImplTest extends Assert {
// My goal is not to write a full and right unit tests, but just show you examples of working with MyBatis
#Autowired
private PaymentService paymentService;
#Before
public void setUp() throws Exception {
assert paymentService != null;
}
#Test
public void updateDepositAmount() throws Exception {
final int customerId = 4301887; // not recommended way. Just for sample
final String username = "ITCSC";
boolean exceptionRaised = false;
PostpaidBalance balance = paymentService.getPostpaidBalance(customerId);
assertTrue("Find customer with deposit = 0", balance.getDeposit().compareTo(BigDecimal.ZERO) == 0);
balance.setDeposit(BigDecimal.TEN);
try {
paymentService.updateDepositAmount(customerId, balance, username);
} catch (Exception e) {
exceptionRaised = true;
}
assertTrue(exceptionRaised);
balance = paymentService.getPostpaidBalance(customerId);
// We check that transaction was rollback and amount was not changed
assertTrue(balance.getDeposit().compareTo(BigDecimal.ZERO) == 0);
final BigDecimal minDepositAmount = new BigDecimal("150");
balance.setDeposit(minDepositAmount);
paymentService.updateDepositAmount(customerId, balance, username);
balance = paymentService.getPostpaidBalance(customerId);
assertTrue(balance.getDeposit().compareTo(minDepositAmount) != -1);
}
}
Unit test fails on assertTrue(balance.getDeposit().compareTo(BigDecimal.ZERO) == 0);. The I check the database and see that first update postpaidCustomerDao.updateDepositAmount(customerId, postpaidBalance); was not rollback despite the #Transactional annotation.
Please help to solve problem.
If you have multiple TransactionManagers, you'll need to reference the one you want to use for #Transactional using Bean names or Qualifiers.
In your Java Config:
#Bean("myTM")
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(myDatasource());
}
In Services:
#Transactional("myTM")
public void insertWithException(JdbcTemplate jdbcTemplate) {
}
I debug'd my app to see how Spring chooses the TransactionManager. This is done in org.springframework.transaction.interceptor.TransactionAspectSupport#determineTransactionManager
/**
* Determine the specific transaction manager to use for the given transaction.
*/
protected PlatformTransactionManager determineTransactionManager(TransactionAttribute txAttr) {
// Do not attempt to lookup tx manager if no tx attributes are set
if (txAttr == null || this.beanFactory == null) {
return getTransactionManager();
}
String qualifier = txAttr.getQualifier();
if (StringUtils.hasText(qualifier)) {
return determineQualifiedTransactionManager(qualifier);
}
else if (StringUtils.hasText(this.transactionManagerBeanName)) {
return determineQualifiedTransactionManager(this.transactionManagerBeanName);
}
else {
PlatformTransactionManager defaultTransactionManager = getTransactionManager();
if (defaultTransactionManager == null) {
defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
if (defaultTransactionManager == null) {
defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
this.transactionManagerCache.putIfAbsent(
DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
}
}
return defaultTransactionManager;
}
}
So it's just getting the default primary PlatformTransactionManager bean.

spring boot configuration properties not working

This is driving me up the wall trying to get spring boot #configurationproperties annotation working. So hoping someone can shed some light on this for me as to what I am doing wrong.
I have a spring boot application and it contains a application.properties on the classpath. It has a value in there of
server.contextPath=/test/v1
server.port=8080
spring.profiles.active=dev
vendors=me
I have a application.class which has the spring boot annotation and sits at the top of my package hierarchy
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
#EnableConfigurationProperties
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I am trying to map the property vendors into a configurationproperties bean as below
package com.test.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
#Component
#PropertySource("classpath:application.properties")
#ConfigurationProperties
public class GlobalProperties {
private String vendors;
public String getVendors() {
return vendors;
}
public void setVendors(String vendors) {
this.vendors = vendors;
}
}
and then call this bean from my rest controller. I know it resolves the property as when i rename it the server fails to start. In the code below the props bean is not getting autowired and is null. //code ommitted for brevity
package com.test.controller;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.test.config.GlobalProperties;
#RestController
#Component
public class MController {
//TODO should be wired in from properties file
#Autowired
GlobalProperties props;
private boolean vendorUnknown(String vendor) {
if(props.getAllowedVendor().equalsIgnoreCase(vendor)) {
return true;
}
return false;
}
#RequestMapping(value = "/test/{id}", method = RequestMethod.GET, produces = { "application/json" })
public ResponseEntity<?> getStatus(
#PathVariable String id) {
//#RequestBody Bookmark input
if(vendorUnknown("me")) {
System.out.println("found");
};
return ResponseEntity.noContent().build();
}
}
Anybody point me to what i have done wrong please?
UPDATE:
changed the code above to be a more simplistic version with a test class to recreate the issue. See below for my pom.xml and test class
Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.me.test</groupId>
<artifactId>test-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- tag::actuator[] -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- end::actuator[] -->
<!-- tag::tests[] -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- end::tests[] -->
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Test class:
package com.test.controller;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class MControllerTest {
#Autowired
private MockMvc mockMvc;
#InjectMocks
MController testController;
#Before
public void setup() {
// this must be called for the #Mock annotations above to be processed
// and for the mock service to be injected into the controller under
// test.
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(testController).build();
}
#Test
public void testPropertiesMsg() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/test/1").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
Remove #PropertySource. You can keep #Component, or if you don't, specify #EnableConfigurationProperties(GlobalProperties.class).
Don't need #Component on testController. But I think your issue is where you're calling vendorUnknown method from. It's not shown in your code. If calling from the constructor, then the bean initialization has not completed yet, and GlobalProperties props is indeed null.
Edit:
Based on OP's edit, here's a fully working solution.
DemoApplication.java:
#SpringBootApplication
#EnableConfigurationProperties
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
DemoController.java:
#RestController
public class DemoController {
#Autowired
private DemoProperties props;
#GetMapping(value = "/", produces = APPLICATION_JSON_VALUE)
public ResponseEntity<?> getVendor() {
return ResponseEntity.ok(props.getVendors());
}
}
DemoProperties.java:
#ConfigurationProperties
#Component
public class DemoProperties {
private String vendors;
public String getVendors() {
return vendors;
}
public void setVendors(String vendors) {
this.vendors = vendors;
}
}
application.properties:
vendors=me
DemoControllerTest.java:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DemoControllerTest {
#Autowired
TestRestTemplate restTemplate;
#Test
public void testGenVendor() throws Exception {
String vendor = restTemplate.getForObject("/", String.class);
assertThat(vendor).isEqualTo("me");
}
}
Problems with OP's code:
MockMvc doesn't use the main class, so EnableConfigurationPropertiesis not processed. MockMvc or WebMvcTest is designed to test the web layer (duh!), not the whole app. Using either of those. the properties bean should be set by the test.
InjectMocks fails silently. See Why You Should Not Use InjectMocks Annotation to Autowire Fields.
Add #EnableConfigurationProperties to your Application class to enable scanning ConfigurationProperties beans.

java.lang.NoClassDefFoundError: org/springframework/orm/hibernate4/SpringSessionContext

I am try to implement SpringMVC 4 and Hibernate 4 integration with annotation in my project but I am getting this error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in com.config.ApplicationContextConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.hibernate.SessionFactory]: Factory method 'getSessionFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: org/springframework/orm/hibernate4/SpringSessionContext
My Config file is::
import javax.sql.DataSource;
import java.util.Properties;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.orm.hibernate4.LocalSessionFactoryBuilder;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.orm.hibernate4.SpringSessionContext;
import com.dao.UserDao;
import com.dao.UserDaoImpl;
import com.pojo.User;
#Configuration
#ComponentScan("com.config")
#EnableTransactionManagement
public class ApplicationContextConfig {
#Bean(name = "viewResolver")
public InternalResourceViewResolver getViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean(name = "dataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/usersdb");
dataSource.setUsername("root");
dataSource.setPassword("");
return dataSource;
}
#Autowired
#Bean(name = "sessionFactory")
public SessionFactory getSessionFactory(DataSource dataSource) {
LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(dataSource);
sessionBuilder.addAnnotatedClasses(User.class);
return sessionBuilder.buildSessionFactory();
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
return properties;
}
#Autowired
#Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager(
SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager(
sessionFactory);
return transactionManager;
}
#Autowired
#Bean(name = "userDao")
public UserDao getUserDao(SessionFactory sessionFactory) {
return new UserDaoImpl(sessionFactory);
}
}
It appears that you need, but do not have spring-orm-4.3.0.RELEASE.jar on your classpath. (I have identified the 4.3.0 version, you may be using a different 4.X version).
If you are using Maven, add the appropriate dependency to your pom.xml.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.0.RELEASE</version>
<scope>runtime</scope>
</dependency>
If you are not using Maven, you can download the jar file here.
For information on setting the classpath see this section of the Java Tutorial.

Resources