33

次のような主キー/ ID フィールドを持つエンティティがあります。

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

これはうまくいきます。私はEclipseLinkを使用してDDLスキーマを作成していますが、列は次のように正しく作成されています:

`id` bigint(20) NOT NULL AUTO_INCREMENT

ただし、PK を自分で指定したいエンティティがいくつかあります (これは、古いデータベースから構築中の新しいデータベースにデータを転送する小さなアプリケーションです)。POJO の ID を ( を使用してsetId(Long id)) 指定して永続化すると、EclipseLinkはそれを保存しません(つまり、レコードは保存されますが、id は eclipseLink によって自動生成されます)。

@GeneratedValue を持つ列の値を手動で指定する方法はありますか?

この問題に関するいくつかの考えを次に示します。

@GeneratedValue をまったく使用せずに問題を回避しようとしましたが、列を手動で AUTO_INCREMENTed に定義するだけです。ただし、EclipseLink は主キーを検証するため (そのため、null、ゼロ、または負の数ではない可能性があります) 、 ID を手動で提供する必要があります。例外メッセージには、eclipselink.id_validation を指定する必要があることが示されていますが、これは何の違いもないようです (注釈を付け@PrimaryKey(validation = IdValidation.NONE)ましたが、同じメッセージが表示されます)。

明確にするために:私はEclipseLink(2.4.0)を永続化プロバイダーとして使用していますが、それから切り替えることはできません(プロジェクトの大部分はEclipseLink固有のクエリヒント、注釈、およびクラスに依存しています).


編集(回答に応じて):

カスタム シーケンス:独自のシーケンスを実装しようとしました。DefaultSequence のサブクラス化を試みましたが、EclipseLink が教えてくれますInternal Exception: org.eclipse.persistence.platform.database.MySQLPlatform could not be found。しかし、私はチェックしました:クラスはクラスパスにあります。

そこで、別のクラス NativeSequence をサブクラス化しました。

public class MyNativeSequence extends NativeSequence {

    public MyNativeSequence() {
        super();
    }

    public MyNativeSequence(final String name) {
        super(name);
    }


    @Override
    public boolean shouldAlwaysOverrideExistingValue() {
        return false;
    }

    @Override
    public boolean shouldAlwaysOverrideExistingValue(final String seqName) {
        return false;
    }

}

しかし、私が得るものは次のとおりです。

javax.persistence.RollbackException: Exception [EclipseLink-7197] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Null or zero primary key encountered in unit of work clone [de.dfv.datenbank.domain.Mitarbeiter[ id=null ]], primary key [null]. Set descriptors IdValidation or the "eclipselink.id-validation" property.
    at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:102)
    ...
Caused by: Exception [EclipseLink-7197] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Null or zero primary key encountered in unit of work clone [de.dfv.datenbank.domain.Mitarbeiter[ id=null ]], primary key [null]. Set descriptors IdValidation or the "eclipselink.id-validation" property.
    at org.eclipse.persistence.exceptions.ValidationException.nullPrimaryKeyInUnitOfWorkClone(ValidationException.java:1451)
    ...

(わかりやすくするために、スタック トレースは短縮されています)。これは私が以前に受け取ったのと同じメッセージです。NativeSequence をサブクラス化すべきではありませんか? もしそうなら、Sequence や StandardSequence の抽象メソッドに何を実装すればよいかわかりません。

また、(メソッドをオーバーライドせずに) サブクラス化するだけで、クラスが期待どおりに機能することにも注意してください。ただし、 false を返すとshouldAlwaysOverrideExistingValue(...)、単一の値はまったく生成されません (プログラムをステップ実行したところ、getGeneratedValue()一度も呼び出されませんでした)。

また、トランザクション内に特定の種類のエンティティを 8 つ挿入すると、データベースに 11 レコードが作成されました (一体何!?)。

編集 (2012-09-01):問題の解決策がまだありません。独自のシーケンスを実装しても解決しませんでした。私が必要としているのは、ID を明示的に設定しない (自動生成される) 方法と、ID を明示的に設定できる方法 (データベースでのレコードの作成に使用される方法) です。

列を自分でauto_incrementとして定義し、@GeneratedValueを省略しようとしましたが、検証が開始され、そのようなエンティティを保存できません。値 != 0 および != ゼロを指定すると、mysql は主キーが重複していると警告します。

試すアイデアやオプションが不足しています。どれでも?(賞金を開始)

4

7 に答える 7

11

これは eclipselink で動作します。シーケンス用に別のテーブルが作成されますが、問題になることはありません。

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="id", insertable=true, updatable=true, unique=true, nullable=false)
private Long id;

GenerationType.AUTO は、理想的な生成戦略を選択します。フィールドは挿入および更新可能として指定されているため、TABLE 生成戦略が使用されます。これは、eclipselink が現在のシーケンス値を保持する別のテーブルを生成し、データベースに委任する代わりにシーケンス自体を生成することを意味します。列は挿入可能と宣言されているため、永続化時に id が null の場合、eclipselink は id を生成します。それ以外の場合は、既存の ID が使用されます。

于 2012-09-07T13:25:27.303 に答える
7

TABLEシーケンスを使用する場合、EclipseLinkを使用すると、値をオーバーライドできます(または、データベースがこれをサポートしている場合はSEQUENCE、MySQLはサポートしていません)。

IDENTITYの場合、MySQLで独自のIDを提供できるかどうかさえわかりません。これを確認することをお勧めします。一般に、IDENTITYは事前割り当てをサポートしていないため、IDENTITYの使用はお勧めしません。

IDENTITYがIDを提供できるようにするかどうかには、いくつかの問題があります。1つは、ID値に応じて2つの異なる挿入SQLを生成する必要があることです。これは、IDENTITYの場合、IDを挿入に含めることはできません。IDENTITYがユーザー提供のIDをサポートするように、バグをログに記録することをお勧めします。

それでも、独自のSequenceサブクラス、または場合によってはMySQLPlatformサブクラスで動作させることができるはずです。「eclipselink.target-database」永続性ユニットプロパティを使用して、MySQLPlatformサブクラスを設定します。

于 2012-09-04T14:04:13.250 に答える
6

問題に対するデータベース中心のソリューション:

  1. テーブルに補助的な null 許容列を作成します。手動で割り当てた ID を保持します。

    CREATE TABLE `test_table`
    (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `manual_id` bigint(20) NULL,
      `some_other_field` varchar(200) NOT NULL,
      PRIMARY KEY(id)
    );
    
  2. この列をエンティティの通常のフィールドにマップします。

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name="manual_id")
    private Integer manualId;
    
  3. テーブル ID が null でない場合に手動で割り当てられた ID に設定するトリガーを作成します。

    DELIMITER //
    CREATE TRIGGER `test_table_bi` BEFORE INSERT ON `test_table`
      FOR EACH ROW 
      BEGIN
        IF NEW.`manual_id` IS NOT NULL THEN 
          SET NEW.`id` = NEW.`manual_id`;
        END IF;
    END;//
    DELIMITER;    
    
  4. manualIdカスタム ID を割り当てる必要がある場合は、常に を使用してください。トリガーはあなたのために魔法を行います:

    testEntiy.setManualId(300);
    entityManager.persist(testEntity);
    
  5. データベースのインポート フェーズの後、トリガー、補助列、およびそのマッピングを単純に削除します。

    DROP TRIGGER `test_table_bi`;
    ALTER TABLE `test_table` DROP COLUMN `manual_id`;
    

警告

現在の値より大きい ID を手動で指定するAUTO_INCREMENTと、次に生成さidれる値は、手動で割り当てられた ID に 1 を加えた値にジャンプします。例:

INSERT INTO `test_table` (manual_id, some_other_field) VALUES (50, 'Something');
INSERT INTO `test_table` (manual_id, some_other_field) VALUES (NULL, 'Something else');
INSERT INTO `test_table` (manual_id, some_other_field) VALUES (90, 'Something 2');
INSERT INTO `test_table` (manual_id, some_other_field) VALUES (NULL, 'Something else 2');
INSERT INTO `test_table` (manual_id, some_other_field) VALUES (40, 'Something 3');
INSERT INTO `test_table` (manual_id, some_other_field) VALUES (NULL, 'Something else 3');

結果を使用します:

+----+-----------+------------------+
| id | manual_id | some_other_field |
+----+-----------+------------------+
| 50 |        50 | Something        |
| 51 |      NULL | Something else   |
| 90 |        90 | Something 2      |
| 91 |      NULL | Something else 2 |
| 40 |        40 | Something 3      |
| 92 |      NULL | Something else 3 |
+----+-----------+------------------+

問題を回避するためAUTO_INCREMENTに、以前のデータベースの既存のすべての ID よりも大きい番号で始まる列を設定することを強くお勧めします。たとえば、次のようになります。

ALTER TABLE `test_table` AUTO_INCREMENT = 100000;
于 2012-09-06T20:01:25.190 に答える
4

明らかな何かが欠けているかもしれませんが、同じ@Table(name=".....")アノテーションと同じフィールドを持つ別のエンティティを定義するだけでなく、id を生成しないようにするのはなぜですか? 次に、そのエンティティを古い DB から新しい DB にデータをコピーするコードに使用できます。生成された Id を持つものは、ID 生成を必要とする通常の作成に使用できます。

EclipseLink で動作するかどうかはわかりませんが、ここでは Hibernate を使用しており、問題ないようです。

于 2012-09-06T03:05:45.720 に答える
2

GenerationType.SEQUENCEを PostgreSQL と EclipseLink で使用するとうまくいきました。

1) 変更

@GeneratedValue(strategy = GenerationType.IDENTITY) 

@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="step_id_seq")
@SequenceGenerator(name="step_id_seq", sequenceName="step_id_seq")

これで、NativeQuery を使用してシーケンスを呼び出すことができます。

return ((Vector<Integer>) em.createNativeQuery("select nextval('step_id_seq')::int").getSingleResult()).get(0);

EntityManager.persist() メソッドを呼び出す前に、返された Id をエンティティに設定します。

手遅れではないことを願っています!

于 2013-06-13T15:06:40.920 に答える
0

カスタム ID ジェネレーターを探す

http://blog.anorakgirl.co.uk/2009/01/custom-hibernate-sequence-generator-for-id-field/

たぶんこれが役立つかもしれません。

于 2012-08-17T08:36:49.190 に答える