web-dev-qa-db-ja.com

SpringBeanはフライウェイに注入されませんJavaベースの移行

フライウェイマイグレーションJavaコードに構成プロパティのコンポーネントを挿入しようとしていますが、常にnullです。

Flywayでスプリングブーツを使用しています。

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

    private String codePath;
 }

次に、Flyway移行コード内で、このコンポーネントを次のように自動書き込みしようとします。

public class V1_4__Migrate_codes_metadata implements SpringJdbcMigration {

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

ここで、codesPropは常にnullです。

フライウェイ内にスプリングビーンを注入したり、フライウェイビーンの前に初期化する方法はありますか?

ありがとうございました。

14
Lana

Flywayは、SpringJdbcMigration実装への依存性注入をサポートしていません。 SpringJdbcMigrationを実装するクラスパス上のクラスを検索し、デフォルトのコンストラクターを使用して新しいインスタンスを作成するだけです。これは SpringJdbcMigrationResolver で実行されます。移行が実行されると、 SpringJdbcMigrationExecutor は新しいJdbcTemplateを作成し、移行実装のmigrateメソッドを呼び出します。

Javaベースの移行に依存関係を注入する必要がある場合は、アプリケーションコンテキストから特定のタイプのBeanを取得し、それぞれにMigrationResolverインスタンスを作成して返す、独自のResolvedMigrationを実装する必要があると思います。

5
Andy Wilkinson

私のように、Flyway 4.1を待ちたくない場合は、Flyway 4.0を使用して、SpringBootアプリケーションに以下を追加できます。

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)新しい構成クラスを追加してSpringBootで生成された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());
                }
            }
        };
    }
}

Tomcat jdbcプール、h2、およびmysqlにハードコードされた依存関係があることに注意してください。他のものを使用している場合は、そこでコードを変更する必要があります(それを回避する方法を知っている人がいる場合は、コメントしてください!)

また、@ComponentScanパッケージは、Java移行クラスを配置する場所と一致する必要があることに注意してください。

また、SQLと移行の​​Javaフレーバー)の両方をサポートしたいので、SqlMigrationResolverを追加し直さなければならなかったことにも注意してください。

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を実装する必要があることに注意してください。このクラスでは、移行を行う必要がある可能性のあるコンテキストから、任意のSpringBeanにSpringコンストラクターインジェクションを使用できます。

注:検証はFlywayが実行される前に実行されるように見えるため、Hibernateのddl検証を必ず無効にしてください。

spring.jpa.hibernate.ddl-auto=none
4
Wim Deblauwe

Deltaspikeを使用している場合は、BeanProviderを使用してクラスへの参照を取得できます。これはDAOの例ですが、クラスでも問題なく機能するはずです。

DAOコードを変更します。

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

次に、移行方法で:

UserDao userdao = UserDao.getInstance();

そして、そこにあなたの参照があります。

(参照元: Javaを使用したFlyway Migration

0

つまり、データベース移行でBeanを自動配線したり、アプリケーションからクラスを参照したりしないでください。移行で参照したクラスをリファクタリング/削除/変更すると、移行がコンパイルされないか、さらに悪いことに破損する可能性があります。

移行にプレーンJDBCテンプレートを使用するオーバーヘッドは、リスクに見合う価値がありません。

0
Naso