web-dev-qa-db-ja.com

LiquiBaseとSpringを使用してデータベースに(FKを使用して)多くの値を挿入する

Liquibaseを使用して(現在Excelファイルにある)多くのレコードをDBに追加しようとしています(将来のDB変更のためにそれを行う方法を知っているため)

私のアイデアは、Javaを使用してExcelファイルを読み取り、Spring初期化クラスから次のようにChangeLogParametersに入力することでした。

SpringLiquibase liqui = new SpringLiquibase();
liqui.setBeanName("liquibaseBean");
liqui.setDataSource(dataSource());
liqui.setChangeLog("classpath:changelog.xml");

HashMap<String, String> values = new HashMap<String, String>();
values.put("line1col1", ExcelValue1);
values.put("line1col2", ExcelValue2);
values.put("line1col3", ExcelValue3);
values.put("line2col1", ExcelValue4);
values.put("line2col2", ExcelValue5);
values.put("line2col3", ExcelValue6);
...
liqui.setChangeLogParameters(values);

このアプローチの問題は、私のchangelog.xmlが非常に奇妙な(そして非生産的である)ことです。

<changeSet author="gcardoso" id="2012082707">
    <insert tableName="t_user">
        <column name="login" value="${ExcelValue1}"/>
        <column name="name" value="${ExcelValue2}}"/>
        <column name="password" value="${ExcelValue3}"/>
    </insert>
    <insert tableName="t_user">
        <column name="login" value="${ExcelValue4}"/>
        <column name="name" value="${ExcelValue5}}"/>
        <column name="password" value="${ExcelValue6}"/>
    </insert>
    ...
</changeSet>

私がこのようなことをすることができる方法はありますか?

HashMap<String, ArrayList<String>> values = new HashMap<String, ArrayList<String>>();
values.put("col1", Column1);
values.put("col2", Column2);
values.put("col3", Column3);
liqui.setChangeLogParameters(values);

<changeSet author="gcardoso" id="2012082707">
    <insert tableName="t_user">
        <column name="login" value="${Column1}"/>
        <column name="name" value="${Column2}}"/>
        <column name="password" value="${Column3}"/>
    </insert>
</changeSet>

それとも他の方法はありますか?

編集:私の現在のオプションは、ExcelをCSVファイルに変換し、を使用してデータをインポートすることです。

<changeSet author="gcardoso" id="InitialImport2" runOnChange="true">

    <loadData tableName="T_ENTITY" file="com/exictos/dbUpdate/entity.csv">
        <column header="SHORTNAME" name="SHORTNAME" />
        <column header="DESCRIPTION" name="DESCRIPTION" />
    </loadData>


    <loadData tableName="T_CLIENT" file="com/exictos/dbUpdate/client.csv">
        <column header="fdbhdf" name="ENTITYID" defaultValueComputed="(SELECT ID FROM T_ENTITY WHERE SHORTNAME = ENTITY_REFERENCE"/>
        <column header="DESCRIPTION" name="DESCRIPTION" />
    </loadData>


</changeSet>

これらのCSVファイルで:

entity.csv

SHORTNAME,DESCRIPTION
nome1,descricao1
nome2,descricao2

client.csv

DESCRIPTION,ENTITY_REFERENCE
descricaoCliente1,nome1
descricaoCliente2,nome2

しかし、私はこのエラーを受け取ります:

liquibase.exception.DatabaseException: Error executing SQL INSERT INTO `T_CLIENT` (`DESCRIPTION`, `ENTITY_REFERENCE`) VALUES ('descricaoCliente1', 'nome1'): Unknown column 'ENTITY_REFERENCE' in 'field list'

Client.csvのヘッダーをDESCRIPTION、ENTITYIDに変更すると、次のエラーが発生します。

liquibase.exception.DatabaseException: Error executing SQL INSERT INTO `T_CLIENT` (`DESCRIPTION`, `ENTITYID`) VALUES ('descricaoCliente1', 'nome1'): Incorrect integer value: 'nome1' for column 'entityid' at row 1

これらのいずれの場合でも、defaultValueComputedは次の例のvalueComputedと同じようには機能しないようです。

<changeSet author="gcardoso" id="InitialImport1">

    <insert tableName="T_ENTITY">
        <column name="SHORTNAME">nome1</column>
        <column name="DESCRIPTION">descricao1</column>
    </insert>

    <insert tableName="T_CLIENT">
        <column name="ENTITYID" valueComputed="(SELECT ID FROM T_ENTITY WHERE SHORTNAME = 'nome1')"/>
        <column name="DESCRIPTION">descricaoCliente</column>
    </insert>

</changeSet>

これは予想される動作ですか? LiquiBaseのバグ?それとも私が何か間違ったことをしているだけですか(最も可能性が高いです)?

または、大量のデータをインポートする他の方法はありますか?ただし、常にLiquiBaseやSpringを使用します。

EDIT2:私の問題は、正しい外部キーを使用して2番目のテーブルにデータを挿入できないことです。

19

Liquibaseはあなたが達成したいことのための理想的なツールではないと思います。 Liquibaseは、データベースのデータではなく、データベース構造の管理に適しています。

それでもLiquibaseを使用してデータを管理したい場合は、いくつかのオプションがあります( ここ を参照)-

  1. 挿入ステートメントをSQLとして記録し、次のようにchangelog.xmlから参照します。

    _<sqlFile path="/path/to/file.sql"/>_

  2. 次のようにchangelog.xmlから参照する カスタムリファクタリングクラス を使用します。

    _<customChange class="com.example.YourJavaClass" csvFile="/path/to/file.csv"/>_

    YourJavaClassは、CSVファイルからレコードを読み取り、データベースに適用して、次のメソッドを実装します。

    void execute(Database database) throws CustomChangeException;

Liquibaseを介してこのデータをロードした後は、ファイル内のデータを変更しないでください。これらの変更は再適用されないためです。変更を加える場合は、後続のチェンジセットで行う必要があります。そのため、しばらくすると、多くの異なるCSVファイル/ liquibaseチェンジセットが作成され、すべて同じ/類似のデータで動作する可能性があります(これは、このデータの使用方法によって異なります。挿入すると変更されますか?)。

参照データの管理には DBUnit の使用を検討することをお勧めします。これは主に単体テストで使用されるツールですが、非常に成熟しており、本番環境での使用に適しています。情報はCSVまたはXMLで保存できます。 Springの「InitializingBean」を使用してクラスパスからデータセットをロードし、DBUnitの「refresh」操作を実行することをお勧めします。これは docs から実行されます。

この操作は、文字通りデータセットの内容をデータベースに更新します。これは、既存の行のデータが更新され、存在しない行が挿入されることを意味します。データベースには存在するがデータセットには存在しない行は影響を受けません。

このようにして、参照データを1つの場所に保持し、時間の経過とともに追加して、情報のソースが1つだけになり、複数のLiquibaseチェンジセットに分割されないようにすることができます。 DBUnitデータセットをバージョン管理で維持すると、トレース機能が提供されます。また、ボーナスとして、DBUnitデータセットはデータベース間で移植可能であり、挿入順序などを管理して外部キー違反を防ぐことができます。

11
Barry Pitman

ターゲットデータベースによって異なります。 Sybaseまたは[〜#〜] mssql [〜#〜]サーバーを使用している場合は、 インストールされているclient + driverに付属するBCPツール 。これは、これらのデータベースに大量のデータを出し入れするための最速の方法です。

グーグルでこれらのリンクも見つけました...

Oracleには SQL * LOADER ツールがあります

MySQLには LOAD DATA INFILE コマンドがあります

私は、各データベースベンダーが、データの一括読み込みのための何らかの説明のツールを提供することを期待しています。

2
Brad