You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Kotlin与KMongo实现多集合通用BaseDao的方案问询

用Kotlin+KMongo实现泛型BaseDao复用通用方法的完整方案

我经常在KMongo项目里遇到你说的这种Dao层重复代码的问题,用泛型BaseDao抽离通用逻辑是最优雅的解决方案,下面给你一步步拆解实现:

第一步:统一实体类的基础约束

首先咱们得确保所有实体类都具备MongoDB文档的核心标识——_id字段,这样通用CRUD方法才有统一的操作依据。你可以定义一个基础实体类让所有业务实体继承:

import org.litote.kmongo.Id
import org.litote.kmongo.newId

abstract class BaseEntity {
    val _id: Id<out BaseEntity> = newId()
}

// 业务实体示例
data class UserEntity(
    val username: String,
    val email: String
) : BaseEntity()

data class MovieEntity(
    val title: String,
    val releaseYear: Int
) : BaseEntity()

如果不想用继承,也可以直接在每个实体类里声明_id字段,只要保证字段名是_id或者用@BsonId注解标记主键就行。

第二步:实现泛型BaseDao抽象类

接下来咱们定义BaseDao<T>,这里把泛型约束为T : BaseEntity(如果没继承基础类,就用T : Any,但要确保T有主键字段)。核心是通过KMongo获取对应实体的集合,然后封装通用CRUD方法:

import org.litote.kmongo.Id
import org.litote.kmongo.MongoDatabase
import org.litote.kmongo.deleteOneById
import org.litote.kmongo.find
import org.litote.kmongo.findOneById
import org.litote.kmongo.insertOne
import org.litote.kmongo.insertMany
import org.litote.kmongo.updateOneById

abstract class BaseDao<T : BaseEntity>(private val database: MongoDatabase) {
    // 通过泛型获取对应的Mongo集合,懒加载提升性能
    private val collection by lazy {
        database.getCollection<T>()
    }

    // 根据ID查询单个文档
    open fun getById(id: Id<T>): T? {
        return collection.findOneById(id)
    }

    // 查询所有文档
    open fun getAll(): List<T> {
        return collection.find().toList()
    }

    // 插入单个文档并返回ID
    open fun insert(entity: T): Id<T> {
        collection.insertOne(entity)
        return entity._id as Id<T>
    }

    // 批量插入文档
    open fun insertMany(entities: List<T>) {
        collection.insertMany(entities)
    }

    // 根据ID更新文档,返回是否更新成功
    open fun updateById(id: Id<T>, entity: T): Boolean {
        val result = collection.updateOneById(id, entity)
        return result.modifiedCount > 0
    }

    // 根据ID删除文档,返回是否删除成功
    open fun deleteById(id: Id<T>): Boolean {
        val result = collection.deleteOneById(id)
        return result.deletedCount > 0
    }

    // 删除所有文档(谨慎使用!)
    open fun deleteAll(): Long {
        val result = collection.deleteMany()
        return result.deletedCount
    }
}

关键细节说明:

  • 用lazy初始化集合:避免提前创建集合实例,提升启动性能
  • 依赖MongoDatabase:可以通过Koin、Spring等依赖注入框架把数据库实例注入到Dao中
  • 所有通用方法标记为open:方便子类根据业务需求重写(比如某些实体需要自定义查询逻辑)

第三步:实现业务专属Dao类

现在你的业务Dao只需要继承BaseDao,专注实现自己的特有方法即可,完全不用重复写通用CRUD:

// UserDao示例
class UserDao(database: MongoDatabase) : BaseDao<UserEntity>(database) {
    // 自定义业务方法:根据用户名查询用户
    fun getByUsername(username: String): UserEntity? {
        return collection.find("{username: ?}", username).first()
    }
}

// MovieDao示例
class MovieDao(database: MongoDatabase) : BaseDao<MovieEntity>(database) {
    // 自定义业务方法:根据年份查询电影
    fun getByReleaseYear(year: Int): List<MovieEntity> {
        return collection.find("{releaseYear: ?}", year).toList()
    }
}

第四步:使用Dao的示例

最后,你可以通过依赖注入或者直接实例化的方式使用这些Dao:

import org.litote.kmongo.KMongo

fun main() {
    // 初始化MongoDB连接
    val client = KMongo.createClient("mongodb://localhost:27017")
    val database = client.getDatabase("my_database")

    // 实例化Dao
    val userDao = UserDao(database)
    val movieDao = MovieDao(database)

    // 使用通用方法
    val newUser = UserEntity("john_doe", "john@example.com")
    val userId = userDao.insert(newUser)
    val foundUser = userDao.getById(userId)
    println(foundUser)

    // 使用自定义方法
    val movies2023 = movieDao.getByReleaseYear(2023)
    println(movies2023)
}

可选优化:如果实体没有继承BaseEntity

如果你的实体类不想继承BaseEntity,可以调整BaseDao的泛型约束,并通过@BsonId标记主键字段:

// 实体类示例
data class UserEntity(
    @BsonId val id: Id<UserEntity> = newId(),
    val username: String,
    val email: String
)

// 调整后的BaseDao
abstract class BaseDao<T : Any>(private val database: MongoDatabase) {
    private val collection by lazy { database.getCollection<T>() }

    // 这里需要确保T有@BsonId标记的主键字段,类型为Id<T>
    open fun getById(id: Id<T>): T? {
        return collection.findOneById(id)
    }

    // 其他通用方法和之前一致...
}

这样同样能实现通用方法的复用,只是需要每个实体自己声明主键字段。

内容的提问来源于stack exchange,提问作者Blo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:45:45