2016-01-21 10 views
10

Sto provando a iniettare il componente delle proprietà di configurazione nel codice java di migrazione flyway ma è sempre nullo.I bean Spring non vengono iniettati nella migrazione flyway basata su flyway

Sto usando lo stivale a molla con Flyway.

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

    private String codePath; 
} 

Poi all'interno Flyway codice di migrazione, cercando di autowrire questo componente come segue:

public class V1_4__Migrate_codes_metadata implements SpringJdbcMigration { 

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

Qui, codesProp è sempre nullo.

C'è un modo per iniettare i fagioli a molla nel flyway o renderlo inizializzato prima del beanway?

Grazie.

risposta

5

Flyway non supporta l'integrazione delle dipendenze nelle implementazioni SpringJdbcMigration. Cerca semplicemente le classi sul classpath che implementano SpringJdbcMigration e crea una nuova istanza utilizzando il costruttore predefinito. Questo viene eseguito in SpringJdbcMigrationResolver. Quando viene eseguita la migrazione, SpringJdbcMigrationExecutor crea un nuovo JdbcTemplate e quindi chiama il metodo migrate dell'implementazione di migrazione.

Se davvero si devono iniettare dipendenze nelle migrazioni basate su Java, penso che sarà necessario implementare il proprio MigrationResolver che recupera i bean di un tipo particolare dal contesto dell'applicazione e crea e restituisce un'istanza ResolvedMigration per ogni .

+0

Grazie, penso che sia legato dal problema https://github.com/flyway/flyway/issues/1062, ora, ho bisogno di importare alcuni codici memorizzati in file system in db. hai un'idea di leggere il percorso esterno come configurazione e passarlo a flyway. – Mango

3

Se, come me, non si vuole aspettare per Flyway 4.1, è possibile utilizzare Flyway 4.0 e aggiungere il seguente alla vostra applicazione Stivale Primavera:

1) Creare una classe ApplicationContextAwareSpringJdbcMigrationResolver nel progetto:

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) Aggiungere una nuova classe di configurazione per pubblicare processo primavera avvio generato esempio Flyway:

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

nota che ho un po 'hardcoded dipendono encies su tomcat jdbc pool, h2 e mysql. Se stai usando qualcos'altro, dovrai modificare il codice lì (Se c'è qualcuno che sa come evitarlo, per favore commenta!)

Inoltre, il pacchetto @ComponentScan deve corrispondere a dove verrà inserito Classi di migrazione Java.

Si noti inoltre che ho dovuto aggiungere il SqlMigrationResolver indietro in quanto voglio supportare sia l'SQL e l'aroma Java delle migrazioni.

3) Creare una classe Java nel pacchetto db.migrations che fa la migrazione effettiva:

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

Nota che è necessario rendere la classe un @Component ed ha la necessità di implementare il SpringJdbcMigration. In questa classe, è possibile utilizzare l'iniezione del costruttore Spring per qualsiasi bean Spring dal contesto in cui potrebbe essere necessario eseguire la/e migrazione/i.

Nota: assicurarsi di disattivare la convalida ddl di Hibernate, perché la convalida sembra correre prima Flyway corre:

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

Sembra che questa funzionalità è essere aggiunti a Flyway 5.0. Questa domanda è emersa come primo risultato quando ho cercato "spring beanway flyway" su Google, quindi altre persone potrebbero ancora avere accesso al problema.

Se sei bloccato su Flyway 4.0, ho trovato un'altra soluzione rapida pubblicata nella pagina spring-beans-flyway repository on my GitHub.

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

È possibile aggiungere l'annotazione @ComponentScan oltre a @SpringBootApplication.

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

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

Potrai inserire il codice all'interno del migrate(JdbcTemplate) metodo implementato, anche se non sarà effettivamente utilizzare il JdbcTemplate. Ciò consentirà a Flyway di eseguire la migrazione in ordine lasciando che la classe di supporto svolga il lavoro relativo a 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.

È possibile iniettare i fagioli necessari all'interno del costruttore. La migrazione V ### ora può utilizzare questi metodi statici per operare con i bean con filo Spring.

@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

Se si utilizza deltaspike, è possibile utilizzare BeanProvider per ottenere un riferimento alla classe. Ecco un esempio DAO, ma dovrebbe funzionare anche con la tua classe.

modificare il codice DAO:

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

Poi, nel tuo metodo di migrazione:

UserDao userdao = UserDao.getInstance(); 

E ci hai il tuo riferimento.

(riferimento da: Flyway Migration with java)

Problemi correlati