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
相关产品推荐
相关产品推荐

