2016-01-21 84 views
10

我試圖在遷移遷移java代碼中注入配置屬性的組件,但它始終爲空。春豆沒有注入基於遷移路線的Java遷移

我正在使用Flyway的彈簧引導。

@Component 
@ConfigurationProperties(prefix = "code") 
public class CodesProp { 

    private String codePath; 
} 

內。然後遷飛遷移代碼,試圖autowrire此組件如下:

public class V1_4__Migrate_codes_metadata implements SpringJdbcMigration { 

@Autowired 
private CodesProp codesProp ; 
public void migrate(JdbcTemplate jdbcTemplate) throws Exception { 
    codesProp.getCodePath(); 
} 

這裏,codesProp始終爲空。

有什麼辦法可以將spring bean注入到flyway中,或者在flyway bean之前初始化它嗎?

謝謝。

回答

5

Flyway不支持依賴注入SpringJdbcMigration實現。它只是在類路徑中查找實現SpringJdbcMigration的類,並使用默認構造函數創建一個新實例。這在SpringJdbcMigrationResolver中執行。執行遷移時,SpringJdbcMigrationExecutor會創建一個新的JdbcTemplate,然後調用您的遷移實施的migrate方法。

如果你真的需要依賴關係注入到你的基於Java的遷移,我認爲你必須實現自己的MigrationResolver從應用程序上下文檢索特定類型的豆類和創建並返回一個ResolvedMigration實例爲每。

+0

謝謝,我認爲這是問題https://github.com/flyway/flyway/issues/1062相關的,現在,我需要導入存儲在文件系統中的一些代碼進入分貝。你有沒有想過將外部路徑作爲配置讀取並傳遞給flyway的問題。 – Mango

3

如果像我一樣,你不希望等待遷飛4.1,你可以使用遷飛4.0及以下內容添加到您的春季啓動應用程序:

1)在項目中創建一個ApplicationContextAwareSpringJdbcMigrationResolver類:

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)添加一個新的配置類發佈過程中的春天引導產生的遷飛例如:

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()); 
       } 
      } 
     }; 
    } 
} 

注意,我有些硬編碼的依賴在tomcat jdbc池,h2和mysql上進行加密。如果你正在使用其他的東西,你需要在那裏更改代碼(如果有人知道如何避免它,請評論!)

另外請注意,@ComponentScan包需要匹配您將放置的位置Java遷移類。

另請注意,我必須重新添加SqlMigrationResolver,因爲我想支持遷移的SQL和Java風格。

3)db.migrations包,做實際的遷移創建一個Java類:

@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(...); 
    } 
} 

請注意,你需要做的類@Component,它需要實現SpringJdbcMigration。在這個類中,你可以從你的上下文中爲任何Spring bean使用Spring構造器注入來完成遷移。

注:一定要禁用休眠的DDL驗證,因爲驗證似乎遷飛運行之前運行:

spring.jpa.hibernate.ddl-auto=none 
13

它看起來像這樣功能中Flyway 5.0添加。當我在Google上搜索「spring beans flyway」時,第一個結果就出現了這個問題,所以其他人可能仍然會遇到這個問題。

如果您遇到Flyway 4.0,我想出了另一個快速解決方案,發佈在spring-beans-flyway repository on my GitHub頁面。

1)Tell Spring to do a component scan of your db.migration directory

除了@SpringBootApplication之外,您還可以添加@ComponentScan註釋。

@SpringBootApplication 
@ComponentScan({ "com.avehlies.springbeansflyway", "db.migration" }) 
public class SpringBeansFlywayApplication 

2)Create a SpringJdbcMigration in your db.migrations directory.

你把你的代碼中實現migrate(JdbcTemplate)方法裏面,雖然你不會實際使用的JdbcTemplate。這讓Flyway能夠按順序運行遷移,同時讓你的助手類完成與Spring相關的工作。

public class V2__Add_Person implements SpringJdbcMigration { 
    public void migrate(JdbcTemplate jdbcTemplate) { 
     Person person = new Person(4, 'Cosmo Kramer'); 
     AddPerson.addPerson(person); 
    } 
} 

3)Make a class annotated as a Component and create static methods in it.

你可以注入你需要的構造函數裏面的豆子。 V ###遷移現在可以使用這些靜態方法來使用Spring-wired bean進行操作。

@Component 
public class AddPerson { 
    private static PersonRepository personRepository; 

    public AddPerson(PersonRepository personRepository) { 
     this.personRepository = personRepository; 
    } 

    public static int addPerson(Person person) { 
     return personRepository.save(person); 
    } 
} 
0

如果您使用的是deltaspike,則可以使用BeanProvider獲取對您的類的引用。這是一個DAO示例,但它也應該適合您的課程。

更改您的DAO代碼:

public static UserDao getInstance() { 
    return BeanProvider.getContextualReference(UserDao.class, false, new DaoLiteral()); 
} 

然後在您的遷移方法:

UserDao userdao = UserDao.getInstance(); 

有你有你的參考。

(從引用:Flyway Migration with java