1. 程式人生 > >Play 2.6 訪問SQL資料庫

Play 2.6 訪問SQL資料庫

訪問SQL資料庫

Note: JDBC是一種阻塞操作。你不能直接在controller中執行JDBC查詢語句。詳見配置CustomExecutionContext章節

配置JDBC連線池

Play提供外掛來管理連線池,你可以根據需要配置多個數據庫。
為了啟動資料庫外掛,需要新增以下依賴:

libraryDependencies += javaJdbc

然後需要字application.conf中配置連線池。出於方便考慮預設JDBC資料來源必須叫做default

# Default database configuration
db.default.driver=org.h
2.Driver db.default.url="jdbc:h2:mem:play"

如果需要新增別的資料來源

# Orders database
db.orders.driver=org.h2.Driver
db.orders.url="jdbc:h2:mem:orders"

# Customers database
db.customers.driver=org.h2.Driver
db.customers.url="jdbc:h2:mem:customers"

H2

# Default database configuration using H2 database engine in an in-memory mode
db.default.driver=org.h2.Driver db.default.url="jdbc:h2:mem:play" # Default database configuration using H2 database engine in a persistent mode db.default.driver=org.h2.Driver db.default.url="jdbc:h2:/path/to/db-file"

SQLite

# Default database configuration using SQLite database engine
db.default
.driver=org.sqlite.JDBC db.default.url="jdbc:sqlite:/path/to/db-file"

PostgreSQL

# Default database configuration using PostgreSQL database engine
db.default.driver=org.postgresql.Driver
db.default.url="jdbc:postgresql://database.example.com/playdb"

MySql

# Default database configuration using MySQL database engine
# Connect to playdb as playdbuser
db.default.driver=com.mysql.jdbc.Driver
db.default.url="jdbc:mysql://localhost/playdb"
db.default.username=playdbuser
db.default.password="a strong password"

獲取JDBC資料來源

play.db包提供了獲取default資料的方法,主要是通過play.db.DataBase類

/*
 * Copyright (C) 2009-2017 Lightbend Inc. <https://www.lightbend.com>
 */
package javaguide.sql;

import javax.inject.*;

import play.db.*;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

@Singleton
class JavaApplicationDatabase {

    private Database db;
    private DatabaseExecutionContext executionContext;

    @Inject
    public JavaApplicationDatabase(Database db, DatabaseExecutionContext context) {
        this.db = db;
        this.executionContext = executionContext;
    }

   public CompletionStage<Integer> updateSomething() {
       return CompletableFuture.supplyAsync(() -> {
           return db.withConnection(connection -> {
               // do whatever you need with the db connection
               return 1;
           });
       }, executionContext);
   }
}

對於其他的資料庫

/*
 * Copyright (C) 2009-2017 Lightbend Inc. <https://www.lightbend.com>
 */
package javaguide.sql;

import javax.inject.Inject;
import javax.inject.Singleton;

import play.mvc.Controller;
import play.db.NamedDatabase;
import play.db.Database;

// inject "orders" database instead of "default"
@javax.inject.Singleton
class JavaNamedDatabase {
    private Database db;
    private DatabaseExecutionContext executionContext;

    @Inject
    public JavaNamedDatabase(@NamedDatabase("orders") Database db, DatabaseExecutionContext executionContext) {
        this.db = db;
        this.executionContext = executionContext;
    }

    // do whatever you need with the db using supplyAsync(() -> { ... }, executionContext);
}

配置CustomExecutionContext

在使用JDBC時必須要使用一個自定義的執行上下文,來保證Play的執行緒池專注與頁面處理以及自己的功能。可以使用Play的CustomExecutionContext來為JDBC操作配置一個執行上下文。可以在 JavaAsyncThreadPools來獲取更多細節。

由於執行緒池的大小對JDNC連線池有影響,在使用thred pool executor是,你需要一個可變大小的執行緒池來匹配連線池。根據[HikariCP]https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing)的建議,你需要將JDNC連線池的大小設定為物理核心數量的兩倍再加上磁碟的資料。如果你又一個4核CPU加一個硬碟,你需要將連線池的大小設為9

# db connections = ((physical_core_count * 2) + effective_spindle_count)
fixedConnectionPool = 9

database.dispatcher {
  executor = "thread-pool-executor"
  throughput = 1
  thread-pool-executor {
    fixed-pool-size = ${fixedConnectionPool}
  }
}

獲取JDBC連線

/*
 * Copyright (C) 2009-2017 Lightbend Inc. <https://www.lightbend.com>
 */
package javaguide.sql;

import java.sql.Connection;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import javax.inject.Inject;

import play.mvc.Controller;
import play.db.NamedDatabase;
import play.db.Database;

class JavaJdbcConnection {
    private Database db;
    private DatabaseExecutionContext executionContext;

    @Inject
    public JavaJdbcConnection(Database db, DatabaseExecutionContext executionContext) {
        this.db = db;
        this.executionContext = executionContext;
    }

    public CompletionStage<Void> updateSomething() {
        return CompletableFuture.runAsync(() -> {
            // get jdbc connection
            Connection connection = db.getConnection();

            // do whatever you need with the db connection
            return;
        }, executionContext);
    }

}

需要注意的是你要在程式碼的某個地方呼叫closs()方法。

通過JNDI暴露資料來源

有些庫需要通過JNDI來獲取Datasource引用,可以在application.conf中進行如下配置

db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
db.default.jndiName=DefaultDS

如何配置SQL log

不是所有的連線池都提供SQL語句的日誌。HikariCP建議我們使用資料庫提供商的log功能:

Log Statement Text / Slow Query Logging

就像是語句的快取,大多數資料庫提供商都支援SQL日誌。其中的一些還能提供慢查詢日誌。我們認為這是一個開發階段的特性。對於那些不支援的資料,jdbcdslog-exp是一個很好的選擇

出於以上的原因,Play使用jdbcdslog-exp來啟用SQL語句輸出的支援,可以通過logSql來配置

# Default database configuration using PostgreSQL database engine
db.default.driver=org.postgresql.Driver
db.default.url="jdbc:postgresql://database.example.com/playdb"
db.default.logSql=true

然後根據jdbcdslog-exp的手冊(該連線需要科學上網)來配置日誌級別。大體上,需要將root設定為info,然後確定jdbcdslog-exp輸出哪些內容(連線,語句和結果集)。下面是一個例子

<!--
  ~ Copyright (C) 2009-2017 Lightbend Inc. <https://www.lightbend.com>
  -->
<!-- The default logback configuration that Play uses if no other configuration is provided -->
<configuration>

  <conversionRule conversionWord="coloredLevel" converterClass="play.api.libs.logback.ColoredLevel" />

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
     <file>${application.home:-.}/logs/application.log</file>
     <encoder>
       <pattern>%date [%level] from %logger in %thread - %message%n%xException</pattern>
     </encoder>
  </appender>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%coloredLevel %logger{15} - %message%n%xException{10}</pattern>
    </encoder>
  </appender>

  <appender name="ASYNCFILE" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="FILE" />
  </appender>

  <appender name="ASYNCSTDOUT" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="STDOUT" />
  </appender>

  <logger name="play" level="INFO" />
  <logger name="application" level="DEBUG" />

  <!-- https://hibernate.atlassian.net/browse/HV-1323 -->
  <logger name="org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator" level="ERROR" />

  <logger name="org.jdbcdslog.ConnectionLogger" level="OFF"  /> <!-- Won' log connections -->
  <logger name="org.jdbcdslog.StatementLogger"  level="INFO" /> <!-- Will log all statements -->
  <logger name="org.jdbcdslog.ResultSetLogger"  level="OFF"  /> <!-- Won' log result sets -->

  <root level="WARN">
    <appender-ref ref="ASYNCFILE" />
    <appender-ref ref="ASYNCSTDOUT" />
  </root>

</configuration>
Note: 這些配置僅適用於開發環境。

配置JDBC驅動

如果使用MySql5,

libraryDependencies += "mysql" % "mysql-connector-java" % "5.1.41"

選擇和配置連線池

Play提供了兩種連線池,HikariCP和BoneCP。預設的是HikariCP,可以進行修改

play.db.pool=bonecp

所有可配置的選擇在 reference.conf(寫在application.conf中應該也是可以的)

測試

啟用Play資料庫提升

根據文件檢視Play資料庫提升的用途及用法。