【问题标题】:Good practice for saving only unique by property records to the database仅将唯一的属性记录保存到数据库的良好做法
【发布时间】:2020-09-01 02:21:15
【问题描述】:

我想知道在以下情况下有什么好的做法:

@Entity
@Table(name = "word")
class WordEntity(uuid: UUID? = null,
                 @Column(nullable = false, unique = true) val name: String
) : BaseEntity(uuid) 

如果我有这个实体的 Iterable 并且想要调用方法 saveAll 将其持久保存到数据库中,则可以抛出 ConstraintViolationException。

所以我的目标是只将唯一记录添加到数据库中。我可以循环并执行以下操作:

fun saveAll(words: List<WordRequest>): List<WordDTO> {

        ...

        for (wordEntity in wordEntities) {
            try {
                result.add(wordRepository.save(wordEntity))
            } catch (e: RuntimeException) { }
        }

        ...
    }

或者我可以在每个循环上做一个 findByName 来检查它是否存在。

所以我的问题是我应该选择哪个选项,有没有更好的方法来处理这个问题?

【问题讨论】:

    标签: java spring database jpa kotlin


    【解决方案1】:

    你有两个选择:

    • 某些数据库支持 INSERT IF NOT EXIST 语法。它正在做你需要的。但这意味着您需要编写原生 SQL。
    • 如果您想坚持使用 ORM - 您必须为每条记录打开一个新会话(并启动新事务)。因为如果抛出异常,您将无法继续依赖同一个 Session (EntityManager),因此该行为未记录在案。

    检查记录是否已经存在可能会减少失败的 INSERT 数量(您可以使用 in() 语句在 1 个 SELECT 中执行此操作),但它不能保证任何事情 - 在您的 SELECT 和 INSERT 之间存在时间。另一个事务可以在两者之间插入。好吧,除非你使用 Serializable 隔离级别。

    【讨论】:

    • 感谢您的回答。我尝试了使用本机查询的第一个选项 (@Query( value = "INSERT INTO word (uuid, name) VALUES (?1, ?2) ON CONFLICT (name) DO NOTHING;", nativeQuery = true)) 但它抛出GenericJDBCException:无法提取 ResultSet。既然插不进去
    • 如果使用 Spring Data,还需要为 INSERTs/UPDATEs 添加@Modifying
    • 解决了这个问题,谢谢。您知道添加修改注释时是否可以将插入的行作为实体返回?
    • 由于您使用的是 UUID,因此您没有 getGeneratedKeys() 选项。所以这可能是不可能的,看看你的数据库是否支持returning语法:stackoverflow.com/a/3552290/886697。您必须尝试使用​​ SpringData(例如删除 @Modifying),但我猜您必须在纯 JDBC 中执行此操作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多