Create and Unit test an Util class - spring

In my application I need to make advanced search against an equipements database, so i created this function in my service interface:
public List<Equipement> findByCriterias(SearchEquipement searchEquipement) ;
I thought that the best way to implement this function is, since I'm working with Spring MVC and Hibernate, to add a function to my DAO interface:
public List<Equipement> getByCriteria(org.hibernate.Criteria criteria) ;
in order to accomplish her mission the findByCriterias method needs to transform the searchEquipement to a org.hibernate.Criteria, so i decided to create an util class to do this (not complete yet) :
public class ApplicationUtil {
private ClassPathXmlApplicationContext context = new
ClassPathXmlApplicationContext("mmapp/util/application-context-util.xml") ;
private SessionFactory sessionFactory ;
public void setSessionFactory(){
sessionFactory = (SessionFactory) context.getBean("sessionFactory") ;
}
public synchronized Criteria changeSearchEquipementToCriteria(SearchEquipementsearchEquipement) {
setSessionFactory() ;
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Equipement.class) ;
return criteria ;
}
}
the problem is when i tried to unit test this part, i have many troubles.
public class ApplicationUtilUnitTests {
#Test
public void testChangeSearchEquipementToCriteria(){
ApplicationUtil util = new ApplicationUtil() ;
Criteria criteria = util.changeSearchEquipementToCriteria(null) ;
assertNotNull(criteria) ;
}
}
that's after several other tries, now i get this error:
org.hibernate.HibernateException: No Session found for current thread

I believe your answer lies in this post. You're mixing Integration Testing with Unit Testing. By nature of your requirement what you can do is:
Use a mocking API such Mockito, PowerMock, EasyMock, or any one that suits you
Mock Hibernate's fundamental features such as SessionFactory, Session, and Criteria (e.g. this post)
Now, you can perform Unit Testing to verify your method is working properly

Try this...
package com.om39a.spring.training.bean;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
#RunWith(PowerMockRunner.class)
#PrepareForTest(ApplicationUtil.class)
public class ApplicationUtilTest {
private ApplicationUtil applicationUtil;
#Mock
SessionFactory mockSessionFactory;
#Mock
Session mockSession;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
applicationUtil = PowerMockito.spy(new ApplicationUtil());
}
#Test
public void testApplicationUtil() throws Exception {
Whitebox.setInternalState("sessionFactory", mockSession);
PowerMockito.doNothing().when(applicationUtil, "setSessionFactory");
Mockito.when(mockSessionFactory.getCurrentSession()).thenReturn(
mockSession);
applicationUtil.changeSearchEquipementToCriteria(null);
/* ...
* ...
* ...
* Assert statments goes here
* ...
* ...
* ...
*/
}
}

Related

How to assert that the controller has been created in Spring Boot?

According to the tutorial Testing the Web Layer, testing that the controller has been created can be done with the following code:
#Test
public void contexLoads() throws Exception {
assertThat(controller).isNotNull();
}
but I get the following error:
The method assertThat(T, Matcher<? super T>) in the type Assert is not applicable for the arguments (HomeController)"
even with the statement:
import static org.junit.Assert.assertThat;
The code of my class is the same than the one given in the example:
package com.my_org.my_app;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class SmokeTest {
#Autowired
private HomeController controller;
#Test
public void contexLoads() throws Exception {
assertThat(controller).isNotNull();
}
}
If I change the assert statement to:
#Test
public void contexLoads() throws Exception {
assertNotNull(controller);
}
it works as expected.
My controller class has some Autowired objects, but since they are managed by Spring Boot it should not be an issue. Any idea of what could be wrong with assertThat(controller).isNotNull();? Thanks in advance.
You used the wrong assertThat import. You should use the following:
import static org.assertj.core.api.Assertions.assertThat;
The correct method is located in AssertJ library, not in JUnit.

How to make Spring IoC container available through out project

I feel stupid to even ask for this but I spent days looking for the answer and I'm still with nothing.
I wanna include simple Spring IoC container in my project. All I want it to do is to allow me Injecting/Autowiring some reusable objects in other classes. What I've done so far looks like this:
-> Project structure here <-
Configuration code:
package com.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.util.Random;
#Configuration
#ComponentScan(basePackages = "com.example")
public class AppConfig {
#Bean
public Random rand() {
return new Random(42);
}
#Bean
public String string() {
return "Hello World!";
}
}
Main class code:
package com.example;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Random;
public class Main {
#Autowired
Random rand;
#Autowired
String string;
public static void main(String[] args) {
// workflow
Main main = new Main();
System.out.println(main.string);
}
}
AnotherClass code:
package com.example.deeperpackage;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Random;
public class AnotherClass {
#Autowired
Random rand;
#Autowired
String string;
public void methodToBeCalled() {
// TODO
System.out.println(string);
}
}
How can I make these #Autowired annotations work? Do I have to instantiate container in every single class in which I want to autowire components? I've seen in work a oracle app which used Spring and #Inject to distribute objects to numerous classes and there was no container logic in any class available for me. Just fields with #Inject annotation. How to achieve that?
Simply add the annotation #Component on the classes you want to inject :
#Component
public class AnotherClass {
...
}
But you cannot inject static attributes and when you do new Main(), no Spring context is being created. If you use Spring Boot, you should look at how to write a main with it.
https://spring.io/guides/gs/spring-boot/

Spring aop doesn't run when project starts

I'v implemented a spring-boot aop demo and it runs well, but when I want to use it to load some resource when the project starts, it doesn't work somehow
Aop:
package com.neo.mysql;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* Created by li_weia on 2017/7/6.
*/
#Aspect
#Component
public class DynamicDataSourceAspect {
#Before("#annotation(VendorSource)")
public void beforeSwitchDS(JoinPoint point){
//获得当前访问的class
Class<?> className = point.getTarget().getClass();
//获得访问的方法名
String methodName = point.getSignature().getName();
//得到方法的参数的类型
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DEFAULT_DS;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
// 判断是否存在#DS注解
if (method.isAnnotationPresent(VendorSource.class)) {
VendorSource annotation = method.getAnnotation(VendorSource.class);
// 取出注解中的数据源名
dataSource = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
// 切换数据源
DataSourceContextHolder.setDB(dataSource);
}
#After("#annotation(VendorSource)")
public void afterSwitchDS(JoinPoint point){
DataSourceContextHolder.clearDB();
}
}
The VendorSource annotation:
package com.neo.mysql;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by li_weia on 2017/7/6.
*/
#Target({ ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface VendorSource {
String value() default "vendor-master";
}
It runs well here, I can successfully change datasource by annotation:
package com.neo.web;
import com.neo.entity.SiteEntity;
import com.neo.mapper.ClassMappingDao;
import com.neo.mysql.VendorSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
public class UserController {
private final ClassMappingDao siteMapper;
#Autowired(required = false)
public UserController(ClassMappingDao siteMapper) {
this.siteMapper = siteMapper;
}
#RequestMapping("/getSites")
#VendorSource("vendor-read")
public List<SiteEntity> getUsers() {
return siteMapper.getAllSite();
}
}
but it doesn't work here, the aop method is not invoked at all:
package com.neo.component;
import com.neo.entity.SiteEntity;
import com.neo.mapper.ClassMappingDao;
import com.neo.mysql.VendorSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* Created by li_weia on 2017/7/7.
*/
#Component
public class TestComponent{
private final ClassMappingDao userMapper;
#Autowired(required = false)
public TestComponent(ClassMappingDao userMapper) {
this.userMapper = userMapper;
init();
}
#VendorSource("vendor-read")
public void init() {
List<SiteEntity> sites = userMapper.getAllSite();
for(SiteEntity site: sites){
System.out.println(site.getSite());
}
}
}
You need to fully qualify the annotation, like so:
#Before("execution(public * *(..)) && #annotation(com.neo.mysql.VendorSource)")
private void whatever() {}
Also, as mentioned in my comment above, you need to have spring-boot-starter-aop on classpath. Maybe you already do, but since you didn't say, it's worth mentioning.
Edit:
I didn't notice the real problem before, I wasn't paying attention.
Spring AOP only triggers if you make calls from another class. This is because Spring needs to be able to intercept the call and run the pointcut. Calling the method from constructor is not going to do anything.
You can do a hackish workaround. Create a #PostConstruct void postConstruct() {} method in your class (not constructor), autowire ApplicationContext, and then do MyClassWithInitMethod myClass = context.getBean(MyClassWithInitMethod.class) in the postConstruct method. Then call the method on myClass, and AOP will kick in.
Frankly, I didn't previously check what you are doing in your pointcut, and it's a terrible idea. When multiple threads run, they are going to overwrite the static context, and create a race-condition that you'll then create another question for. Don't do it! Use the factory pattern instead, and inject the DataSourceFactory in the classes that now have the annotation.

spring data JPA/Hibernate. Complex cross table SQL which doesn't map to a table entity

* Edited with possible solution - any comments ? *
spring 4.2.5 RELEASE
I'm starting to create Java web services onto a Legacy database.
Following the spring-data JPA repository pattern creating entities which map to tables, a repository extending CrudRepository is working well.
As described in this great tutorial
All the examples I've seen assume simple mapping of a table to an entity. Order -> OrderEntity, OrderLine, Customer etc.
How would you deal with read-only reporting type queries which do not fit into this pattern where the query result contains columns from many tables and use complex cross table joins.
I'm just struggling to get my head around how to deal with this scenario.
Possible Solution
I've managed to run native SQL using the NamedParameterJdbcTemplate and map the results onto a POJO using a BeanPropertyRowMapper
ApplicationContext class
The NamedParameterJdbcTemplate bean is defined (the rest of the beans HikariCP, JPA Session Factory, JPA Transaction Manager, DozerBean mapper have been left out for brevity)
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = {"com.savant.test.spring.donorservicejpa.dao.repository"},
repositoryBaseClass = com.savant.test.spring.donorservicejpa.dao.repository.BaseRepositoryImpl.class )
#ComponentScan(
{"com.savant.test.spring.donorservicejpa.dao.jdbc.repository",
"com.savant.test.spring.donorservicejpa.dao.query.objects"})
public class ApplicationContext {
#Bean
NamedParameterJdbcTemplate jdbcTemplate(DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
}
POJO for search results. No Spring annotations, just a simple class
package com.savant.test.spring.donorservicejpa.dao.query.objects;
public class SessionSearchResult {
private String sessno;
private String sesdate;
// etc
// setters/getters
}
'Repository'. It's not actually a repository in spring terms, just an interface/class implementation
package com.savant.test.spring.donorservicejpa.dao.jdbc.repository;
public interface SessionSearchRepository{
List<SessionSearchResult> findByCriteria(String searchCriteria);
}
Base implementation
package com.savant.test.spring.donorservicejpa.dao.jdbc.repository;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
public class BaseJdbcRepositoryImpl {
protected final NamedParameterJdbcTemplate jdbcTemplate;
BaseJdbcRepositoryImpl(NamedParameterJdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
Simple test implementation of the search.
package com.savant.test.spring.donorservicejpa.dao.jdbc.repository;
import com.savant.test.spring.donorservicejpa.dao.query.objects.SessionSearchResult;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
#Component
public class SessionSearchRepositoryImpl extends BaseJdbcRepositoryImpl implements SessionSearchRepository {
private static final String SESSION_SEARCH_SQL
= "SELECT sesdet.sessno, sessdays.sesdate "
+ "FROM sesdet, sessdays "
+ "WHERE sessdays.sessno = sesdet.sessno "
+ "AND sesdet.sessno = :sessno";
#Autowired
public SessionSearchRepositoryImpl(NamedParameterJdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
#Transactional(readOnly = true)
#Override
public List<SessionSearchResult> findByCriteria(String searchCriteria) {
Map<String, String> queryParams = new HashMap<>();
queryParams.put("sessno", searchCriteria);
List<SessionSearchResult> searchResults = jdbcTemplate.query(SESSION_SEARCH_SQL, queryParams,
new BeanPropertyRowMapper<>(SessionSearchResult.class));
return searchResults;
}
}
And a simple test just to run the SQL
#Autowired
SessionSearchRepository sessionSearchRepository;
#Test
public void a_testSessionSearch() throws Exception, Throwable {
List<SessionSearchResult> sl = sessionSearchRepository.findByCriteria("CA04AS");
for (SessionSearchResult sessionSearchEntity : sl) {
}
}

Spring beans are not injected in flyway java based migration

I'm trying to inject component of configuration properties in the flyway migration java code but it always null.
I'm using spring boot with Flyway.
#Component
#ConfigurationProperties(prefix = "code")
public class CodesProp {
private String codePath;
}
Then inside Flyway migration code, trying to autowrire this component as following:
public class V1_4__Migrate_codes_metadata implements SpringJdbcMigration {
#Autowired
private CodesProp codesProp ;
public void migrate(JdbcTemplate jdbcTemplate) throws Exception {
codesProp.getCodePath();
}
Here, codesProp is always null.
Is there any way to inject spring beans inside flyway or make it initialized before flyway bean?
Thank You.
Flyway doesn't support dependency injection into SpringJdbcMigration implementations. It simply looks for classes on the classpath that implement SpringJdbcMigration and creates a new instance using the default constructor. This is performed in SpringJdbcMigrationResolver. When the migration is executed, SpringJdbcMigrationExecutor creates a new JdbcTemplate and then calls your migration implementation's migrate method.
If you really need dependencies to be injected into your Java-based migrations, I think you'll have to implement your own MigrationResolver that retrieves beans of a particular type from the application context and creates and returns a ResolvedMigration instance for each.
If like me, you don't want to wait for Flyway 4.1, you can use Flyway 4.0 and add the following to your Spring Boot application:
1) Create a ApplicationContextAwareSpringJdbcMigrationResolver class in your project:
import org.flywaydb.core.api.FlywayException;
import org.flywaydb.core.api.MigrationType;
import org.flywaydb.core.api.MigrationVersion;
import org.flywaydb.core.api.configuration.FlywayConfiguration;
import org.flywaydb.core.api.migration.MigrationChecksumProvider;
import org.flywaydb.core.api.migration.MigrationInfoProvider;
import org.flywaydb.core.api.migration.spring.SpringJdbcMigration;
import org.flywaydb.core.api.resolver.ResolvedMigration;
import org.flywaydb.core.internal.resolver.MigrationInfoHelper;
import org.flywaydb.core.internal.resolver.ResolvedMigrationComparator;
import org.flywaydb.core.internal.resolver.ResolvedMigrationImpl;
import org.flywaydb.core.internal.resolver.spring.SpringJdbcMigrationExecutor;
import org.flywaydb.core.internal.resolver.spring.SpringJdbcMigrationResolver;
import org.flywaydb.core.internal.util.ClassUtils;
import org.flywaydb.core.internal.util.Location;
import org.flywaydb.core.internal.util.Pair;
import org.flywaydb.core.internal.util.StringUtils;
import org.flywaydb.core.internal.util.scanner.Scanner;
import org.springframework.context.ApplicationContext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
/**
* Migration resolver for {#link SpringJdbcMigration}s which are registered in the given {#link ApplicationContext}.
* This resolver provides the ability to use other beans registered in the {#link ApplicationContext} and reference
* them via Spring's dependency injection facility inside the {#link SpringJdbcMigration}s.
*/
public class ApplicationContextAwareSpringJdbcMigrationResolver extends SpringJdbcMigrationResolver {
private final ApplicationContext applicationContext;
public ApplicationContextAwareSpringJdbcMigrationResolver(Scanner scanner, Location location, FlywayConfiguration configuration, ApplicationContext applicationContext) {
super(scanner, location, configuration);
this.applicationContext = applicationContext;
}
#SuppressWarnings("unchecked")
#Override
public Collection<ResolvedMigration> resolveMigrations() {
// get all beans of type SpringJdbcMigration from the application context
Map<String, SpringJdbcMigration> springJdbcMigrationBeans =
(Map<String, SpringJdbcMigration>) this.applicationContext.getBeansOfType(SpringJdbcMigration.class);
ArrayList<ResolvedMigration> resolvedMigrations = new ArrayList<ResolvedMigration>();
// resolve the migration and populate it with the migration info
for (SpringJdbcMigration springJdbcMigrationBean : springJdbcMigrationBeans.values()) {
ResolvedMigrationImpl resolvedMigration = extractMigrationInfo(springJdbcMigrationBean);
resolvedMigration.setPhysicalLocation(ClassUtils.getLocationOnDisk(springJdbcMigrationBean.getClass()));
resolvedMigration.setExecutor(new SpringJdbcMigrationExecutor(springJdbcMigrationBean));
resolvedMigrations.add(resolvedMigration);
}
Collections.sort(resolvedMigrations, new ResolvedMigrationComparator());
return resolvedMigrations;
}
ResolvedMigrationImpl extractMigrationInfo(SpringJdbcMigration springJdbcMigration) {
Integer checksum = null;
if (springJdbcMigration instanceof MigrationChecksumProvider) {
MigrationChecksumProvider version = (MigrationChecksumProvider) springJdbcMigration;
checksum = version.getChecksum();
}
String description;
MigrationVersion version1;
if (springJdbcMigration instanceof MigrationInfoProvider) {
MigrationInfoProvider resolvedMigration = (MigrationInfoProvider) springJdbcMigration;
version1 = resolvedMigration.getVersion();
description = resolvedMigration.getDescription();
if (!StringUtils.hasText(description)) {
throw new FlywayException("Missing description for migration " + version1);
}
} else {
String resolvedMigration1 = ClassUtils.getShortName(springJdbcMigration.getClass());
if (!resolvedMigration1.startsWith("V") && !resolvedMigration1.startsWith("R")) {
throw new FlywayException("Invalid Jdbc migration class name: " + springJdbcMigration.getClass()
.getName() + " => ensure it starts with V or R," + " or implement org.flywaydb.core.api.migration.MigrationInfoProvider for non-default naming");
}
String prefix = resolvedMigration1.substring(0, 1);
Pair info = MigrationInfoHelper.extractVersionAndDescription(resolvedMigration1, prefix, "__", "");
version1 = (MigrationVersion) info.getLeft();
description = (String) info.getRight();
}
ResolvedMigrationImpl resolvedMigration2 = new ResolvedMigrationImpl();
resolvedMigration2.setVersion(version1);
resolvedMigration2.setDescription(description);
resolvedMigration2.setScript(springJdbcMigration.getClass().getName());
resolvedMigration2.setChecksum(checksum);
resolvedMigration2.setType(MigrationType.SPRING_JDBC);
return resolvedMigration2;
}
}
2) Add a new configuration class to post process the Spring Boot generated Flyway instance:
import org.flywaydb.core.Flyway;
import org.flywaydb.core.internal.dbsupport.DbSupport;
import org.flywaydb.core.internal.dbsupport.h2.H2DbSupport;
import org.flywaydb.core.internal.dbsupport.mysql.MySQLDbSupport;
import com.pegusapps.zebra.infrastructure.repository.flyway.ApplicationContextAwareSpringJdbcMigrationResolver;
import org.flywaydb.core.internal.resolver.sql.SqlMigrationResolver;
import org.flywaydb.core.internal.util.Location;
import org.flywaydb.core.internal.util.PlaceholderReplacer;
import org.flywaydb.core.internal.util.scanner.Scanner;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.sql.SQLException;
#Configuration
#ComponentScan("db.migration")
public class FlywayConfiguration {
#Bean
public BeanPostProcessor postProcessFlyway(ApplicationContext context) {
return new BeanPostProcessor() {
#Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
return o;
}
#Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
if (o instanceof Flyway) {
Flyway flyway = (Flyway) o;
flyway.setSkipDefaultResolvers(true);
ApplicationContextAwareSpringJdbcMigrationResolver resolver = new ApplicationContextAwareSpringJdbcMigrationResolver(
new Scanner(Thread.currentThread().getContextClassLoader()),
new Location("classpath:db/migration"),
context.getBean(org.flywaydb.core.api.configuration.FlywayConfiguration.class),
context);
SqlMigrationResolver sqlMigrationResolver = null;
try {
sqlMigrationResolver = new SqlMigrationResolver(
getDbSupport(),
new Scanner(Thread.currentThread().getContextClassLoader()),
new Location("classpath:db/migration"),
PlaceholderReplacer.NO_PLACEHOLDERS,
"UTF-8",
"V",
"R",
"__",
".sql");
} catch (SQLException e) {
e.printStackTrace();
}
flyway.setResolvers(sqlMigrationResolver, resolver);
}
return o;
}
private DbSupport getDbSupport() throws SQLException {
DataSource dataSource = context.getBean(DataSource.class);
if( ((org.apache.tomcat.jdbc.pool.DataSource)dataSource).getDriverClassName().equals("org.h2.Driver"))
{
return new H2DbSupport(dataSource.getConnection());
}
else
{
return new MySQLDbSupport(dataSource.getConnection());
}
}
};
}
}
Note that I have some hardcoded dependencies on tomcat jdbc pool, h2 and mysql. If you are using something else, you will need to change the code there (If there is anybody that knows how to avoid it, please comment!)
Also note that the #ComponentScan package needs to match with where you will put the Java migration classes.
Also note that I had to add the SqlMigrationResolver back in since I want to support both the SQL and the Java flavor of the migrations.
3) Create a Java class in the db.migrations package that does the actual migration:
#Component
public class V2__add_default_surveys implements SpringJdbcMigration {
private final SurveyRepository surveyRepository;
#Autowired
public V2__add_surveys(SurveyRepository surveyRepository) {
this.surveyRepository = surveyRepository;
}
#Override
public void migrate(JdbcTemplate jdbcTemplate) throws Exception {
surveyRepository.save(...);
}
}
Note that you need to make the class a #Component and it needs to implement the SpringJdbcMigration. In this class, you can use Spring constructor injection for any Spring bean from your context you might need to do the migration(s).
Note: Be sure to disable ddl validation of Hibernate, because the validation seems to run before Flyway runs:
spring.jpa.hibernate.ddl-auto=none
In short do not autowire beans in your db migrations or even reference classes from your application!
If you refactor/delete/change classes you referenced in the migration it may not even compile or worse corrupt your migrations.
The overhead of using plain JDBC template for the migrations is not worth the risk.
If you are using deltaspike you can use BeanProvider to get a reference to your Class. Here is a DAO example, but it should work fine with your class too.
Change your DAO code:
public static UserDao getInstance() {
return BeanProvider.getContextualReference(UserDao.class, false, new DaoLiteral());
}
Then in your migration method:
UserDao userdao = UserDao.getInstance();
And there you've got your reference.
(referenced from: Flyway Migration with java)

Resources