LoginSignup
1
0

More than 5 years have passed since last update.

ScalaでJDBC+HikariCPを直接使ってみる

Last updated at Posted at 2019-04-03

はじめに

Javaで実装したことある方には何のこともない話しなんですが・・・。

今までRDBに対する処理はslickに頼ってたんですよ。
でもトランザクション周りの処理をFrameworkに依存しないように切り離せないかなぁなんて考えはじめたんですが、slick内のDBIOActionから上手いこと切り離せなかったんですよ。

そんなこんなでもうJavaのJDBCのDriverとHikariCPを直接使っちゃおうかとなったわけです。

ScalikeJDBC使えばいい? ・・・知ってるけど、今回は置いてきます。

環境

  • scala: 2.12.8
  • sbt: 1.2.8
  • mysql-connector-java: 6.0.6
  • HikariCP: 3.3.1

※JDBCとHikariCP自体の細かな解説はしません

環境構築

取りあえず手元にあるのがMySQLなのでそれで進めていきます。

Table

簡単なuserテーブルを作成します。

CREATE TABLE `user` (
    `id` BIGINT NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(50) DEFAULT NULL.
    PRIMARY KEY (`id`)
);

SBT

sbtにデータベース周りの依存を追加。

build.sbt
libraryDependencies ++= Seq(
  "mysql"                      % "mysql-connector-java"     % "6.0.6",
  "com.zaxxer"                 % "HikariCP"                 % "3.3.1"
)

HikariCP

まずはHikariCPを使えるようにします。この辺を参考にHikariConfigを生成します。

val hikariConfig = new HikariConfig()

hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver")
hikariConfig.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/mms?useSSL=false&")
hikariConfig.addDataSourceProperty("user", "root")
hikariConfig.addDataSourceProperty("password", "1234")
hikariConfig.addDataSourceProperty("cachePrepStmts", "true")
hikariConfig.addDataSourceProperty("prepStmtCacheSize", "250")
hikariConfig.addDataSourceProperty("prepStmtCacheSqlLimit", "2048")
hikariConfig.addDataSourceProperty("useServerPrepStmts", "true")
hikariConfig.addDataSourceProperty("useLocalSessionState", "true")
hikariConfig.addDataSourceProperty("rewriteBatchedStatements", "true")
hikariConfig.addDataSourceProperty("cacheResultSetMetadata", "true")
hikariConfig.addDataSourceProperty("cacheServerConfiguration", "true")
hikariConfig.addDataSourceProperty("elideSetAutoCommits", "true")
hikariConfig.addDataSourceProperty("maintainTimeStats", "true")

バリッバリにハードコーディングしてますが、実際使うときはconfigライブラリ等を使えばいいでしょう。

んでHikariDataSourceを生成します。

val hikariDataSource = new HikariDataSource(hikariConfig)

~~~JDBC処理~~~

hikariDataSource.close()

元々がJavaなのでHikariDataSoruce明示的に閉じてやる必要があります。忘れるとコネクションプールがゾンビにでもなるのかも?

JDBC

上記、HikariDataSourceからjava.sql.Connectionを取得してJDBCを動かします。

・・・とその前に、クエリの結果をjava.sql.ResultSetで受けるのですが、行に対する走査がwhileを使ったループでちょっとscala的に残念な感じになるので拡張してmapを関数を作っておきます。

implicit class RichResultSet(self: ResultSet) {
  def map[T](f: ResultSet => T): Seq[T] = {
    val buf = ListBuffer.empty[T]

    while (self.next()) {
      buf += f(self)
    }

    buf
  }
}

それでは環境構築で作成したuserテーブルに対してトランザクション付きでSELECTINSERTを実装してみましょう。

val hikariDataSource = new HikariDataSource(hikariConfig)

val connection = hikariDataSource.getConnection()

try {
  connection.setAutoCommit(false)

  val selectStmt = connection.prepareStatement("SELECT * FROM user")
  val selectResultSet = selectStmt.executeQuery()
  selectResultSet.map(row => (row.getLong("id"), 
  row.getString("name"))).foreach(println)
  selectResultSet.close()

  val insertStmt = connection.prepareStatement("INSERT INTO user(name) VALUES(?)")
  insertStmt.setString(1, "TEST")
  val affectedRows = insertStmt.executeUpdate()
  println(affectedRows)

  connection.commit()
} catch {
  case _ =>
    connection.rollback()
}

connection.close()

hikariDataSource.close()

・・・雑。

java.sql.ResultSetjava.sql.ConnectionHikariDataSource同様に明示的なクローズ処理が必要です。もし本当に使う場合はLoanPattern等を利用するのが無難でしょう。

おわりに

やっぱりslickScalikeJDBCって便利なんだぁとしみじみ感じてみたり。

ただ、課題だったトランザクションの分離については、実際に書いてみたことで
十分に実現可能なこと(LoanPatternで追い出すとか、implicitパラメータで持つとか)が何となく掴めたのでよしとする。

1
0
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
1
0