【问题标题】:How to do an insert if not exists with Kotlin Exposed Postgres?如果 Kotlin Exposed Postgres 不存在,如何插入?
【发布时间】:2023-02-06 20:28:37
【问题描述】:

我正在构建一个费用跟踪器应用程序,使用 Spring Boot 和 Kotlin 来制作 API,并将用户数据存储到 Postgresql 数据库中。我选择了 Exposed ORM 来处理 Spring 和 Postgres 之间的所有 CRUD 操作。

要将费用插入数据库,用户可以选择标签以更好地组织他们的费用。费用和标签是多对多关系(一个费用可以有多个标签,一个标签可以有多个费用)。

我有以下代码来定义表:

费用

object ExpenseTable : IntIdTable("expense") {
    val userId: Column<String> = varchar("user_id", 50)
    val concept: Column<String> = varchar("concept", 50)
    val total: Column<Double> = double("total")
    val dateAdded: Column<LocalDateTime> = datetime("date_added")
    val comments: Column<String?> = varchar("comments", 200).nullable()
}

class ExpenseEntity(
    id: EntityID<Int>
) : IntEntity(id) {
    companion object : IntEntityClass<ExpenseEntity>(ExpenseTable)

    var userId by ExpenseTable.userId
    var concept by ExpenseTable.concept
    var total by ExpenseTable.total
    var dateAdded by ExpenseTable.dateAdded
    var tags by TagEntity via ExpensesTags
    var comments by ExpenseTable.comments

    fun toExpense() = Expenses(
        id.value,
        userId,
        concept,
        total,
        dateAdded,
        tags.toList().toTags(),
        comments
    )
}

标签

object TagsTable: IntIdTable("tag") {
    val tagName: Column<String> = varchar("tag_name", 25)
    val dateAdded: Column<LocalDateTime> = datetime("date_added")
}

class TagEntity(
    id: EntityID<Int>
): IntEntity(id) {
    companion object: IntEntityClass<TagEntity>(TagsTable)

    var tagName by TagsTable.tagName
    var dateAdded by TagsTable.dateAdded

    fun toTags() = Tags(
        id.value,
        tagName,
        dateAdded
    )
}

多对多关系

object ExpensesTags : Table() {
    val expense = reference("expense", ExpenseTable)
    val tag = reference("tag", TagsTable)
    override val primaryKey = PrimaryKey(expense, tag, name = "PK_ExpensesTags")
}

我只想拥有一种用户可以创建的标签,到目前为止的问题是,使用 ORM 中的可用代码,我无法找到一种方法来确保用户正在使用的标签是否存在。

到目前为止,我已经有了这个实现,但我觉得这不是最好的方法,因为我需要查询并检查标签是否已经存在,而且感觉有点过头了。

fun insertExpense(expenses: ExpensesPost): Expenses {
    val userIdName = authenticationFacade.userId()
    val tagsPost = expenses.tag

    // We only accept 10 tags max per request
    if (tagsPost.size > MAX_TAG_REQUEST) throw BadRequestException(
        Status.BAD_REQUEST,
        "Only $MAX_TAG_REQUEST tags are allowed"
    )

    var insertedExpense: Expenses? = null
    loggedTransaction {
        // Check if some tags already exists
        tagsPost.forEach { tag ->
            val internTag = tagsCrudTable.find { TagsTable.tagName eq tag.tagName }.firstOrNull()
            // Only insert into the table tags that doesn't exist
            if (internTag == null) {
                tagsCrudTable.new {
                    dateAdded = tag.dateAdded
                    tagName = tag.tagName
                }
            }
        }
        // Get all the tags that come from the request
        val tagsArr = mutableListOf<TagEntity>()
        tagsPost.map {
            val internTag = tagsCrudTable.find {
                TagsTable.tagName eq it.tagName
            }.first()
            tagsArr.add(internTag)
        }
        val expense = expenseCrudTable.new {
            userId = userIdName
            concept = expenses.concept
            total = expenses.total
            dateAdded = expenses.dateAdded
            comments = expenses.comments
        }

        expense.tags = SizedCollection(tagsArr)
        insertedExpense = expense.toExpense()
    }

    return insertedExpense ?: throw EntityNotFoundException(
        status = Status.NO_DATA,
        customMessage = "Something went wrong",
        id = authenticationFacade.userId()
    )
}

【问题讨论】:

    标签: postgresql spring-boot kotlin orm kotlin-exposed


    【解决方案1】:

    您应该能够通过将 tag_name 定义为唯一并使用 insertIgnore 来实现此目的:

    object TagsTable: IntIdTable("tag") {
        val tagName = varchar("tag_name", 25).uniqueIndex()
        ...
    }
    

    然后这个块:

            tagsPost.forEach { tag ->
                val internTag = tagsCrudTable.find { TagsTable.tagName eq tag.tagName }.firstOrNull()
                // Only insert into the table tags that doesn't exist
                if (internTag == null) {
                    tagsCrudTable.new {
                        dateAdded = tag.dateAdded
                        tagName = tag.tagName
                    }
                }
            }
    

    可以改成这样:

    tagsPost.forEach { tag ->
        tagsCrudTable.table.insertIgnore {
            dateAdded = tag.dateAdded
            tagName = tag.tagName
        }
    }
    

    请注意,insertIgnore 仅适用于表,不适用于实体。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-03
      • 2013-02-18
      • 2018-09-17
      • 1970-01-01
      • 1970-01-01
      • 2019-01-16
      相关资源
      最近更新 更多