PostgreSQL Upsert 使用记录

PostgreSQL Upsert Using Records

我目前正在尝试使用记录执行更新插入(使用 3.10 快照),并且注意到当我使用 addRecord(R) 时,更新的 set 数据没有添加到查询中。在 DAO 中我有:

    try (Connection writeConn = DatabaseManager.getConnection(true);
            final DSLContext writeContext = DatabaseManager.getBuilder(writeConn);
            InsertQuery<UserProgramAssignmentsRecord> programAssignmentsUpdateQuery = writeContext
                    .insertQuery(AccessControlWriteDAO.userProgramAssignments)) {
        programAssignmentsUpdateQuery.onDuplicateKeyUpdate(true);

        programAssignments.forEach(pa -> programAssignmentsUpdateQuery.addRecord(pa.toRecord()));

        if (!programAssignments.isEmpty()) {
            if (AccessControlWriteDAO.logger.isInfoEnabled()) {
                AccessControlWriteDAO.logger.info(writeContext.renderInlined(programAssignmentsUpdateQuery));
            }
            // programAssignmentsUpdateQuery.execute();
        }
    } catch (final SQLException e) {
        throw new DAOException(e.getMessage(), e);
    }

在 programAssignments 的 class 内部,我有:

/**
 * <p>
 * This method turns the object into a {@link Record} that can then be attached to a {@link Connection} and
 * {@link DSLContext}.
 * </p>
 * 
 * @return A usable {@link UserProgramAssignmentsRecord}
 */
public UserProgramAssignmentsRecord toRecord() {
    UserProgramAssignmentsRecord upar = new UserProgramAssignmentsRecord();
    getAdministerProgram().ifPresent(upar::setAdminister);
    getCreateProjects().ifPresent(upar::setCreateProjects);
    getJustification().ifPresent(upar::setJustification);
    getProgramId().ifPresent(upar::setProgramId);
    getUserId().ifPresent(upar::setUserId);
    getViewRevisions().ifPresent(upar::setViewRevisions);

    return upar;
}

代码生成以下查询:

insert into "%%removed%%"."user_project_assignments" (
    "project_id", 
    "edit_draft", 
    "justification", 
    "user_id"
)
values (
    98332, 
    true, 
    'The user created the project', 
    675
)
on conflict (
    "project_id", 
    "user_id"
) do update
set [ no fields are updated ]

我必须改用 addValue(Field<T>,T)addValueForUpdateField(Field<T>,T) 和 newRecord 吗?我会 认为 传递记录是等效的,但情况似乎并非如此,因为 addRecord(R) 似乎没有将字段设置为更新 - 或者- 我的 toRecord() 方法有问题吗? InsertQuery 对象有某种 addRecordForUpdate(R) 吗?这对多行更新插入有多有效(这是我正在尝试做的)?

查看文档后,我认为这有点……不足……虽然有人可能 认为 addRecord(R) + onDuplicateKeyUpdate(true) 将意味着更新数据设置为 会有一个 addValueForUpdate(R),这(显然)是错误的。我的解决方案是让我的 POJO 也有另一种方法 toExcludedMap(),看起来像这样:

/**
 * <p>
 * This method returns mappings that can be used in an UPSERT in the update portion of the query.
 * </p>
 *
 * @return A map between the assignments table and the excluded table
 */
public Map<Field<?>, Field<?>> toExcludedMap() {
    final UserProgramAssignments excludedTable = UserProgramAssignment.pua.as("excluded");

    final Map<Field<?>, Field<?>> excludedMap = new HashMap<>(4);
    getAdministerProgram()
            .ifPresent(i -> excludedMap.put(UserProgramAssignment.pua.ADMINISTER, excludedTable.ADMINISTER));
    getCreateProjects().ifPresent(
            i -> excludedMap.put(UserProgramAssignment.pua.CREATE_PROJECTS, excludedTable.CREATE_PROJECTS));
    getJustification()
            .ifPresent(i -> excludedMap.put(UserProgramAssignment.pua.JUSTIFICATION, excludedTable.JUSTIFICATION));
    getViewRevisions().ifPresent(
            i -> excludedMap.put(UserProgramAssignment.pua.VIEW_REVISIONS, excludedTable.VIEW_REVISIONS));

    return excludedMap;
}

然后我将我的 DAO 调整为这样:

    try (Connection writeConn = DatabaseManager.getConnection(true);
            final DSLContext writeContext = DatabaseManager.getBuilder(writeConn);
            InsertQuery<UserProgramAssignmentsRecord> programAssignmentsUpdateQuery = writeContext
                    .insertQuery(AccessControlWriteDAO.userProgramAssignments)) {
        programAssignmentsUpdateQuery.onDuplicateKeyUpdate(true);

        programAssignments
                .forEach(assignment -> AccessControlWriteDAO.addRecordToUpsert(programAssignmentsUpdateQuery,
                        assignment.toRecord(), assignment.toExcludedMap()));

        if (!programAssignments.isEmpty()) {
            if (AccessControlWriteDAO.logger.isInfoEnabled()) {
                AccessControlWriteDAO.logger.info(writeContext.renderInlined(programAssignmentsUpdateQuery));
            }
            programAssignmentsUpdateQuery.execute();
        }
    } catch (final SQLException e) {
        throw new DAOException(e.getMessage(), e);
    }

然后,为了保持干净,我创建了一个更新 InsertQuery 的新方法:

/**
 * <p>
 * This method is used to add a record and the update information to an UPSERT query.
 * </p>
 *
 * @param query
 *            The query to add to
 * @param record
 *            The record to add
 * @param excludedMap
 *            The mappings to the "excluded" table for this record
 */
private static <R extends Record> void addRecordToUpsert(final InsertQuery<R> query, final R record,
        final Map<Field<?>, Field<?>> excludedMap) {
    query.addRecord(record);
    query.addValuesForUpdate(excludedMap);
}

有点糟透了,我们没有对称性,我 1) 必须制作地图,2) 必须再次调用方法来获取数据在 我相信这是目前唯一的方法。如果 jOOQ 添加更新数据或至少记录并排除 PK 会很好(因为如果我们更新重复键,显然 PK 没有改变),但也许这是不可能的。