我想以一种可为我的系统恢复的方式重命名PostgreSQL(9.6)表(使用JPA / Hibernate的Java应用程序)
在我的Java代码中,JPA实体将具有以下注释@Entity
@Table(name="old_name")
,数据库将具有名为的等效表old_name
。
我想将表重命名为new_name
,以便可以逐步更新数据库和Java应用程序,从而允许失败和回滚。
典型的步骤是
old_name
in的副本new_name
new_name
old_name
实际上,我希望在具有相同数据的相同模式下的重复表能够接收来自JPA实体的读取和写入。
我知道触发器的使用,并希望避免这种情况。我希望有一种我不知道并且还没有发现的技术,比使用触发器可以减轻痛苦。
我试图重命名该表并在其上创建一个“简单视图”,但是JPA实体抱怨,因为它找不到带有视图名称的表。(因为它是一个视图,而不是一个表:),并且似乎没有@
View / @ Table JPA批注可以处理此问题)
我还没有尝试过这里列出的功能:http
:
//wiki.postgresql.org/wiki/Replication,_Clustering,_and_Connection_Pooling,因为大多数似乎与池化,分片有关,我需要一个简单的短期表副本,但是我还将对此进行调查。
谢谢-当然,我想要最简单的选择,宁愿内置于postgres / JPA中,但也要认真考虑第三者的选择。
由于这是一个非常常见的问题,因此我写了 这篇文章,此答案基于该文章。
假设您有以下两个表:
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)
)
该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
}
现在,我们必须注册3个事件侦听器,以拦截Post
实体的INSERT,UPDATE和DELETE操作。
我们可以通过以下事件监听器来做到这一点:
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;
}
}
可以使用Hibernate注册3个事件侦听器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
记录也被删除:
Query:["DELETE FROM old_post WHERE id = ?"], Params:[(1)]
Query:["delete from post where id=? and version=?"], Params:[(1, 1)]
有关更多详细信息,请查看本文。
代码可在GitHub上获得。
问题内容: 我想知道以下脚本是否可以某种方式进行优化。它确实在磁盘上写了很多东西,因为它可能删除了最新的行并重新插入它们。我正在考虑应用“在重复键更新中插入…”之类的东西,并发现了单行更新的一些可能性,但我不知道如何在的上下文中应用它。 编辑: 的架构,,,:http://pastebin.com/3tRVPPVi。 要点是更新列和中的内容。表上有一个触发器,可根据这些列设置列的值。 in是表中的
问题内容: 控制器逻辑: 服务逻辑 怎么了 在90%的情况下,这将正常工作。 问题 我已经阅读了相关问题,并找到了上面看到的电话。它解决了大约50%的案件,但不是全部。 问题答案: 对于我们来说,一些不同的方法最终解决了StaleObjectException定期发生的问题: 检索对象后刷新对象解决了我们的大多数StaleObjectExceptions。尤其是在某人可能会从其他地方处理同一对象并
问题内容: 我已经能够让JPA / Hibernate 成功复制功能(似乎是默认行为),但是我现在正在尝试复制功能,并且遇到了问题。 这是我的两节课: 当我尝试删除老师时,出现以下错误: org.springframework.dao.DataIntegrityViolationException:无法执行JDBC批处理更新。SQL [从教师那里删除,其中Teacher_id =?];约束[nul
问题内容: 我在一个表中有触发器,并且想在插入,更新或删除行时读取值。怎么做?下面的代码不起作用,出现错误 问题答案: 请注意,其含义与相同,并给出每一行的每种组合。我怀疑这就是你想要的。 这样的事情可能会帮助您入门… 然后根据您要执行的操作,使用或引用您感兴趣的表,等等。 最后,请注意和是 表, 并且可以(并且确实)包含多个记录。 如果您一次插入10条记录,该表将包含所有10条记录。删除和表也
Hibernate3 can use custom SQL for create, update, and delete operations. The SQL can be overridden at the statement level or inidividual column level. This section describes statement overrides. For c
问题内容: 这个问题已经在这里有了答案 : 如果存在使用VB.net更新其他插入(SQL参数化查询) (3个答案) 7年前关闭。 我有以下查询: 我需要查看表中是否有任何记录,是否有更新而不是其他插入。如何实现? 问题答案: 您可以在SQL中使用Exists函数。例如 这应该够了吧