Я хотел бы переименовать таблицу PostgreSQL (9.6) таким образом, что это извлекаемые для моей системы (приложение Java с помощью JPA / Hibernate)
В моем 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, но будет серьезно рассмотреть вопрос о 3-й варианте партии также.
Это был очень интересным вопрос, так что я решил превратить этот ответ в полномасштабную статью .
таблицы баз данных
Если у вас есть следующие две таблицы:
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
Теперь мы должны зарегистрировать 3 слушателей событий для перехвата INSERT, UPDATE и DELETE операций для 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) {
}
}
И поручить Hibernate использовать этот обычай 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 .