How to set up a unit test for spring data with service and repository? - java

I have checked many SO comments and the docs for spring data and unit testing but I cant get this to work and I dont know why its not working.
I have a junit test class that looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class DealServiceTest {
#Configuration
static class ContextConfiguration {
// this bean will be injected into the OrderServiceTest class
#Bean
public DealService orderService() {
DealService dealService = new DealService();
// set properties, etc.
return dealService;
}
#Bean
public EmployeeService employeeService(){
EmployeeService employeeService = new EmployeeService();
return employeeService;
}
}
#Autowired
DealService dealService;
#Autowired
EmployeeService employeeService;
#Test
public void createDeal() throws ServiceException {
Employee employee = new Employee("Daniel", "tuttle", "danielptm#me.com", "dannyboy", "secret password", 23.234, 23.23);
Deal d = dealService.createDeal("ADSF/ADSF/cat.jpg", "A title goes here", "A deal description", 23.22, "Name of business", 23.23,23.23, employee, "USA" );
Assert.assertNotNull(d);
}
}
And then I have my service class that looks like this
#Service
public class DealService {
#Autowired
private DealRepository dealRepository;
public Deal createDeal(String image, String title, String description, double distance, String location, double targetLat, double targetLong, Employee employee, String country) throws ServiceException {
Deal deal = new Deal(image, title, description, distance, location, targetLat, targetLong, employee, country);
try {
return dealRepository.save(deal);
}catch(Exception e){
throw new ServiceException("Could not create a deal: "+deal.toString(), e);
}
}
public Deal updateDeal(Deal d) throws ServiceException {
try{
return dealRepository.save(d);
}catch(Exception e){
throw new ServiceException("Could not update deal at this time: "+d.toString(),e);
}
}
public List<Deal> getAllDealsForEmployeeId(Employee employee) throws ServiceException {
try{
return dealRepository.getAllDealsBy_employeeId(employee.getId());
}catch(Exception e){
throw new ServiceException("Could not get deals for employee: "+employee.getId(), e);
}
}
}
And then my repository:
*/
public interface DealRepository extends CrudRepository<Deal, Long>{
public List<Deal> getDealsBy_country(String country);
public List<Deal> getAllDealsBy_employeeId(Long id);
}
My config file looks like this:
#Configuration
#EnableJpaRepositories("com.globati.repository")
#EnableTransactionManagement
public class InfrastructureConfig {
#Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.mysql.jdbc.Driver");
config.setJdbcUrl("jdbc:mysql://localhost:3306/DatabaseProject");
config.setUsername("awesome");
config.setPassword("database");
return new HikariDataSource(config);
}
// #Bean
// public DataSource derbyDataSource(){
// HikariConfig config = new HikariConfig();
// config.setDriverClassName("jdbc:derby:memory:dataSource");
// config.setJdbcUrl("jdbc:derby://localhost:1527/myDB;create=true");
// config.setUsername("awesome");
// config.setPassword("database");
//
// return new HikariDataSource(config);
//
// }
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.MYSQL);
adapter.setGenerateDdl(true);
return adapter;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource()); //Get data source config here!
factory.setJpaVendorAdapter(jpaVendorAdapter());
factory.setPackagesToScan("com.globati.model");
return factory;
}
}
But I get this error.
java.lang.IllegalStateException: Failed to load ApplicationContext ...
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean found for dependency
[com.globati.repository.DealRepository]: expected at least 1 bean
which qualifies as autowire candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Any suggestions for how I can successfully do unit testing with spring data, junit and with my service and repositories would be greatly appreciated. Thanks!

For a repository bean to be injected,
You need to enable Repositories, using one of the spring-data annotations. So add #Enable*Repositories to your configuration class
You also need dB factories and other related beans configured. I am using Mongo and I have mongoDbFactory bean configured
And for the most part your test configuration should look like your main configuration except for unnecessary bean replaced by mock implementations
UPDATE
Here is my code (sorry mine is in mongo, I think you can relate)
#Configuration
#WebAppConfiguration
#ComponentScan(basePackages = "com.amanu.csa",
excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = WebConfig.class))
#EnableMongoRepositories(repositoryImplementationPostfix = "CustomImpl")
class TestConfig {
#Bean
Mongo mongo() throws Exception {
return new MongoClient("localhost")
}
#Bean
MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(mongo(), "csa_test")
}
#Bean
MongoTemplate mongoTemplate() throws Exception {
MongoTemplate template = new MongoTemplate(mongoDbFactory())
template.setWriteResultChecking(WriteResultChecking.EXCEPTION)
return template
}
}
That is my test config file... As you can see it explicitly excludes my main configuration file.
#ContextConfiguration(classes = TestConfig)
#RunWith(SpringRunner.class)
class OrganizationServiceTest {
#Autowired
OrganizationService organizationService
#Test
void testRegister() {
def org = new Organization()
//...
organizationService.register(org)
// ...
}
And that is my test class. It refers to the test config, and I recommend using named config classes. You can put common options onto a super class and extend those and use them for your tests.
I hope this helps

You can try to add
#ActiveProfiles("your spring profile")
In addition I would suggest to use an embedded test db like flapdoodle (https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo)

You could either :
Mock your repository methods (Using Mockito for example)
Using an embedded database for your unit tests

Related

Access mulitple Datasource with JdbcTemplate results in error

I have two projects one with DAO classes and Model and another with Rest Controller
Project A : DAO Classes + Model
Project B : Rest Controller
Project A
application.properties:
spring.abcDatasource.url=
spring.abcDatasource.username=
spring.abcDatasource.password=
spring.abcDatasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.xyzDatasource.url=
spring.xyzDatasource.username=
spring.xyzDatasource.password=
spring.xyzDatasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.initialize=false
DBConfiguration.java
#Configuration
public class DBConfiguration {
#Primary
#Bean(name = "abcDS")
#ConfigurationProperties(prefix = "spring.abcDatasource")
public DataSource abcDS() {
return DataSourceBuilder.create().build();
}
#Bean(name = "abcJdbc")
public JdbcTemplate abcJdbcTemplate(#Qualifier("abcDS") DataSource abcDS) {
return new JdbcTemplate(abcDS);
}
#Bean(name = "xyzDS")
#ConfigurationProperties(prefix = "spring.xyzDatasource")
public DataSource xyzDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "xyzJdbc")
public JdbcTemplate ebsJdbcTemplate(#Qualifier("xyzDS") DataSource xyzDatasource) {
return new JdbcTemplate(xyzDatasource);
}
}
AlphaDAO.Java
#Repository
public class AlphaDAO{
#Autowired
#Qualifier("abcJdbc")
private JdbcTemplate abcJdbc;
#Autowired
#Qualifier("xyzJdbc")
private JdbcTemplate xyzJdbc;
SqlParameterSource namedParameters;
public Collection<Alpha> findAll(String owner){
String sql = "SELECT * from alpha where OWNER in (:owner)" ;
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(abcJdbc.getDataSource());
namedParameters = new MapSqlParameterSource("owner", owner);
List<Alpha> list = namedParameterJdbcTemplate.query(sql,namedParameters,
new BeanPropertyRowMapper(Alpha.class));
return list;
}
Project B Rest Controller :
AlphaServiceApplication.java
#SpringBootApplication
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class AlphaServiceApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(AlphaServiceApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(AlphaServiceApplication.class, args);
}
}
AlphaServiceController.java
#RestController
public class AlphaServiceController {
private static final Logger logger = LoggerFactory.getLogger(AlphaServiceController.class);
#Autowired
AlphaDAO dao;
#CrossOrigin(origins = "http://localhost:4200")
#RequestMapping("/alpha")
public Collection<Alpha> index(#RequestBody String owner) {
return dao.findAll(owner);
}
If I try to run the rest controller I am getting the error saying
APPLICATION FAILED TO START
Description:
Field dao in com.xyz.web.wip.AlphaService.AlphaServiceController required a bean of type 'com.xyz.comp.wip.alphaComp.dao.AlphaDAO' that could not be found.
Action:
Consider defining a bean of type 'com.xyz.comp.wip.alphaComp.dao.AlphaDAO' in your configuration.
Your AlphaDao class doesnt make much sense, you are trying to autowire two fields but you still have a constructor.
Spring cant build the object because there is no qualifier on the constructor.
You can either do constructor injection or field injection but you shouldn’t use both.
I would recommend using constructor injection.
#Repository
public class AlphaDAO{
private final JdbcTemplate abcJdbc;
private final JdbcTemplate xyzJdbc;
#Autowired
public AlphaDAO(
#Qualifier("abcJdbc") JdbcTemplate abcJdbc,
#Qualifier("xyzJdbc") JdbcTemplate xyzJdbc){
this.abcJdbc = abcJdbc;
this.xyzJdbc = xyzJdbc;
}
Also remove your #Bean method from the controller.
Since the DAO classes and Rest Controller is in different packages. Added scanBasePackages to #SpringBootApplication annotation with one level up worked fine.
AlphaServiceApplication.java
#SpringBootApplication(scanBasePackages = { "com.xyz" })

How to test #Cacheable?

I am struggling with testing #Cacheable within a Spring Boot Integration Test. This is my second day learning how to do Integration Tests and all of the examples I have found use older versions. I also saw an example of assetEquals("some value", is()) but nothing with an import statement to know which dependency "is" belongs to. The test fails at the second
This is my integration test....
#RunWith(SpringRunner.class)
#DataJpaTest // used for other methods
#SpringBootTest(classes = TestApplication.class)
#SqlGroup({
#Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD,
scripts = "classpath:data/Setting.sql") })
public class SettingRepositoryIT {
#Mock
private SettingRepository settingRepository;
#Autowired
private Cache applicationCache;
#Test
public void testCachedMethodInvocation() {
List<Setting> firstList = new ArrayList<>();
Setting settingOne = new Setting();
settingOne.setKey("first");
settingOne.setValue("method invocation");
firstList.add(settingOne);
List<Setting> secondList = new ArrayList<>();
Setting settingTwo = new Setting();
settingTwo.setKey("second");
settingTwo.setValue("method invocation");
secondList.add(settingTwo);
// Set up the mock to return *different* objects for the first and second call
Mockito.when(settingRepository.findAllFeaturedFragrances()).thenReturn(firstList, secondList);
// First invocation returns object returned by the method
List<Setting> result = settingRepository.findAllFeaturedFragrances();
assertEquals("first", result.get(0).getKey());
// Second invocation should return cached value, *not* second (as set up above)
List<Setting> resultTwo = settingRepository.findAllFeaturedFragrances();
assertEquals("first", resultTwo.get(0).getKey()); // test fails here as the actual is "second."
// Verify repository method was invoked once
Mockito.verify(settingRepository, Mockito.times(1)).findAllFeaturedFragrances();
assertNotNull(applicationCache.get("findAllFeaturedFragrances"));
// Third invocation with different key is triggers the second invocation of the repo method
List<Setting> resultThree = settingRepository.findAllFeaturedFragrances();
assertEquals(resultThree.get(0).getKey(), "second");
}
}
ApplicationContext, components, entities, repositories and service layer for tests. The reason why I do it this way is because this maven module is used in other modules as a dependency.
#ComponentScan({ "com.persistence_common.config", "com.persistence_common.services" })
#EntityScan(basePackages = { "com.persistence_common.entities" })
#EnableJpaRepositories(basePackages = { "com.persistence_common.repositories" })
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Cache config....
#Configuration
#EnableCaching
public class CacheConfig {
public static final String APPLICATION_CACHE = "applicationCache";
#Bean
public FilterRegistrationBean registerOpenSessionInViewFilterBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
OpenEntityManagerInViewFilter filter = new OpenEntityManagerInViewFilter();
registrationBean.setFilter(filter);
registrationBean.setOrder(5);
return registrationBean;
}
#Bean
public Cache applicationCache() {
return new GuavaCache(APPLICATION_CACHE, CacheBuilder.newBuilder()
.expireAfterWrite(30, TimeUnit.DAYS)
.build());
}
}
The repository under test....
public interface SettingRepository extends JpaRepository<Setting, Integer> {
#Query(nativeQuery = true, value = "SELECT * FROM Setting WHERE name = 'featured_fragrance'")
#Cacheable(value = CacheConfig.APPLICATION_CACHE, key = "#root.methodName")
List<Setting> findAllFeaturedFragrances();
}
The first problem with SettingRepositoryIT is, the #Mock anotation on the field settingRepository. This is paradox for any normal-test, integration-test or any else.
You should let Spring bring in the dependencies for the class-under-test, which is SettingRepository in your case.
Please look at this example how #Autowired is used for the class-under-test, which is OrderService in this example:
#RunWith(SpringRunner.class)
// ApplicationContext will be loaded from the
// static nested Config class
#ContextConfiguration
public class OrderServiceTest {
#Configuration
static class Config {
// this bean will be injected into the OrderServiceTest class
#Bean
public OrderService orderService() {
OrderService orderService = new OrderServiceImpl();
// set properties, etc.
return orderService;
}
}
#Autowired
private OrderService orderService;
#Test
public void testOrderService() {
// test the orderService
}
}
Go for the documentation with the full example: § 15. Integration Testing
The second problem is that you do not have to test #Cachable. You should only test your implementation. Here is a very good example from Oliver Gierke on how you should test it: How to test Spring's declarative caching support on Spring Data repositories?
In my case I wanted to validate the expression in the unless expression in the #Cacheable annotation, so I think it makes perfect sense and I'm not testing Spring's code.
I managed to test it without using Spring Boot, so it is plain Spring test:
#RunWith(SpringRunner.class)
#ContextConfiguration
public class MyTest {
private static MyCacheableInterface myCacheableInterfaceMock = mock(MyCacheableInterface.class);
#Configuration
#EnableCaching
static class Config {
#Bean
public MyCacheableInterface myCacheableInterface() {
return myCacheableInterfaceMock;
}
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("myObject");
}
}
#Autowired
private MyCacheableInterface myCacheableInterface;
#Test
public void test() {
when(myCacheableInterfaceMock.businessMethod(anyString())).then(i -> {
List<MyObject> list = new ArrayList<>();
list.add(new MyObject(new Result("Y")));
return list;
});
myCacheableInterface.businessMethod("test");
verify(myCacheableInterfaceMock).businessMethod(anyString());
myCacheableInterface.businessMethod("test");
verifyNoMoreInteractions(myCacheableInterfaceMock);
}
}
In MyCacheableInterface I have the following annotation:
public interface MyCacheableInterface {
#Cacheable(value = "myObject", unless = "#result.?[Result.getSuccess() != 'Y'].size() == #result.size()")
List<MyObject> businessMethod(String authorization);
}

Spring boot - how to configure multiple datasources

I am trying to setup multiple data sources(MySql, Postgres & Oracle) using Spring boot. I am not using JPA. Setting up with a JdbcTemplate.
I have tried setting up something like this.
application.properties
spring.datasource.test-oracle.username=test-oracle
spring.datasource.test-oracle.password=test-password
spring.datasource.test-oracle.url=dburl/test
spring.datasource.test-oracle.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.int-oracle.username=int-oracle
spring.datasource.int-oracle.password=int-password
spring.datasource.int-oracle.url=dburl/int
spring.datasource.int-oracle.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.d.int-mysql.username=user
spring.datasource.d.int-mysql.password=password
spring.datasource.d.int-mysql.url=dburl/d
spring.datasource.d.int-mysql.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.m.int-mysql.username=user
spring.datasource.m.int-mysql.password=password
spring.datasource.m.int-mysql.url=dburl/m
spring.datasource.m.int-mysql.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.d.test-mysql.username=user
spring.datasource.d.test-mysql.password=password
spring.datasource.d.test-mysql.url=dburl/d
spring.datasource.d.test-mysql.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.m.test-mysql.username=user
spring.datasource.m.test-mysql.password=password
spring.datasource.m.test-mysql.url=dburl/m
spring.datasource.m.test-mysql.driver-class-name=com.mysql.jdbc.Driver
MySqlConfiguration.java
#Configuration
public class MySqlConfiguration() {
#Bean(name = "dMySql")
#ConfigurationProperties(prefix = "spring.datasource.d.int-mysql")
public DataSource mysqlDrupalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "dJdbc")
public JdbcTemplate drupalJdbcTemplate(DataSource dMySql) {
return new JdbcTemplate(dMySql);
}
#Bean(name = "mMySql")
#ConfigurationProperties(prefix = "spring.datasource.m.int-mysql")
public DataSource mysqlDrupalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "mJdbc")
public JdbcTemplate drupalJdbcTemplate(DataSource mMySql) {
return new JdbcTemplate(mMySql);
}
}
OracleConfiguration.java
#Configuration
public class OracleConfiguration {
#Primary
#Bean(name = "tOracle")
#ConfigurationProperties(prefix = "spring.datasource.test-oracle")
public DataSource heOracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "tOracleJdbc")
public JdbcTemplate jdbcTemplate(DataSource tOracle) {
return new JdbcTemplate(tOracle);
}
#Bean(name = "iOracle")
#ConfigurationProperties(prefix = "spring.datasource.int-oracle")
public DataSource heOracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "iOracleJdbc")
public JdbcTemplate jdbcTemplate(DataSource iOracle) {
return new JdbcTemplate(iOracle);
}
}
I am not sure if the above is the correct way to go about this. When I use #Primary as per the boot docs, the Bean that has #Primary is always used. Then I use the configurations in my DAO implementations like this
One of the DAO Implementation
#Repository
public class DAOImpl implements DAOInterface {
#Autowired
#Qualifier("dJdbc")
private JdbcTemplate jdbc;
#Override
public Map<String, Object> getBasicStudentInfo(String MAIL) {
return jdbc.queryForMap(GET_BASIC_STUDENT_INFO, new Object[]{MAIL});
}
How do I go about doing this.? I did see many articles which is about mutliple datasources but unfortunately the examples or solutions don't suite me.
Further to this I need to be able to query against the DB's based on some user input. So if a user provides an environment e.g., "test" or "int", how can I trigger the correct properties based on that input.
I understand that Environment is #Autowired into Spring boot and I can intercept the user input, but unsure how I should provide the plumbing between the user input and the DAO configurations.
If something is unclear or needs a bit more explanation from my side or need more code I can provide that. Any help to resolve this situation would be appreciated.Thanks
Here is complete solution to your problem ...
Your configuration classes will look like this :
MySqlConfiguration.java
#Configuration
public class MySqlConfiguration {
#Bean(name = "dMySql")
#ConfigurationProperties(prefix = "spring.datasource.d.int-mysql")
public DataSource mysqlDrupalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "dJdbc")
public JdbcTemplate drupalJdbcTemplate(#Qualifier("dMySql") DataSource dMySql) {
return new JdbcTemplate(dMySql);
}
#Bean(name = "mMySql")
#ConfigurationProperties(prefix = "spring.datasource.m.int-mysql")
public DataSource mysqlDrupalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "mJdbc")
public JdbcTemplate drupalJdbcTemplate(#Qualifier("mMySql") DataSource mMySql) {
return new JdbcTemplate(mMySql);
}
}
OracleConfiguration.java
#Configuration
public class OracleConfiguration {
#Primary
#Bean(name = "tOracle")
#ConfigurationProperties(prefix = "spring.datasource.test-oracle")
public DataSource heOracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "tOracleJdbc")
public JdbcTemplate jdbcTemplate(#Qualifier("tOracle") DataSource tOracle) {
return new JdbcTemplate(tOracle);
}
#Bean(name = "iOracle")
#ConfigurationProperties(prefix = "spring.datasource.int-oracle")
public DataSource heOracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "iOracleJdbc")
public JdbcTemplate jdbcTemplate(#Qualifier("iOracle") DataSource iOracle) {
return new JdbcTemplate(iOracle);
}
}
and in your DAO class , you can autowire the JdbcTemplate like this :
#Repository
public class DAOImpl implements DAOInterface {
#Autowired
#Qualifier("dJdbc")
private JdbcTemplate dJdbc;
#Autowired
#Qualifier("mJdbc")
private JdbcTemplate mJdbc;
#Autowired
#Qualifier("tOracleJdbc")
private JdbcTemplate tOracleJdbc;
#Autowired
#Qualifier("iOracleJdbc")
private JdbcTemplate iOracleJdbc;
#Override
public Map<String, Object> getBasicStudentInfo(String MAIL) {
return dJdbc.queryForMap(GET_BASIC_STUDENT_INFO, new Object[]{MAIL});
}
.
.
.
}
Note: Make Sure to annotate one of DataSource with #Primary annotation
My setup: spring-boot version 1.2.5.RELEASE
I succeeded in running a setup like this, with the jdbc being created with the correct DataSources by adding a #Qualifier in each JDBC method creation
So, for every JDBC method you should match the qualifying datasource like this
#Bean(name = "dJdbc")
public JdbcTemplate drupalJdbcTemplate(#Qualifier("dMySql") DataSource dMySql) {
return new JdbcTemplate(dMySql);
}
No matter you choose for #Primary, using the #Qualifier for every JDBC should work good.
Autowiring jdbcTemplates in repositories, and using #Qualifier for them is ok also.
In your DAO you could wire in additional jdbctemplates. Then at runtime you can pick which one to use.
#Repository
public class DAOImpl implements DAOInterface {
#Autowired
#Qualifier("tOracle")
private JdbcTemplate testJdbc;
#Autowired
#Qualifier("intOracle")
private JdbcTemplate intJdbc;
#Override
public Map<String, Object> getBasicStudentInfo(String MAIL, String source) {
if ("TEST".equals(source)){
return testJdbc.queryForMap(GET_BASIC_STUDENT_INFO, new Object[]{MAIL});
}else {
return intJdbc.queryForMap(GET_BASIC_STUDENT_INFO, new Object[]{MAIL});
}
}

Spring Data CrudRepository on java based configuration - EntityManager - no transaction is in progress

I've read I believe tried all of the posts on this, but no luck in finding the right answer.
I am using java based configuration with my spring mvc project, and wanted to try Spring CrudRepository, to get away from DAOs, and that is when the whole hell broke loose:
started with "no transaction is in progress" on flush after persist:
- tried adding #Transactional to the method - none of the variations found here worked
- tried changing configuration, but since it is java based, most of the answers are xml based. no luck either.
So finally I have to ask:
How to configure my project to make CrudRepository persist, or how to create Spring EntityManager using java configuration.
This is the last version of my configuration file:
#Configuration
#ComponentScan(basePackages = { "ba.fit.vms" })
#ImportResource(value = "classpath:spring-security-context.xml")
#EnableTransactionManagement
#EnableJpaRepositories
public class AppConfig {
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocation(new ClassPathResource("/persistence.properties"));
return ppc;
}
// Security Configuration
#Bean
public KorisnickiServis korisnickiServis(){
return new KorisnickiServis();
}
#Bean
public TokenBasedRememberMeServices rememberMeServices() {
return new TokenBasedRememberMeServices("remember-me-key", korisnickiServis());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new StandardPasswordEncoder();
}
// Jpa Configuration
#Value("${dataSource.driverClassName}")
private String driver;
#Value("${dataSource.url}")
private String url;
#Value("${dataSource.username}")
private String username;
#Value("${dataSource.password}")
private String password;
#Value("${hibernate.dialect}")
private String dialect;
#Value("${hibernate.hbm2ddl.auto}")
private String hbm2ddlAuto;
#Bean
public DataSource configureDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean configureEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(configureDataSource());
entityManagerFactoryBean.setPackagesToScan("ba.fit.vms");
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, dialect);
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, hbm2ddlAuto);
//jpaProperties.put(org.hibernate.cfg.Environment.SHOW_SQL, true);
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Bean
public PlatformTransactionManager transactionManager() {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(configureEntityManagerFactory().getObject());
return transactionManager;
}
}
I've tried number of variations, but was always receiving same "no transaction is in progress" error.
Also, just a glimpse at the repos:
LokacijaRepository:
#Transactional
public interface LokacijaRepository extends CrudRepository<Lokacija, Long> {
}
And LokacijaRepositoryImpl:
#Repository
public class LokacijaRepositoryImpl implements LokacijaRepository {
protected static Logger logger = Logger.getLogger("repo");
#PersistenceContext // tried this as well(type= PersistenceContextType.EXTENDED)
private EntityManager entityManager;
#Override
#Transactional// tried number of variations here as well, like REQUIRED...
public <S extends Lokacija> S save(S entity) {
logger.debug("trying to save!");
try {
entityManager.persist(entity);
entityManager.flush();
return entity;
} catch (Exception e) {
logger.debug("error: "+ e.toString());
return null;
}
}
If you need anything else to help me figure this one out, let me know.
The problem is that you are attempting to create an implementation of LokacijaRepository (in LokacijaRepositoryImpl) while Spring Data JPA (which you have configured) is trying to do the same.
What you need to do is:
totally remove LokacijaRepositoryImpl
Either change configureEntityManagerFactory to entityManagerFactory or add entityManagerFactoryRef=configureEntityManagerFactory to #EnableJpaRepositories

#Autowired is not injecting my service in my managed bean

I'm trying to excute the following test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = DBDataManipulatorServiceTestContext.class, loader =
SpringApplicationContextLoader.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class UserInterfaceBeanTest {
#Autowired
private DBDataManipulatorService dbDataManipulatorService;
.....
#Test
public void testGetDistinctWorkPackageId() {
WorkPackage workPackage1 = new WorkPackage();
workPackage1.setWorkPackageId("TEST1");
workPackage1.setWorkPackageName("WP_1");
WorkPackage workPackage2 = new WorkPackage();
workPackage2.setWorkPackageId("TEST2");
workPackage2.setWorkPackageName("WP_2");
WorkPackage workPackage3 = new WorkPackage();
workPackage3.setWorkPackageId("TEST3");
workPackage3.setWorkPackageName("WP_3");
WorkPackage workPackage4 = new WorkPackage();
workPackage4.setWorkPackageId("TEST4");
workPackage4.setWorkPackageName("WP_4");
Aircraft aircraft1 = new Aircraft();
aircraft1.setAircraftId(50001);
aircraft1.setModel("100");
Aircraft aircraft2 = new Aircraft();
aircraft2.setAircraftId(50002);
aircraft2.setModel("100");
aircraft1.addOrUpdateWorkPackageToAircraft(workPackage1);
aircraft1.addOrUpdateWorkPackageToAircraft(workPackage3);
aircraft2.addOrUpdateWorkPackageToAircraft(workPackage2);
aircraft2.addOrUpdateWorkPackageToAircraft(workPackage3);
Map<Integer, Aircraft> aircraftsMap = new HashMap<Integer, Aircraft>();
aircraftsMap.put(50001, aircraft1);
aircraftsMap.put(50002, aircraft2);
UserInterfaceBean userInterfaceBean = new UserInterfaceBean();
userInterfaceBean.setModel("100");
aircraftsMap = dbDataManipulatorService.saveToDataBase(aircraftsMap);
List<String> workPackgesIds = userInterfaceBean.getDistinctWorkPackageId();
assertTrue(workPackgesIds.contains("TEST1"));
assertTrue(workPackgesIds.contains("TEST2"));
assertTrue(workPackgesIds.contains("TEST3"));
assertTrue(!workPackgesIds.contains("TEST4"));
}
But I get NullPointerException due to dbDataManipulatorService that is not correctly injected, my serviceContext is the following:
#ComponentScan
#EnableAutoConfiguration
#EnableBatchProcessing
#EnableJpaRepositories
public class DBDataManipulatorServiceContext {
.....
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setName("aircraft").setType(H2)
.build();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean lef = new
LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource);
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com");
return lef;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter =
new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.H2);
return hibernateJpaVendorAdapter;
}
.....
Can anyone tell me where is the problem? and how to solve it.
EDIT
Here's my DBDataManipulatorServiceImpl:
#Service
#Transactional
public class DBDataManipulatorServiceImpl implements DBDataManipulatorService {
.....
}
And here's it's interface:
public interface DBDataManipulatorService {
public AircraftRepository getAircraftRepository();
public Aircraft getAircraftById(String id);
public Map<Integer, Aircraft> saveToDataBase(Map<Integer, Aircraft> aircrafts);
public MyJob findJobByJobId(String jobId);
public WorkPackage findWorkPackageByAircraftIdAndWorkPackageId
(int aircraftId, String workPackageId);
public Aircraft findAircraftByaircraftId(int aircraftId);
public List<Aircraft> findAllAircrafts();
public List<String> findAllAvailableAircraftsModels();
public List<Aircraft> findAllAircraftsByModel(String model);
public List<WorkPackage> getWorkPackagesByAircraftModel(String model);
}
You have a #ComponentScan but you have not put the name of the package to scan from. The default behavior is to scan from the package where you have put the #ComponentScan essentially the package of your DBDataManipulatorServiceContext config class. I am guessing this is not where your DBDataManipulatorService is, if so just specify a package name to your #ComponentScan
The #Autowired annotation by default means that a bean must have been found otherwise you would have gotten a bean not found exception.
There are a few things that can cause this:
The unit test is instantiated via new DBDataManipulatorServiceContext()instead of being scanned as a Spring Bean, in this case #Autowired is ignored.
There is some XML configuration that overrides the annotations configuration, and so the bean is first created with autowiring on, and then later is discarded by a bean defined in XML with no parameter provided for the missing property
You need to mark the DBDataManipulatorService class somehow. Use #service or #component annotation over class name. This will tell spring to manage the class and thus to inject it in your test.

Categories