【发布时间】:2013-12-22 13:11:57
【问题描述】:
我是新手 scala 和 scalatra 开发人员。我正在尝试集成 c3p0 以在我的应用程序中获取连接池。
scalatra 页面上的所有示例都使用 squeryl 等,但我不想要 orm 和 dsl。有没有人有 scalatra 和 c3p0 的好例子。
谢谢大家:)
【问题讨论】:
我是新手 scala 和 scalatra 开发人员。我正在尝试集成 c3p0 以在我的应用程序中获取连接池。
scalatra 页面上的所有示例都使用 squeryl 等,但我不想要 orm 和 dsl。有没有人有 scalatra 和 c3p0 的好例子。
谢谢大家:)
【问题讨论】:
除了 Steve 的响应之外,您还可以对 collectionPoolDataSource 使用 scala 对象,而不是从请求上下文中获取它。
例如,声明:
对象 DBDataSource {
private val ds = new ComboPooledDataSource
ds.setDriverClass("org.mariadb.jdbc.Driver")
ds.setUser(dbUser)
ds.setPassword(dbPassword)
ds.setDebugUnreturnedConnectionStackTraces(true)
ds.setUnreturnedConnectionTimeout(7200)
ds.setMaxPoolSize(100)
ds.setMaxStatements(0)
ds.setCheckoutTimeout(60000)
ds.setMinPoolSize(5)
ds.setTestConnectionOnCheckin(true)
ds.setTestConnectionOnCheckout(false)
ds.setBreakAfterAcquireFailure(false)
ds.setIdleConnectionTestPeriod(50)
ds.setMaxIdleTimeExcessConnections(240)
ds.setAcquireIncrement(1)
ds.setAcquireRetryAttempts(5)
ds.setJdbcUrl(dbUrl)
ds.setPreferredTestQuery("SELECT 1")
def datasource = ds
}
你可以在不需要请求上下文的情况下访问数据源:
def withConnection[T](op: (Connection) => T): T = { var con: Connection = null
try {
con = DBDataSource.datasource.getConnection()
op(con)
} finally {
attemptClose(con)
}
}
【讨论】:
注意:下面的代码都没有被编译或检查,我只是将它写到我的浏览器中。为不可避免的故障道歉。
所以,我从未使用过 Scalatra。但是我写了c3p0,并且经常使用Servlets API。快速浏览一下 scalatra 的指南表明这样的事情会起作用:
import org.scalatra._
import com.mchange.v2.c3p0._
import javax.sql.DataSource
import javax.servlet.ServletContext
class ScalatraBootstrap extends LifeCycle {
override def init(context: ServletContext) {
val cpds = new ConnectionPoolDataSource();
// perform any c3p0 config operations you might
// want here, or better yet, externalize all of
// that into a c3p0.properties, c3p0-config.xml,
// or (c3p0 version 0.9.5 only) application.conf
context.setAttribute( "appDataSource", cpds );
}
override def destroy(context: ServletContext) {
val cpds = context.getAttribute( "appDataSource" );
if ( cpds != null ) {
try {
cpds.close()
} catch {
case e : Exception => e.printStackTrace(); //consider better logging than this
}
}
}
}
要从 ServletRequest 对象访问 DataSource,您需要调用...
request.getServletContext().getAttribute( "appDataSource" ).asInstanceOf[DataSource]
您可能希望使用您的 Scala-fu 来拉皮条 ServletRequest 并使对连接池的访问更容易和更漂亮。例如,你可以写...
implicit class ConnectionPoolRequest( request : ServletRequest ) {
def connectionPool : DataSource = request.getServletContext().getAttribute( "appDataSource" ).asInstanceOf[DataSource]
}
将它放在一个包对象或您导入代码中的某个对象中,当您处理请求时,您应该能够编写类似...
val conn = request.connectionPool.getConnection();
// do stuff
conn.close()
但是,上面的代码很糟糕,容易泄漏,因为 close() 不在 finally 中,并且会被异常跳过。在 Java7 风格中,您将使用 try-with-resources 来避免这种情况。在 Scala 中,天真的方法是这样做:
var conn = null;
try {
conn = request.connectionPool.getConnection();
// do stuff
} finally {
try { if ( conn != null ) conn.close() } catch {
case e : Exception => e.printStackTrace() // better logging would be nice
}
}
然而,在 Scala 中一个更好的方法是定义这样的实用方法:
def withConnection[T]( ds : DataSource )( op : (Connection) => T) : T = {
var con : Connection = null;
try {
con = ds.getConnection();
op(con);
} finally {
attemptClose( con );
}
}
def attemptClose( con : Connection ) {
if ( con != null ) {
try { if ( conn != null ) conn.close() } catch {
case e : Exception => e.printStackTrace() // better logging would be nice
}
}
}
然后你就可以写...
withConnection( request.connectionPool ) { conn =>
// do stuff with the Connection
// don't worry about cleaning up, that's taken care of for you
}
要真正保持 Scala 中的 JDBC 干净,请考虑编写类似的方法,例如 withStatement 和 withResultSet,这样您就可以做到
withConnection( request.connectionPool ) { conn =>
withStatement( conn ) { stmt =>
withResultSet( stmt.executeQuery("SELECT * FROM spacemen") ) { rs =>
// read stuff from ResultSet
}
}
}
【讨论】: