LoginSignup
2
2

More than 5 years have passed since last update.

Playframework2 + Slickでたまに発生するランタイムエラー

Posted at

この記事の目的

Playframeworkは静的型付けにより型安全になっているため、実行時エラーが起こることはあまりありませんが、いくつか発生したケースがあるためメモとして記録します。

動作環境

  • scala 2.11.7
  • play 2.4.3
  • slick 3.1.1

モデルのサンプルコード

説明に使用するサンプルコードです。

Dog.scala
package models
import slick.driver.MySQLDriver.api._

case class Dog(name: String, color: String, breed: Option[String])

class DogTables(tag: Tag) extends Table[Dog](tag, "DOGS") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def color = column[String]("color") 
  def breed = column[Option[String]]("breed")
  def * = (name, color, breed) <> (Dog.tupled, Dog.unapply _)
}

例外

SlickException: Read NULL value for ResultSet column

  • DBのデータをモデルにマッピングにした際、Option指定がない属性に対応するカラムにNullがセットされていた場合に例外が発生します。

  • DogTablesを例にすると、nameはStringで定義されていますが、テーブル定義ではNullが許可されいて、かつ実際にNullの値が設定されていた場合に例外が発生します。

Dog.scala
class DogTables(tag: Tag) extends Table[Dog](tag, "DOGS") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name") // Optionの指定がないためRead時にNullが入るとエラー
  def color = column[String]("color") 
  def breed = column[Option[String]]("breed")
  def * = (name, color, breed) <> (Dog.tupled, Dog.unapply _)
}
table_description
mysql> desc dogs;
+-----------+--------------+-----+-----+----------+---------------+
| Field     | Type         | Null| Key | Default  | Extra         |
+-----------+--------------+-----+-----+----------+---------------+
| ID        | int(11)      | NO  | PRI | NULL     | auto_increment|
| NAME      | varchar(255) | YES |     | NULL     |               |
| COLOR     | varchar(255) | NO  |     | NULL     |               |
| BREED     | varchar(255) | YES |     | NULL     |               |
+-----------+--------------+-----+-----+----------+---------------+

  • 対応方法: モデルとテーブル定義の型を合わせる。
    • テーブル定義でNull YesをNull Noに変更する。
    • または、DogTablesのnameをcolumn[Option[String]]と指定する。

--

[MySQLSyntaxErrorException: Unknown column 'CREATED_AT' in 'field list’]

  • モデルに定義している属性がDBのテーブル定義にない場合に発生します。
Dog.scala
class DogTables(tag: Tag) extends Table[Dog](tag, "DOGS") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def color = column[String]("color") 
  def breed = column[Option[String]]("breed")
  def createdAt = column[Timestamp]("CREATED_AT")  // dogsテーブルにないカラムを指定
  def updatedAt = column[Timestamp]("UPDATED_AT")  // dogsテーブルにないカラムを指定 
  def * = (name, color, breed, createdAt, updatedAt) <> (Dog.tupled, Dog.unapply _)
}
table_description
mysql> desc dogs;
+-----------+--------------+-----+-----+----------+---------------+
| Field     | Type         | Null| Key | Default  | Extra         |
+-----------+--------------+-----+-----+----------+---------------+
| ID        | int(11)      | NO  | PRI | NULL     | auto_increment|
| NAME      | varchar(255) | YES |     | NULL     |               |
| COLOR     | varchar(255) | NO  |     | NULL     |               |
| BREED     | varchar(255) | YES |     | NULL     |               |
+-----------+--------------+-----+-----+----------+---------------+
  • 対応方法: DBのテーブル構造にCREATED_ATとUPDATED_ATを追加する

[NoSuchElementException: Invoker.first]

  • Queryを実行してDBからデータを取得した結果、Noneが返されたものを利用して後続の処理を行おうとしている。
  • 結果が取得できない可能性があるものはOption型として扱う
    • primary_key以外のキーで検索したもの
val dogs = TableQuery[DogTables]

val dog = dogs.filter { _.name === "Pochi" }.result.head
val owener = owners.filter { _.dog_id === dog.id }

  • 次のことを疑ってみる
    • headOption指定でデータを取得するところがheadになっていないか
    • left outer joinの右側のテーブルを絞込の条件にしていないか
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2