2016-01-21 1 views
10

Я пытаюсь внедрить компонент свойств конфигурации в java-код миграции пролета, но он всегда равен нулю.Весенние бобы не впрыскиваются при миграции на основе перехода на лету.

Я использую весенний ботинок с пролетом.

@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 всегда нуль.

Есть ли способ впрыскивать весенние бобы внутри пролетного пути или сделать его инициализированным перед бризом?

Thank you.

ответ

5

Flyway не поддерживает инъекцию зависимостей в SpringJdbcMigration реализации. Он просто ищет классы на пути к классам, которые реализуют SpringJdbcMigration и создают новый экземпляр, используя конструктор по умолчанию. Это выполняется в SpringJdbcMigrationResolver. Когда выполняется миграция, SpringJdbcMigrationExecutor создает новый JdbcTemplate, а затем вызывает метод вашей реализации миграции migrate.

Если вам действительно нужны зависимости, которые необходимо ввести в ваши миграции на основе Java, я думаю, вам придется реализовать свой собственный MigrationResolver, который извлекает из определенного контекста компоненты определенного типа и создает и возвращает экземпляр ResolvedMigration для каждого ,

+0

Спасибо, я думаю, что это связано с вопросом по https://github.com/flyway/flyway/issues/1062, теперь мне нужно импортировать некоторые коды, хранящиеся в файловых системах в дБ. у вас есть идея прочитать внешний путь как конфигурацию и передать его на пролет. – Mango

3

Если как я, вы не хотите ждать Flyway 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) Создание класса Java в db.migrations пакете, который делает процесс миграции:

@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 для любого компонента Spring из вашего контекста, вам может потребоваться выполнить миграцию.

Примечание: Не забудьте отключить DDL проверку Hibernate, так как проверка кажется скороходов пролетный путь проходит:

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

Похоже, эту функциональность добавляется, чтобы Flyway 5.0. Этот вопрос явился первым результатом, когда я искал «пролет« фасоль »в Google, так что другие люди все еще могут столкнуться с проблемой.

Если вы застряли на Flyway 4.0, я придумал еще одно быстрое решение, размещенное на странице spring-beans-flyway repository on my GitHub.

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

Вы можете добавить аннотацию @ComponentScan в дополнение к @SpringBootApplication.

@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 beans.

@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)