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

Slick 3.x封装MySQL驱动后,如何统一处理重复插入异常?

当然可以实现!

这其实是封装Slick数据库层时非常合理的需求——把数据库特定的细节完全隐藏在通用封装之后,让业务代码彻底摆脱对具体数据库驱动类的依赖。下面是具体的实现方案,分步骤帮你搞定:

1. 封装数据库异常处理工具

首先,在你的通用数据库封装模块里,创建一个专门处理数据库异常的工具类/单例对象,把MySQL的特定异常类在这里统一导入和处理。这样业务代码就完全不需要接触com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException了:

import java.sql.SQLException
import com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException

object DatabaseExceptionHandler {
    // 判断是否是MySQL唯一键/主键重复的异常
    def isDuplicateKeyViolation(e: SQLException): Boolean = {
        e match {
            case mysqlEx: MySQLIntegrityConstraintViolationException =>
                // MySQL重复键的标准错误码是1062,用错误码判断比仅依赖异常类更可靠
                mysqlEx.getErrorCode == 1062
            case _ => false
        }
    }

    // 定义一个自定义的业务异常,方便上层统一处理
    class DuplicateKeyException(message: String = "数据重复,违反唯一约束", cause: Throwable) 
        extends RuntimeException(message, cause)
    
    object DuplicateKeyException {
        def apply(cause: Throwable): DuplicateKeyException = 
            new DuplicateKeyException(cause = cause)
    }
}

2. 在通用DAO封装中统一处理异常

接下来,在你封装的通用DAO基类(比如BaseDAO)里,把数据库操作的异常处理逻辑统一加上。利用Slick的Future异步特性,在recover块里调用上面的工具类判断异常类型,转换成自定义的业务异常:

import slick.jdbc.JdbcProfile
import scala.concurrent.Future
import DatabaseExceptionHandler._

trait BaseDAO {
    // 这里是你已经封装好的通用Driver引用
    protected val profile: JdbcProfile
    protected val db: profile.api.Database

    import profile.api._

    // 封装通用的插入操作,自动处理重复键异常
    protected def safeInsert[T](action: profile.api.InsertAction[T]): Future[Int] = {
        db.run(action).recover {
            case sqlEx: SQLException if isDuplicateKeyViolation(sqlEx) =>
                // 抛出自定义异常,上层业务代码只需捕获这个异常即可
                throw DuplicateKeyException(sqlEx)
        }
    }

    // 你还可以封装其他通用操作,比如update、delete的异常处理
}

3. 业务层无需依赖MySQL特定类

现在,业务DAO只需要继承这个BaseDAO,调用封装好的safeInsert方法,完全不需要导入任何MySQL相关的类:

// 业务DAO类,完全不需要接触MySQL的异常类
class UserDAO extends BaseDAO {
    override protected val profile = MyDatabaseConfig.profile // 你的通用Driver配置
    override protected val db = MyDatabaseConfig.db

    // 用户表定义
    private val users = TableQuery[Users]

    def createUser(user: User): Future[Unit] = {
        safeInsert(users += user).map(_ => ())
    }
}

在业务逻辑层,你只需要捕获DatabaseExceptionHandler.DuplicateKeyException就能处理重复插入的场景,完全不用关心底层是MySQL的哪个具体异常类。

额外优势

这种做法还有两个额外好处:

  • 可扩展性:如果以后需要切换到其他数据库(比如PostgreSQL),只需要修改DatabaseExceptionHandler里的异常判断逻辑(比如PostgreSQL的重复键错误码是23505),业务代码完全不需要改动。
  • 统一逻辑:所有数据库操作的异常处理都集中在一处,避免了业务代码里到处写重复的异常判断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:16:49