私は私のシステムの回収可能な方法でのPostgreSQL(9.6)テーブルの名前を変更したい(JPA / Hibernateのを使用してJavaアプリ)
私のJavaコードで、JPAエンティティは、次の注釈を持っているでしょう@Entity
@Table(name="old_name")
し、データベースと呼ばれる同等のテーブルを持っているでしょうold_name
。
私は、に、テーブルの名前を変更したいと思いnew_name
、私は漸進的障害やロールバックを可能にし、データベースとJavaアプリを更新できるような方法で。
典型的な手順は次のようになります
- コピーを作成
old_name
中にnew_name
- 読み確保/(つまり、データは両方の方法を複製されている)の両方で利用可能に書き込みます
- 新しいテーブルを使用するには、Javaアプリを更新
new_name
- 自信を持って、システムのアップデート完了し、削除
old_name
効果的に、私は同じデータと同じスキーマ内の重複するテーブルをしたいと思い、受け入れることの両方が可能でJPAエンティティから読み取ることができる、読み取りと書き込み。
私は、トリガーの使用を認識していて、それを避けるためにしたいと思います。私は認識していないよ、それはトリガーを使用するよりも、これは苦痛が少ないになるだろう発見していない技術がある期待しています。
私はそれがビューの名前を持つテーブルを見つけることができないとして、しかし、JPAエンティティは文句を言い、テーブルの名前を変更し、その上に「シンプルビュー」を作成しようとしました。(それはビューではなく、テーブルで:)なしビュー@ /これを処理します。表JPA注釈@あるようですので)
私はまだここに記載されている施設試していません:http://wiki.postgresql.org/wiki/Replication,_Clustering,_and_Connection_Pooling大半はプール、シャーディングについてのように見えるように、と私は、簡単な短期のテーブルのレプリカが必要ですが、私はまた、これらを調査します。
おかげで - 私はPostgresの/ JPAに組み込まれ、何かを好む、当然の最も簡単なオプションをしたいと思いますが、真剣にも、サードパーティ製のオプションを検討します。
私はにこの答えをオンにすることを決めたので、これは、非常に興味深い質問した本格的な記事。
データベーステーブル
あなたは次の2つの表を持っていると仮定すると:
CREATE TABLE old_post (
id int8 NOT NULL,
title varchar(255),
version int4 NOT NULL,
PRIMARY KEY (id)
)
CREATE TABLE post (
id int8 NOT NULL,
created_on date,
title varchar(255),
version int4 NOT NULL,
PRIMARY KEY (id)
)
JPAエンティティ
old_post
テーブルには、新しいで複製する必要がありますpost
。注意事項post
表が古いテーブルよりも、今、多くの列を持っています。
私たちは、マップする必要がPost
実体を:
@Entity(name = "Post")
@Table(name = "post")
public static class Post {
@Id
private Long id;
private String title;
@Column(name = "created_on")
private LocalDate createdOn = LocalDate.now();
@Version
private int version;
//Getters and setters omitted for brevity
}
Hibernateのイベントリスナ
今、私たちはのためにINSERT、UPDATE、およびDELETE操作を傍受する3人のイベントリスナーを登録する必要がPost
実体。
我々は、次のイベントリスナーを経由してこれを行うことができます。
public class ReplicationInsertEventListener
implements PostInsertEventListener {
public static final ReplicationInsertEventListener INSTANCE =
new ReplicationInsertEventListener();
@Override
public void onPostInsert(
PostInsertEvent event)
throws HibernateException {
final Object entity = event.getEntity();
if(entity instanceof Post) {
Post post = (Post) entity;
event.getSession().createNativeQuery(
"INSERT INTO old_post (id, title, version) " +
"VALUES (:id, :title, :version)")
.setParameter("id", post.getId())
.setParameter("title", post.getTitle())
.setParameter("version", post.getVersion())
.setFlushMode(FlushMode.MANUAL)
.executeUpdate();
}
}
@Override
public boolean requiresPostCommitHanding(
EntityPersister persister) {
return false;
}
}
public class ReplicationUpdateEventListener
implements PostUpdateEventListener {
public static final ReplicationUpdateEventListener INSTANCE =
new ReplicationUpdateEventListener();
@Override
public void onPostUpdate(
PostUpdateEvent event) {
final Object entity = event.getEntity();
if(entity instanceof Post) {
Post post = (Post) entity;
event.getSession().createNativeQuery(
"UPDATE old_post " +
"SET title = :title, version = :version " +
"WHERE id = :id")
.setParameter("id", post.getId())
.setParameter("title", post.getTitle())
.setParameter("version", post.getVersion())
.setFlushMode(FlushMode.MANUAL)
.executeUpdate();
}
}
@Override
public boolean requiresPostCommitHanding(
EntityPersister persister) {
return false;
}
}
public class ReplicationDeleteEventListener
implements PreDeleteEventListener {
public static final ReplicationDeleteEventListener INSTANCE =
new ReplicationDeleteEventListener();
@Override
public boolean onPreDelete(
PreDeleteEvent event) {
final Object entity = event.getEntity();
if(entity instanceof Post) {
Post post = (Post) entity;
event.getSession().createNativeQuery(
"DELETE FROM old_post " +
"WHERE id = :id")
.setParameter("id", post.getId())
.setFlushMode(FlushMode.MANUAL)
.executeUpdate();
}
return false;
}
}
3つのイベントリスナーは、Hibernateを使用して登録することができますIntegrator
。
public class ReplicationEventListenerIntegrator
implements Integrator {
public static final ReplicationEventListenerIntegrator INSTANCE =
new ReplicationEventListenerIntegrator();
@Override
public void integrate(
Metadata metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
final EventListenerRegistry eventListenerRegistry =
serviceRegistry.getService(EventListenerRegistry.class);
eventListenerRegistry.appendListeners(
EventType.POST_INSERT,
ReplicationInsertEventListener.INSTANCE
);
eventListenerRegistry.appendListeners(
EventType.POST_UPDATE,
ReplicationUpdateEventListener.INSTANCE
);
eventListenerRegistry.appendListeners(
EventType.PRE_DELETE,
ReplicationDeleteEventListener.INSTANCE
);
}
@Override
public void disintegrate(
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
}
}
そして、このカスタムを使用するために休止状態を指示するIntegrator
には、設定する必要がありhibernate.integrator_provider
、構成プロパティを:
<property name="hibernate.integrator_provider"
value="com.vladmihalcea.book.hpjp.hibernate.listener.ReplicationEventListenerIntegrator "/>
試験時間
さて、永続化するときPost
実体を:
Post post1 = new Post();
post1.setId(1L);
post1.setTitle(
"The High-Performance Java Persistence book is to be released!"
);
entityManager.persist(post1);
Hibernateは、次のSQL INSERT文を実行します:
Query:["INSERT INTO old_post (id, title, version) VALUES (?, ?, ?)"], Params:[(1, The High-Performance Java Persistence book is to be released!, 0)]
Query:["insert into post (created_on, title, version, id) values (?, ?, ?, ?)"], Params:[(2018-12-12, The High-Performance Java Persistence book is to be released!, 0, 1)]
既存の更新別のトランザクション行う際Post
のエンティティをして、新しい作成しPost
たエンティティを:
Post post1 = entityManager.find(Post.class, 1L);
post1.setTitle(post1.getTitle().replace("to be ", ""));
Post post2 = new Post();
post2.setId(2L);
post2.setTitle(
"The High-Performance Java Persistence book is awesome!"
);
entityManager.persist(post2);
Hibernateはにすべてのアクションを複製しold_post
、テーブルにも:
Query:["select tablerepli0_.id as id1_1_0_, tablerepli0_.created_on as created_2_1_0_, tablerepli0_.title as title3_1_0_, tablerepli0_.version as version4_1_0_ from post tablerepli0_ where tablerepli0_.id=?"], Params:[(1)]
Query:["INSERT INTO old_post (id, title, version) VALUES (?, ?, ?)"], Params:[(2, The High-Performance Java Persistence book is awesome!, 0)]
Query:["insert into post (created_on, title, version, id) values (?, ?, ?, ?)"], Params:[(2018-12-12, The High-Performance Java Persistence book is awesome!, 0, 2)]
Query:["update post set created_on=?, title=?, version=? where id=? and version=?"], Params:[(2018-12-12, The High-Performance Java Persistence book is released!, 1, 1, 0)]
Query:["UPDATE old_post SET title = ?, version = ? WHERE id = ?"], Params:[(The High-Performance Java Persistence book is released!, 1, 1)]
削除する場合Post
エンティティを:
Post post1 = entityManager.getReference(Post.class, 1L);
entityManager.remove(post1);
old_post
レコードは、同様deletectedされています。
Query:["DELETE FROM old_post WHERE id = ?"], Params:[(1)]
Query:["delete from post where id=? and version=?"], Params:[(1, 1)]
詳細については、チェックアウトこの記事を。
コード上で利用できるGitHubの。