使用 jOOQ 在 PostgreSQL 中进行 UPSERT
Posted
技术标签:
【中文标题】使用 jOOQ 在 PostgreSQL 中进行 UPSERT【英文标题】:UPSERT in PostgreSQL using jOOQ 【发布时间】:2014-05-18 12:10:21 【问题描述】:我正在尝试使用 jOOQ 库在 PostgreSQL 中执行 UPSERT。
为此,我目前正在尝试在 jOOQ 中实现以下 SQL 语句: https://***.com/a/6527838
到目前为止,我的代码如下所示:
public class UpsertExecutor
private static final Logger logger = LoggerFactory.getLogger(UpsertExecutor.class);
private final JOOQContextProvider jooqProvider;
@Inject
public UpsertExecutor(JOOQContextProvider jooqProvider)
Preconditions.checkNotNull(jooqProvider);
this.jooqProvider = jooqProvider;
@Transactional
public <T extends Record> void executeUpsert(Table<T> table, Condition condition, Map<? extends Field<?>, ?> recordValues)
/*
* All of this is for trying to do an UPSERT on PostgreSQL. See:
* https://***.com/a/6527838
*/
SelectConditionStep<Record1<Integer>> notExistsSelect = jooqProvider.getDSLContext().selectOne().from(table).where(condition);
SelectConditionStep<Record> insertIntoSelect = jooqProvider.getDSLContext().select(recordValues).whereNotExists(notExistsSelect);
try
int[] result = jooqProvider.getDSLContext().batch(
jooqProvider.getDSLContext().update(table).set(recordValues).where(condition),
jooqProvider.getDSLContext().insertInto(table).select(insertIntoSelect)
).execute();
long rowsAffectedTotal = 0;
for (int rowsAffected : result)
rowsAffectedTotal += rowsAffected;
if (rowsAffectedTotal != 1)
throw new RuntimeException("Upsert must only affect 1 row. Affected: " + rowsAffectedTotal + ". Table: " + table + ". Condition: " + condition);
catch (DataAccessException e)
if (e.getCause() instanceof BatchUpdateException)
BatchUpdateException cause = (BatchUpdateException)e.getCause();
logger.error("Batch update error in upsert.", cause.getNextException());
throw e;
但此代码无法编译,因为 select() 不支持值映射:
SelectConditionStep<Record> insertIntoSelect = jooqProvider.getDSLContext().select(recordValues).whereNotExists(notExistsSelect);
问题
如何为 select() 提供一组预定义值,例如:SELECT 3, 'C', 'Z'
?
更新 1
我设法让代码正常工作。这是完整的课程:
public class UpsertExecutor
private static final Logger logger = LoggerFactory.getLogger(UpsertExecutor.class);
private final JOOQContextProvider jooqProvider;
@Inject
public UpsertExecutor(JOOQContextProvider jooqProvider)
Preconditions.checkNotNull(jooqProvider);
this.jooqProvider = jooqProvider;
@Transactional
public <T extends Record> void executeUpsert(Table<T> table, Condition condition, List<FieldValue<Field<?>, ?>> recordValues)
/*
* All of this is for trying to do an UPSERT on PostgreSQL. See:
* https://***.com/a/6527838
*/
Map<Field<?>, Object> recordValuesMap = new HashMap<Field<?>, Object>();
for (FieldValue<Field<?>, ?> entry : recordValues)
recordValuesMap.put(entry.getFieldName(), entry.getFieldValue());
List<Param<?>> params = new LinkedList<Param<?>>();
for (FieldValue<Field<?>, ?> entry : recordValues)
params.add(val(entry.getFieldValue()));
List<Field<?>> fields = new LinkedList<Field<?>>();
for (FieldValue<Field<?>, ?> entry : recordValues)
fields.add(entry.getFieldName());
SelectConditionStep<Record1<Integer>> notExistsSelect = jooqProvider.getDSLContext().selectOne().from(table).where(condition);
SelectConditionStep<Record> insertIntoSelect = jooqProvider.getDSLContext().select(params).whereNotExists(notExistsSelect);
try
int[] result = jooqProvider.getDSLContext().batch(
jooqProvider.getDSLContext().update(table).set(recordValuesMap).where(condition),
jooqProvider.getDSLContext().insertInto(table, fields).select(insertIntoSelect)
).execute();
long rowsAffectedTotal = 0;
for (int rowsAffected : result)
rowsAffectedTotal += rowsAffected;
if (rowsAffectedTotal != 1)
throw new RuntimeException("Upsert must only affect 1 row. Affected: " + rowsAffectedTotal + ". Table: " + table + ". Condition: " + condition);
catch (DataAccessException e)
if (e.getCause() instanceof BatchUpdateException)
BatchUpdateException cause = (BatchUpdateException)e.getCause();
logger.error("Batch update error in upsert.", cause.getNextException());
throw e;
但是使用List<FieldValue<Field<?>, ?>> recordValues
参数感觉不是很干净。关于如何做到这一点有更好的想法吗?
【问题讨论】:
看来 val() 静态方法可能是我正在寻找的:jooq.org/javadoc/3.3.x/org/jooq/impl/… 你知道你必须在重试循环中使用SERIALIZABLE
事务,或者锁定表,这样才能可靠地工作,对吧?
@CraigRinger 是的,我知道这一点。你能推荐一个更好的方法吗?我对这个问题的更好解决方案非常感兴趣。
sql select 3,'C','Z';
与 sql values (3,'C','Z');
非常相似,所以是的 val()
听起来是正确的方法。
仅供参考 PosgreSQL 9.5 将具有 UPSERT。 wiki.postgresql.org/wiki/UPSERT
【参考方案1】:
实现目标的方法似乎有点复杂。为什么不使用简单的存储函数? postgresql manual 中描述了如何创建 upsert 函数,然后只需从您的 java 代码中调用它。
【讨论】:
+1。现在实现 upsert 的唯一正确方法(直到 9.5 发布)是使用存储函数和循环,如给定链接中所写。更多信息在这里depesz.com/2012/06/10/why-is-upsert-so-complicated【参考方案2】:jOOQ 3.7+ 支持 PostgreSQL 9.5 的 ON CONFLICT
子句:
尚不支持完整的 PostgreSQL 供应商特定语法,但您可以使用 mysql 或 H2 语法,它们都可以使用 PostgreSQL 的ON CONFLICT
进行模拟:
MySQLINSERT .. ON DUPLICATE KEY UPDATE
:
DSL.using(configuration)
.insertInto(TABLE)
.columns(ID, A, B)
.values(1, "a", "b")
.onDuplicateKeyUpdate()
.set(A, "a")
.set(B, "b")
.execute();
H2MERGE INTO ..
DSL.using(configuration)
.mergeInto(TABLE, A, B, C)
.values(1, "a", "b")
.execute();
【讨论】:
从 Lucas 的 TableRecord 对象解决方案派生的实用程序:public static int upsert(final DSLContext dslContext, final UpdatableRecord record) return dslContext.insertInto(record.getTable()).set(record).onDuplicateKeyUpdate().set(record).execute();
@ud3sh:嘿,谢谢你的提及。随时提供此作为完整答案。我相信它可能对其他人有用!【参考方案3】:
这是一个 upsert 实用方法,源自 Lucas 的上述 UpdatableRecord 对象解决方案:
public static int upsert(final DSLContext dslContext, final UpdatableRecord record)
return dslContext.insertInto(record.getTable())
.set(record)
.onDuplicateKeyUpdate()
.set(record)
.execute();
【讨论】:
【参考方案4】:灵感来自 @ud3sh 对 JOOQ 3.11、Kotlin 和 PostgreSQL DSL 的评论
这是直接在UpdatableRecord
对象上调用upsert
的扩展函数
import org.jooq.UpdatableRecord
internal fun UpdatableRecord<*>.upsert(): Int
if(this.configuration() == null)
throw NullPointerException("Attach configuration to record before calling upsert")
return this.configuration().dsl().insertInto(this.getTable()).set(this).onConflict().doUpdate().set(this).execute()
【讨论】:
以上是关于使用 jOOQ 在 PostgreSQL 中进行 UPSERT的主要内容,如果未能解决你的问题,请参考以下文章
JOOQ快速上手(基于springboot 和 postgresql)
如何使用 JOOQ 在 PostgreSQL 中插入带有 JSON 列的可更新记录?
在 PostgreSQL 数据库中插入带有 jOOQ 的 SQL 枚举