2015-09-12 5 views
0

Я следующие классы, работающие против БД MySQL:Scala Slick: Insert терпит неудачу с RejectedExecutionException

case class User(val userId: Option[String], val firstName: String, val lastName: String, val phoneNum: String, val email: Option[String]) { 
} 

class Users(tag: Tag) extends Table[User](tag, "USERS") { 

    def userId = column[String]("USER_ID", O.PrimaryKey) 
    def firstName = column[String]("FIRST_NAME") 
    def lastName = column[String]("LAST_NAME") 
    def phoneNum = column[String]("PHONE_NUM") 
    def email = column[String]("EMAIL") 

    def * = (userId.?, firstName, lastName, phoneNum, email.?) <> (User.tupled, User.unapply) 
} 

class MySQLUserRepository(private val db: Database)(implicit val executor: ExecutionContextExecutor) extends UserService { 
    val users = TableQuery[Users] 
    def findByFirstName(firstName: String): Future[immutable.Seq[User]] = { 
    val query = users.filter { _.firstName === firstName } 

    runAndThenCleanUp(query) 
    } 

    private def runAndThenCleanUp(query: Query[Users, User, Seq]): Future[immutable.Seq[User]] = { 
    try db.run(query.result).map { _.toList } finally db.close 
    } 

    def createUser(user: User) = { 
    val createAction: DBIO[Option[Int]] = users ++= Seq(user) 

    db.run(createAction.asTry.map { 
     _ match { 
     case Success(res) => res.map { _ => user } 
     case Failure(e) => println(e); None 
     } 
    }) 
    } 
} 

И тест:

class MySQLUserRepositorySpec extends fixture.FlatSpec with Matchers with BeforeAndAfterAll with ScalaFutures { 
    private val userRepository = new MySQLUserRepository(db)(global) 

    implicit val defaultPatience = PatienceConfig(timeout = Span(5, Seconds), interval = Span(500, Millis)) 

    val query = TableQuery[Users] 

    type FixtureParam = User 

    def withFixture(test: OneArgTest) = { 
    val users = userRepository.findByFirstName("John") 

    users.futureValue shouldBe empty 

    dumpAllUsers 

    val testUser = User(Some("1"), "John", "Doe", "111-111-1111", Some("[email protected]")) 

    val newUser = userRepository.createUser(testUser) 

    newUser.onFailure {case NonFatal(ex) => ex.printStackTrace } 
    val user = newUser.futureValue 
    user shouldBe defined 

    println("Before test") 
    dumpAllUsers 

    try { 
     println("Running test") 
     withFixture(test.toNoArgTest(user.get)) // "loan" the fixture to the test 
    } finally { // clean up the fixture 
     println("After test") 
     dumpAllUsers 

//  try db.run(query.delete) finally db.close 
    } 
    } 

    override def afterAll() { 
    println("Cleaning up") 
// try db.run(query.delete) finally db.close 
    } 

    private def dumpAllUsers = { 
    println("Printing all users") 
    (query.result).map { _.toList } 
    } 

    it should "find user with first name" in { testUser => 
    val users = userRepository.findByFirstName(testUser.firstName) 
    verifySingleUser(users.futureValue) 
    } 
} 

я получаю исключение:

2015-09-12 00:45:14.592 [ScalaTest-main-running-MySQLUserRepositorySpec] [DEBUG] s.b.D.action - #1: [fused] asTry 
java.util.concurrent.RejectedExecutionException: Task [email protected] rejected from [email protected][Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 1] 
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047) 

Я читал this и this, безрезультатно. Что я делаю не так? Я подозреваю, что Слик ошибочно отображает столбец PK как обнуляемый (из-за типа Option).

Таблица DDL:

CREATE TABLE `akka`.`users` (
    `user_id` VARCHAR(16) NOT NULL COMMENT '', 
    `first_name` VARCHAR(50) NOT NULL COMMENT '', 
    `last_name` VARCHAR(50) NOT NULL COMMENT '', 
    `phone_num` VARCHAR(25) NOT NULL COMMENT '', 
    `email` VARCHAR(50) NULL COMMENT '', 
    PRIMARY KEY (`user_id`) COMMENT '', 
    UNIQUE INDEX `phone_num_UNIQUE` (`phone_num` ASC) COMMENT '', 
    UNIQUE INDEX `email_UNIQUE` (`email` ASC) COMMENT ''); 

ответ

1

Проблема Ваш runAndThenCleanUp метод. db имеет пул подключения к базе данных, который db.closeотключает. db.run отправляет запрос в пул выполнения, но затем, прежде чем он сможет выполнить запрос, вы закрываете пул.

Не беспокойтесь об очистке соединений в ваших индивидуальных вызовах метода - просто убедитесь, что вы вызываете db.close, когда ваше приложение закрывается, чтобы платно отключилось. Slick (и HikariCP под капотом) позаботится о вашем управлении связью.

Короче говоря, это изменить:

private def runAndThenCleanUp(query: Query[Users, User, Seq]): Future[immutable.Seq[User]] = { 
    try db.run(query.result).map { _.toList } finally db.close 
} 

к этому:

private def runQuery(query: Query[Users, User, Seq]): Future[immutable.Seq[User]] = { 
    db.run(query.result).map { _.toList } 
} 
+0

Это действительно проблема. Спасибо. BTW Я не использую HikariCP, а DBCP2. –

Смежные вопросы