【问题标题】:Scalatra with c3p0 without any extra libs带有 c3p0 的 Scalatra,没有任何额外的库
【发布时间】:2013-12-22 13:11:57
【问题描述】:

我是新手 scala 和 scalatra 开发人员。我正在尝试集成 c3p0 以在我的应用程序中获取连接池。

scalatra 页面上的所有示例都使用 squeryl 等,但我不想要 orm 和 dsl。有没有人有 scalatra 和 c3p0 的好例子。

谢谢大家:)

【问题讨论】:

    标签: c3p0 scalatra


    【解决方案1】:

    除了 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)
    }
    

    }

    【讨论】:

      【解决方案2】:

      注意:下面的代码都没有被编译或检查,我只是将它写到我的浏览器中。为不可避免的故障道歉。

      所以,我从未使用过 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 干净,请考虑编写类似的方法,例如 withStatementwithResultSet,这样您就可以做到

      withConnection( request.connectionPool ) { conn =>
        withStatement( conn ) { stmt =>
          withResultSet( stmt.executeQuery("SELECT * FROM spacemen") ) { rs =>
            // read stuff from ResultSet
          }
        } 
      }
      

      【讨论】:

      • 这是一个很棒的答案。没想到:) 非常感谢。你有使用 scalikeJDBC 的经验吗?或任何其他库?
      • 不,我没有,到目前为止,我几乎使用直接 jdbc 和上面的借用模式。但这并不反映任何判断。我只是懒得查看各种 scala sql 库!
      • 还有一件事。为什么我们要关闭连接,c3p0 不应该处理吗?
      • 不! c3p0 实现了 JDBC3+“透明连接池”。这意味着客户端获取和使用连接的方式与未将这些连接合并时完全相同。没有特殊的池化 API;这是一个提高性能的实现细节。实际上,这意味着当您“关闭”一个连接时,c3p0(或任何其他 JDBC 连接池)会拦截它并将连接检查回池中,而不是关闭它。如果你没有关闭(),连接将永远不会返回到池中,很快就会耗尽。
      • 啊,谢谢你的信息 :) 我想我现在明白了 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-01
      • 2018-06-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多