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


## この記事の目的
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 <computed>


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

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

```scala: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 _)
}
```


```sql: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のテーブル定義にない場合に発生します。

```scala: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 _)
}
```

```sql: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以外のキーで検索したもの

```scala
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の右側のテーブルを絞込の条件にしていないか