Scala數(shù)據(jù)庫(kù)連接池的簡(jiǎn)單實(shí)現(xiàn)
在使用JDBC的時(shí)候,數(shù)據(jù)庫(kù)據(jù)連接是非常寶貴的資源。為了復(fù)用這些資源,可以將連接保存在一個(gè)隊(duì)列中。當(dāng)需要的時(shí)候可以從隊(duì)列中取出未使用的連接。如果沒(méi)有可用連接,則可以在一定時(shí)間內(nèi)等待,直到隊(duì)列中有可用的連接,否則將拋出異常。
以下是DataSoucre的代碼,DataSoucre負(fù)責(zé)對(duì)連接的管理以及分發(fā),同時(shí)設(shè)置隊(duì)列的大小,等待時(shí)間,連接的賬號(hào)、密碼等。
核心方法為getConenction()方法。且實(shí)現(xiàn)AutoCloseable接口,以便后面可以使用using方法自動(dòng)關(guān)閉資源。隊(duì)列中的連接為封裝了conenction的DbConnection類(lèi)。
package pool
import scala.util.control.Breaks._
import scala.collection.mutable.ArrayBuffer
import java.{util => ju}
import scala.collection.mutable.Buffer
import scala.util.control.Breaks
class DataSource(
val driverName: String,
val url: String,
val user: String,
val password: String,
val minSize: Integer = 1,
val maxSize: Integer = 10,
val keepAliveTimeout: Long = 1000
) extends AutoCloseable {
if (minSize < 0 || minSize > maxSize || keepAliveTimeout < 0) {
throw new IllegalArgumentException("These arguments are Illegal")
}
Class.forName(driverName)
private val pool: Buffer[DbConnection] = ArrayBuffer[DbConnection]()
private val lock: ju.concurrent.locks.Lock = new ju.concurrent.locks.ReentrantLock(true)
for (i <- 0 until minSize) {
pool += new DbConnection(url, user, password)
}
def getConenction(): DbConnection = {
val starEntry = System.currentTimeMillis()
Breaks.breakable {
while (true) {
lock.lock()
try {
for (con <- pool) {
if (!con.used) {
con.used = true
return con;
}
}
if (pool.size < maxSize) {
var con = new DbConnection(url, user, password) { used = true }
pool.append(con)
return con
}
} finally {
lock.unlock()
}
if (System.currentTimeMillis() - starEntry > keepAliveTimeout) {
break()
}
}
}
throw new IllegalArgumentException("Connection Pool is empty")
}
def close(): Unit = {
lock.lock()
try {
if (pool != null) {
pool.foreach(t => t.innerConnection.close())
pool.clear()
}
} finally {
lock.unlock()
}
}
}以下是Dbconnection類(lèi),該類(lèi)提供了三個(gè)方法且實(shí)現(xiàn)了AutoCloseable接口
BeginTransaction:開(kāi)啟事務(wù),并返回封裝了的DbTransaction類(lèi)
close:將連接釋放
CreateCommand:創(chuàng)建DbCommand類(lèi),該類(lèi)是負(fù)責(zé)操作連接的類(lèi),比如提交sql,讀取數(shù)據(jù)等
package pool
import java.sql.Connection
import java.sql.DriverAction
import java.sql.DriverManager
class DbConnection(
val url: String,
val user: String,
val password: String
) extends AutoCloseable {
private[pool] var used: Boolean = false
private[pool] val innerConnection: Connection = DriverManager.getConnection(url, user, password)
def close(): Unit = {
if (used) {
used = false
}
}
def BeginTransaction(isolationLevel: Int = IsolationLevel.TRANSACTION_READ_COMMITTED): DbTransaction = {
if (innerConnection.getAutoCommit()) {
innerConnection.setAutoCommit(false)
}
innerConnection.setTransactionIsolation(isolationLevel)
new DbTransaction(this)
}
def CreateCommand(): DbCommand = {
new DbCommand(this)
}
}以下是DbCommand類(lèi)的代碼,該類(lèi)負(fù)責(zé)操作數(shù)據(jù)庫(kù)。如ExecuteResultSet,ExecuteScalar等。
ExecuteScalar:查詢(xún)數(shù)據(jù)庫(kù)并返回第一行第一個(gè)值的方法。
ExecuteResultSet:該方法有兩個(gè)重載方法。
參數(shù)為callBack: ResultSet => Unit的方法,提供了一個(gè)回調(diào)函數(shù),解析數(shù)據(jù)的操作可以在回調(diào)中實(shí)現(xiàn)。
無(wú)參的版本則通過(guò)反射直接將ResultSet通過(guò)字段位置映射,轉(zhuǎn)換成你需要的類(lèi)型。
package pool
import java.sql.CallableStatement
import java.sql.ResultSet
import java.sql.SQLType
import java.sql.Statement
import java.sql.Types
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.Buffer
import scala.language.implicitConversions
import scala.reflect.ClassTag
import scala.reflect.runtime.{universe => ru}
import Dispose.using
import java.{util => ju}
class DbCommand(val connection: DbConnection, var commandText: String = null, val queryTimeout: Integer = 30) extends AutoCloseable {
if (queryTimeout < 0) {
throw new IllegalArgumentException(s"timeout (${queryTimeout}) value must be greater than 0.")
}
val Parameters: Buffer[DbParameter] = ArrayBuffer[DbParameter]()
private val mirror = ru.runtimeMirror(getClass().getClassLoader())
private var statement: CallableStatement = null
/** @author:qingchuan
*
* @return
*/
def ExecuteScalar(): Any = {
var obj: Any = None
ExecuteResultSet(t => {
if (t.next()) {
if (t.getMetaData().getColumnCount() > 0)
obj = t.getObject(1)
}
})
obj
}
/** @author
* qingchuan
* @version 1.0
*
* @param callBack
*/
def ExecuteResultSet(callBack: ResultSet => Unit): Unit = {
if (callBack == null) throw new IllegalArgumentException("The value of parameter callback is null.")
statement = connection.innerConnection.prepareCall(commandText)
statement.setQueryTimeout(queryTimeout)
addParatemetrs()
using(statement.executeQuery()) { t =>
callBack(t)
if (!t.isClosed())
getOutParameterValue()
}
}
def ExecuteResultSet[T: ru.TypeTag](): ArrayBuffer[T] = {
val classSymbol = mirror.symbolOf[T].asClass
val classMirror = mirror.reflectClass(classSymbol)
val consMethodMirror = classMirror.reflectConstructor(classSymbol.primaryConstructor.asMethod)
val fields = ru.typeOf[T].decls.filter(t => t.asTerm.isGetter && t.isPublic).map(t => t.asTerm)
val result = new ArrayBuffer[T]()
ExecuteResultSet(t => {
while (t.next()) {
var i = 1
val values: Buffer[Any] = ArrayBuffer()
for (f <- fields) {
values += t.getObject(i)
i += 1
}
result += consMethodMirror.apply(values: _*).asInstanceOf[T]
}
})
result
}
def ExecuteBatch[T: ru.TypeTag: ClassTag](values: List[T]): Int = {
statement = connection.innerConnection.prepareCall(commandText)
var trans: DbTransaction = null
val fields = ru.typeOf[T].decls.filter(t => t.asTerm.isGetter && t.isPublic).map(t => t.asTerm)
for (t <- values) {
var i = 1
val filedMirror = mirror.reflect(t)
for (f <- fields) {
val instance = filedMirror.reflectField(f)
statement.setObject(i, instance.get)
i += 1
}
statement.addBatch()
}
try {
trans = connection.BeginTransaction()
val obj = statement.executeBatch()
trans.Commit()
statement.clearBatch()
obj.sum
} catch {
case e: Exception => {
if (trans != null)
trans.RollBack()
throw e
}
}
}
def ExecuteNoneQuery(): Integer = {
statement = connection.innerConnection.prepareCall(commandText)
statement.setQueryTimeout(queryTimeout)
addParatemetrs()
val obj = statement.executeUpdate()
getOutParameterValue()
obj
}
def CreateParameter(): DbParameter = {
new DbParameter();
}
private def getOutParameterValue(): Unit = {
for (i <- 1 to Parameters.size) {
val parameter: DbParameter = Parameters(i - 1);
if (parameter.parameterDirection == ParameterDirection.Output || parameter.parameterDirection == ParameterDirection.InputOutput) {
parameter.value = statement.getObject(i);
}
}
}
private def addParatemetrs(): Unit = {
statement.clearParameters()
for (i <- 1 to Parameters.size) {
val p = Parameters(i - 1);
if (p.parameterDirection == ParameterDirection.Input || p.parameterDirection == ParameterDirection.InputOutput) {
statement.setObject(i, p.value)
}
if (p.parameterDirection == ParameterDirection.Output || p.parameterDirection == ParameterDirection.InputOutput) {
statement.registerOutParameter(p.parameterName, p.sqlType, p.scale)
}
}
}
def close() {
if (statement != null) {
statement.close()
}
}
}
case class DbParameter(
var parameterName: String = null,
var value: Any = null,
var parameterDirection: Integer = ParameterDirection.Input,
var scale: Integer = 0,
var sqlType: Integer = null
) {}
object ParameterDirection {
val Input = 1
val InputOutput = 2
val Output = 3
}以下代碼是DbTransaction,該類(lèi)提供了事務(wù)的操作如提交、回滾。
package pool
class DbTransaction(private val connection: DbConnection) {
def Commit(): Unit = {
connection.innerConnection.commit()
if (!connection.innerConnection.getAutoCommit()) {
connection.innerConnection.setAutoCommit(true);
}
}
def RollBack(): Unit = {
connection.innerConnection.rollback()
if (!connection.innerConnection.getAutoCommit()) {
connection.innerConnection.setAutoCommit(true)
}
}
def getConnection(): DbConnection = {
connection
}
def getTransactionIsolation(): Int = {
connection.innerConnection.getTransactionIsolation()
}
}
object IsolationLevel {
val TRANSACTION_NONE = 0
val TRANSACTION_READ_UNCOMMITTED = 1;
val TRANSACTION_READ_COMMITTED = 2;
val TRANSACTION_REPEATABLE_READ = 4;
val TRANSACTION_SERIALIZABLE = 8;
}最后是using的方法。通過(guò)柯里化以及Try-catch-finally的方式 自動(dòng)關(guān)閉實(shí)現(xiàn)了AutoCloseable接口的資源。
package pool
object Dispose {
def using[T <: AutoCloseable](cls: T)(op: T => Unit): Unit = {
try {
op(cls)
} catch {
case e: Exception => throw e
} finally {
cls.close()
}
}
}
以下是客戶(hù)端調(diào)用,代碼模擬了15個(gè)線程并發(fā)訪問(wèn)數(shù)據(jù)庫(kù),連接池最多3個(gè)資源,從而說(shuō)明連接池是可以復(fù)用這些連接的。
import pool.DataSource
import pool.DbCommand
import pool.DbParameter
import pool.DbTransaction
import pool.Dispose.using
import pool.IsolationLevel
import pool.ParameterDirection
import java.sql.Date
import java.sql.ResultSet
import java.sql.Types
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import javax.xml.crypto.Data
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.Buffer
import scala.language.experimental.macros
import scala.reflect.ClassTag
import scala.reflect.runtime.{universe => ru}
import com.nimbusds.oauth2.sdk.util.date.SimpleDate
import java.text.SimpleDateFormat
object App {
def main(args: Array[String]): Unit = {
val pool = new DataSource(
"com.microsoft.sqlserver.jdbc.SQLServerDriver",
"jdbc:sqlserver://localhost:1433;databaseName=HighwaveDW;trustServerCertificate=true",
"賬號(hào)",
"密碼",
minSize = 1,
maxSize = 3,
keepAliveTimeout = 3000
)
val formatter: SimpleDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
for (i <- 1 to 15) {
val thread: Thread = new Thread(() => {
val date = new Date(System.currentTimeMillis())
using(pool.getConenction()) { con =>
{
using(new DbCommand(con)) { cmd =>
{
cmd.commandText = "{call p_get_out(?,?)}"
val p1 = new DbParameter("@id", i)
val p2 = new DbParameter(parameterName = "@msg", parameterDirection = ParameterDirection.Output, sqlType = Types.VARCHAR, scale = 20)
cmd.Parameters.append(p1)
cmd.Parameters.append(p2)
val result = cmd.ExecuteScalar()
println(s"result=${result},output=${p2.value},parameter=${i}")
}
}
}
}
})
thread.start()
}
}
}開(kāi)發(fā)環(huán)境VsCode,SQL Server數(shù)據(jù)庫(kù)。以下是引用的第三方庫(kù)。
version := "1.0" libraryDependencies += "com.microsoft.sqlserver" % "mssql-jdbc" % "11.2.0.jre8" libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.13.8"
以下是執(zhí)行結(jié)果。

到此這篇關(guān)于Scala數(shù)據(jù)庫(kù)連接池的簡(jiǎn)單實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Scala數(shù)據(jù)庫(kù)連接池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- IDEA中scala生成變量后自動(dòng)顯示變量類(lèi)型問(wèn)題
- Idea中添加Maven項(xiàng)目支持scala的詳細(xì)步驟
- Java和Scala集合間的相互轉(zhuǎn)換方式
- C# ExecuteScalar()方法案例講解
- idea中如何創(chuàng)建scala項(xiàng)目
- Scala函數(shù)式編程專(zhuān)題--scala集合和函數(shù)
- Scala函數(shù)式編程專(zhuān)題--scala基礎(chǔ)語(yǔ)法介紹
- Scala入門(mén)教程詳解
- scala中常用特殊符號(hào)詳解
- Scala基礎(chǔ)語(yǔ)法總結(jié)
相關(guān)文章
Java去掉小數(shù)點(diǎn)后面無(wú)效0的方案與建議
當(dāng)前小數(shù)點(diǎn)后面的位數(shù)過(guò)多的時(shí)候,多余的0沒(méi)有實(shí)際意義,下面這篇文章主要給大家介紹了關(guān)于Java去掉小數(shù)點(diǎn)后面無(wú)效0的方案與建議,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07
java RocketMQ快速入門(mén)基礎(chǔ)知識(shí)
這篇文章主要介紹了java RocketMQ快速入門(mén)基礎(chǔ)知識(shí),所以RocketMQ是站在巨人的肩膀上(kafka),又對(duì)其進(jìn)行了優(yōu)化讓其更滿足互聯(lián)網(wǎng)公司的特點(diǎn)。它是純Java開(kāi)發(fā),具有高吞吐量、高可用性、適合大規(guī)模分布式系統(tǒng)應(yīng)用的特點(diǎn)。,需要的朋友可以參考下2019-06-06
idea中一鍵自動(dòng)生成序列化serialVersionUID方式
這篇文章主要介紹了idea中一鍵自動(dòng)生成序列化serialVersionUID方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
手把手教你在eclipse創(chuàng)建第一個(gè)java?web項(xiàng)目并運(yùn)行
Eclipse是用來(lái)做開(kāi)發(fā)的自由集成開(kāi)發(fā)環(huán)境,這也是很多java程序員會(huì)使用的開(kāi)發(fā)環(huán)境,所以可以使用eclipse創(chuàng)建javaweb項(xiàng)目,下面這篇文章主要給大家介紹了關(guān)于如何在eclipse創(chuàng)建第一個(gè)java?web項(xiàng)目并運(yùn)行的相關(guān)資料,需要的朋友可以參考下2023-02-02
利用logback 設(shè)置不同包下的日志級(jí)別
這篇文章主要介紹了利用logback 設(shè)置不同包下的日志級(jí)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
修改Android應(yīng)用的樣式的一些關(guān)鍵點(diǎn)解析
這篇文章主要介紹了修改Android應(yīng)用的樣式的一些關(guān)鍵點(diǎn),即對(duì)影響外觀的theme跟style的相關(guān)修改,需要的朋友可以參考下2015-12-12
解決IDEA報(bào)錯(cuò)java無(wú)效的目標(biāo)發(fā)行版:22
在使用IDEA編譯項(xiàng)目時(shí),可能會(huì)遇到JDK版本不一致的錯(cuò)誤,這篇文章主要介紹了解決IDEA報(bào)錯(cuò)java無(wú)效的目標(biāo)發(fā)行版:22的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-10-10
IDEA搭建SpringBoot多模塊聚合工程過(guò)程詳解(多模塊聚合工程)
這篇文章主要是介紹一下,如何在IDEA開(kāi)發(fā)工具下,搭建一個(gè)基于SpringBoot的多模塊聚合工程項(xiàng)目,本篇文章,將項(xiàng)目模塊細(xì)分為幾個(gè)子工程模塊,在文中給大家詳細(xì)介紹過(guò),對(duì)IDEA搭建SpringBoot多模塊聚合工程感興趣的朋友一起看看吧2022-04-04

