web-dev-qa-db-ja.com

PostgreSQLが主キー値を生成できるようにJPAを構成します

したがって、プロジェクトではPostgreSQLデータベースを使用し、データベースの操作にはJPAを使用します。 Netbeans 7.1.2の自動作成者を使用して、データベースからエンティティを作成しました。

小さな変更後、主キーの値は次のように説明されます。

_@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Basic(optional = false)
@NotNull
@Column(name = "idwebuser", nullable = false)
private Integer idwebuser;
_

問題は、Java app-生成された値が実際のデータベースよりも低いので、データベースを直接変更するとき(SQLまたは別のツールを使用) ID値-したがって、新しいエンティティの作成中にエラーが発生します。

JPAがデータベースにIDを自動的に生成させ、作成プロセス後にIDを取得させる可能性はありますか?または、より良い解決策は何でしょうか?ありがとう。

[〜#〜] edit [〜#〜]より具体的には、ユーザーのテーブルがあり、私の問題は、任意のタイプの戦略生成タイプを使用して、JPAが指定されたジェネレーターIDです。新しいエントリを追加して自分でテーブルを変更すると、アプリケーションのGeneratedValueが現在のIDよりも低くなるため、IDが重複するという例外が発生するため、これは私にとって間違っています。修正できます;)?

答えに関する短いメモPG Admin->最初の100行を表示し、選択を使用する代わりにそこに。とにかく、このエディターは何らかの方法でIDの更新プロセスをスキップするため、適切なINSERTを記述するとDBでも不適切なIDで実行されます。したがって、基本的にはデータベースとアプリケーションよりも、使用したエディターの問題でした...

今では@GeneratedValue(strategy=GenerationType.IDENTITY)を使用しても動作します

35
Atais

テーブル定義が与えられた場合:

CREATE TABLE webuser(
    idwebuser SERIAL PRIMARY KEY,
    ...
)

マッピングを使用します。

@Entity
@Table(name="webuser")
class Webuser {

    @Id
    @SequenceGenerator(name="webuser_idwebuser_seq",
                       sequenceName="webuser_idwebuser_seq",
                       allocationSize=1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
                    generator="webuser_idwebuser_seq")
    @Column(name = "idwebuser", updatable=false)
    private Integer id;

    // ....

}

tablename_columname_seqという命名は、SERIALのPostgreSQLのデフォルトのシーケンス命名であり、これに従うことをお勧めします。

allocationSize=1は、Hibernateがデータベースの他のクライアントと協力する必要がある場合に重要です。

トランザクションがロールバックする場合、このシーケンスには「ギャップ」があることに注意してください。トランザクションは、あらゆる種類の理由でロールバックできます。アプリケーションは、これに対処するように設計する必要があります。

  • ID nにはID n-1またはn+1があると想定しないでください
  • nより小さいIDの前またはnより大きいIDの後にnというIDが追加またはコミットされたと決して想定しないでください。シーケンスの使用方法に本当に注意している場合は、これを行うことができますが、決して試してはいけません。代わりに、テーブルにタイムスタンプを記録してください。
  • IDに追加したり、IDから削除したりしないでください。それらが平等かどうかを比較してください。

シーケンスについてはPostgreSQLのドキュメント および シリアルデータ型 を参照してください。

彼らは、上記のテーブル定義は基本的に次のショートカットであることを説明しています:

CREATE SEQUENCE idwebuser_id_seq;
CREATE TABLE webuser(
    idwebuser integer primary key default nextval('idwebuser_id_seq'),
    ...
)
ALTER SEQUENCE idwebuser_id_seq OWNED BY webuser.idwebuser;

...シーケンスを説明する@SequenceGeneratorアノテーションを追加した理由の説明に役立つはずです。


ギャップのないシーケンス(チェックや請求書番号など)が本当に必要な場合は、 ギャップレスシーケンス を参照してください。ただし、真剣にこの設計を避け、neverで使用してください。主キー用。


:テーブル定義が代わりにこのように見える場合:

CREATE TABLE webuser(
    idwebuser integer primary key,
    ...
)

そして、(unsafeを使用してそれに挿入している、使用しないでください):

INSERT INTO webuser(idwebuser, ...) VALUES ( 
    (SELECT max(idwebuser) FROM webuser)+1, ...
);

または(安全ではない、これを絶対に行わない):

INSERT INTO webuser(idwebuser, ...) VALUES ( 
    (SELECT count(idwebuser) FROM webuser), ...
);

その後、あなたはそれを間違っています、シーケンスに切り替える必要があります(上記のように)またはロックされたカウンターテーブルを使用してcorrectギャップレスシーケンス実装に(再び、上記を参照し、Googleの「ギャップレスシーケンスpostgresql」を参照してください)。データベースで複数の接続が動作している場合、上記の両方とも間違った動作をします。

70
Craig Ringer

次のようなシーケンスジェネレータを使用する必要があるようです。

@GeneratedValue(generator="YOUR_SEQ",strategy=GenerationType.SEQUENCE)
2
Denis Zevakhin

GenerationType.TABLEの代わりにGenerationType.IDENTITYを使用してみてください。データベースは、一意の主キーを生成するために使用される別のテーブルを作成し、最後に使用されたID番号も保存します。

1
Leszek

それは私のために働く

  1. このようなテーブルを作成するには、SERIALを使用します。
CREATE TABLE webuser(
    idwebuser SERIAL PRIMARY KEY,
    ...
)
  1. idフィールドに@GeneratedValue(strategy = GenerationType.IDENTITY)を追加します。
@Entity
@Table(name="webuser")
class Webuser {

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

    // ....

}
0
hang gao

また、一般的なGenerationType.IDENTITYを選択した回答によって提案されたソリューションに大量に変換するスクリプトを記述することで、労力を節約できます。以下のスクリプトは、Javaソースファイルのフォーマット方法に若干の依存関係があり、バックアップなしで変更を行います。

スクリプトを実行した後:

  1. import javax.persistence.Table;を検索してimport javax.persistence.Table; import javax.persistence.SequenceGenerator;に置き換えます。
  2. NetBeansのソースコードを次のように再フォーマットします。
    1. フォーマットするすべてのソースファイルを選択します。
    2. 押す Alt+Shift+F
    3. 再フォーマットを確認します。

次のスクリプトをupdate-sequences.shまたは同様の名前で保存します。

#!/bin/bash

# Change this to the directory name (package name) where the entities reside.
PACKAGE=com/domain/project/entities

# Change this to the path where the Java source files are located.
cd src/main/Java

for i in $(find $PACKAGE/*.Java -type f); do
  # Only process classes that have an IDENTITY sequence.
  if grep "GenerationType.IDENTITY" $i > /dev/null; then
    # Extract the table name line.
    LINE_TABLE_NAME=$(grep -m 1 @Table $i | awk '{print $4;}')
    # Trim the quotes (if present).
    TABLE_NAME=${LINE_TABLE_NAME//\"}
    # Trim the comma (if present).
    TABLE_NAME=${TABLE_NAME//,}

    # Extract the column name line.
    LINE_COLUMN_NAME=$(grep -m 1 -C1 -A3 @Id $i | tail -1)
    COLUMN_NAME=$(echo $LINE_COLUMN_NAME | awk '{print $4;}')
    COLUMN_NAME=${COLUMN_NAME//\"}
    COLUMN_NAME=${COLUMN_NAME//,}

    # PostgreSQL sequence name.
    SEQUENCE_NAME="${TABLE_NAME}_${COLUMN_NAME}_seq"

    LINE_SEQ_GENERATOR="@SequenceGenerator( name = \"$SEQUENCE_NAME\", sequenceName = \"$SEQUENCE_NAME\", allocationSize = 1 )"
    LINE_GENERATED_VAL="@GeneratedValue( strategy = GenerationType.SEQUENCE, generator = \"$SEQUENCE_NAME\" )"
    LINE_COLUMN="@Column( name = \"$COLUMN_NAME\", updatable = false )\n"

    # These will depend on source code formatting.
    DELIM_BEGIN="@GeneratedValue( strategy = GenerationType.IDENTITY )"
    # @Basic( optional = false ) is also replaced.
    DELIM_ENDED="@Column( name = \"$COLUMN_NAME\" )"

    # Replace these lines...
    #
    # $DELIM_BEGIN
    # $DELIM_ENDED
    #
    # With these lines...
    #
    # $LINE_SEQ_GENERATOR
    # $LINE_GENERATED_VAL
    # $LINE_COLUMN

    sed -i -n "/$DELIM_BEGIN/{:a;N;/$DELIM_ENDED/!ba;N;s/.*\n/$LINE_SEQ_GENERATOR\n$LINE_GENERATED_VAL\n$LINE_COLUMN/};p" $i
  else
    echo "Skipping $i ..."
  fi
done

NetBeansを使用してCRUDアプリケーションを生成する場合、ID属性には編集可能な入力フィールドは含まれません。

0
Dave Jarvis