1

Im a newbie scala and scalatra developer. Im trying to integrate c3p0 to get connection pooling in my application.

All the examples on the scalatra page is with squeryl etc, but I dont want orm and dsl. Do anyone have a ok examples with scalatra and c3p0.

Thanks all :)

2 Answers2

2

In addition to Steve's response, you can use a scala object for the collectionPoolDataSource instead of getting it from the request context.

For example, declaring:

object 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

}

and you can access to datasource without needing the request context:

def withConnection[T](op: (Connection) => T): T = { var con: Connection = null

try {
  con = DBDataSource.datasource.getConnection()
  op(con)
} finally {
  attemptClose(con)
}

}

aglavina
  • 375
  • 1
  • 5
  • 13
0

Note: None of the code below has been compiled or checked, i'm just writing it into my browser. apologies for the inevitable glitches.

So, I've never used Scalatra. But I wrote c3p0, and have used the Servlets API a lot. A quick look at scalatra's guides suggests something like this would work:

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
      }
    }
  }
}

To get access to the DataSource from a ServletRequest object, you'd call...

request.getServletContext().getAttribute( "appDataSource" ).asInstanceOf[DataSource]

You might want to use your Scala-fu to pimp ServletRequest and make access to the Connection pool easier and prettier. For example, you could write...

implicit class ConnectionPoolRequest( request : ServletRequest ) {
   def connectionPool : DataSource = request.getServletContext().getAttribute( "appDataSource" ).asInstanceOf[DataSource]
}

Put this in a package object or some object you import into your code, and while you handle requests you should be able to write stuff like...

val conn = request.connectionPool.getConnection();
// do stuff
conn.close()

However, the above is crappy, leak-prone code, because the close() isn't in a finally and will be skipped by an Exception. In Java7-style, you'd use try-with-resources to avoid this. In Scala, the naive way is to do this:

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
  }
}

However, a better way in Scala is to define utility methods like this:

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
    }
  }
}

Then you can just write stuff like...

withConnection( request.connectionPool ) { conn =>
  // do stuff with the Connection
  // don't worry about cleaning up, that's taken care of for you
}

To really keep JDBC clean in scala, consider writing analogous methods like withStatement and withResultSet as well, so you can do

withConnection( request.connectionPool ) { conn =>
  withStatement( conn ) { stmt =>
    withResultSet( stmt.executeQuery("SELECT * FROM spacemen") ) { rs =>
      // read stuff from ResultSet
    }
  } 
}
Steve Waldman
  • 13,689
  • 1
  • 35
  • 45
  • Thats an awesome answer. Didnt expect that :) Thank you so much. Do you have any experience with scalikeJDBC? Or any other libs? – Thomas Torp Dec 25 '13 at 13:54
  • no, i don't, so far i pretty much use straight jdbc with the borrow pattern above. but that doesn't reflect any kind of judgement. i've just been too lazy to check out the various scala sql libs! – Steve Waldman Dec 26 '13 at 05:03
  • One more thing. Why do we close the connection, shouldnt c3p0 handle that? – Thomas Torp Dec 28 '13 at 17:27
  • no! c3p0 implements JDBC3+ "transparent Connection pooling". what that means is that clients acquire and use Connections in precisely the same way that they would if those Connections were not pooled. there is no special API for pooling; it's an implementation detail that enhances performance. in practice, what this means is when you "close" a Connection, c3p0 (or any other JDBC connection pool) intercepts this and checks the Connection back into the pool, rather than close()ing it. if you fail to close(), Connections will never be returned to the pool, which will quickly become exhausted. – Steve Waldman Dec 29 '13 at 05:59
  • Ahh thanks for the information :) I think I understand it right now :) – Thomas Torp Dec 29 '13 at 12:35
  • Hi again @steve-waldman . Im having a problem with connections provided by c3p0 is closed the second time it should be used. If I dont close the connection after use its ok. Here are some info: Got : com.mchange.v2.c3p0.impl.NewProxyConnection@524a95ac Got : com.mchange.v2.c3p0.impl.NewProxyConnection@524a95ac The two connections are the same. I have implemented withConnection, withStatement and withResultSet. The statement and resultset is getting closed. – Thomas Torp Jan 03 '14 at 13:01
  • hi: i'll need more information to help. mb show your implementations of the borrow functions and ideally some code that uses them, please? (i gather you are seeing c3p0 warnings about twice-closed Connections, but are finding yourself surprised that a Connection that you expect to be open is closed upon use.) if that needs a new question, include the c3p0 tag and i'll see it. – Steve Waldman Jan 04 '14 at 03:16
  • I found the problem. I thought I passed a function as a parameter, but it was evaluated, so I just got one connection for the hole application. My bad, thanks for all the help :) – Thomas Torp Jan 04 '14 at 12:57