All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rcumflex.circumflex-orm.3.0-RC1.source-code.config.scala Maven / Gradle / Ivy

The newest version!
package pro.savant.circumflex
package orm

import core._
import javax.sql.DataSource
import javax.naming.InitialContext
import java.util.Date
import java.sql.{Timestamp, Connection, PreparedStatement}
import com.mchange.v2.c3p0.{DataSources, ComboPooledDataSource}
import scala.xml._

/*!# ORM Configuration Objects

Circumflex ORM needs to know a little about your environment to operate.
Following objects configure different aspects of Circumflex ORM:

  * _connection provider_ is used to acquire JDBC connections, can be
    overriden using the `orm.connectionProvider` configuration parameter;

  * _type converter_ handles convertions between JDBC and Scala data types,
    can be overriden using the `orm.typeConverter` configuration parameter;

  * _dialect_ handles all SQL rendering throughout the framework,
    can be overriden using the `orm.dialect` configuration parameter;

  * _transaction manager_ is responsible for allocating current transactions
    for execution contexts.
*/

trait ORMConfiguration {

  // Configuration identification

  def name: String
  def prefix(sym: String) = if (name == "") "" else name + sym

  // Database connectivity parameters

  lazy val url = cx.getString("orm.connection.url")
      .getOrElse(throw new ORMException(
    "Missing mandatory configuration parameter 'orm.connection.url'."))

  lazy val username = cx.getString("orm.connection.username")
      .getOrElse(throw new ORMException(
    "Missing mandatory configuration parameter 'orm.connection.username'."))

  lazy val password = cx.getString("orm.connection.password")
      .getOrElse(throw new ORMException(
    "Missing mandatory configuration parameter 'orm.connection.password'."))

  lazy val dialect = cx.instantiate[Dialect]("orm.dialect", url match {
    case u if (u.startsWith("jdbc:postgresql:")) => new PostgreSQLDialect
    case u if (u.startsWith("jdbc:mysql:")) => new MySQLDialect
    case u if (u.startsWith("jdbc:oracle:")) => new OracleDialect
    case u if (u.startsWith("jdbc:h2:")) => new H2Dialect
    case u if (u.startsWith("jdbc:sqlserver:")) => new MSSQLDialect
    case u if (u.startsWith("jdbc:db2:")) => new DB2Dialect
    case _ => new Dialect
  })

  lazy val driver = cx.get("orm.connection.driver") match {
    case Some(s: String) => s
    case _ => dialect.driverClass
  }

  lazy val isolation: Int = cx.get("orm.connection.isolation") match {
    case Some("none") => Connection.TRANSACTION_NONE
    case Some("read_uncommitted") => Connection.TRANSACTION_READ_UNCOMMITTED
    case Some("read_committed") => Connection.TRANSACTION_READ_COMMITTED
    case Some("repeatable_read") => Connection.TRANSACTION_REPEATABLE_READ
    case Some("serializable") => Connection.TRANSACTION_SERIALIZABLE
    case _ =>
      ORM_LOG.info("Using READ COMMITTED isolation, " +
          "override 'orm.connection.isolation' if necesssary.")
      Connection.TRANSACTION_READ_COMMITTED
  }

  // Configuration objects

  lazy val typeConverter: TypeConverter =
    cx.instantiate[TypeConverter](
      "orm.typeConverter",
      new TypeConverter)

  lazy val transactionManager: TransactionManager =
    cx.instantiate[TransactionManager](
      "orm.transactionManager",
      new DefaultTransactionManager)

  lazy val defaultSchema: Schema =
    new Schema(cx.get("orm.defaultSchema").map(_.toString).getOrElse("public"))

  lazy val statisticsManager: StatisticsManager =
    cx.instantiate[StatisticsManager](
      "orm.statisticsManager",
      new StatisticsManager)

  lazy val connectionProvider: ConnectionProvider =
    cx.instantiate[ConnectionProvider](
      "orm.connectionProvider",
      new SimpleConnectionProvider(driver, url, username, password, isolation))

  // Override to forbid `executeUpdate` statements on configuration-level
  def readOnly = false
}

class SimpleORMConfiguration(val name: String) extends ORMConfiguration

/*!## Connection Provider

The `ConnectionProvider` is a simple trait responsible for acquiring JDBC
connections throughout the application.
*/
trait ConnectionProvider {

  def openConnection(): Connection

  def close()

}

/*! Circumflex ORM provides default `ConnectionProvider` implementation.
It behaves as follows:

  * if `orm.connection.datasource` is set, use it to acquire data source
    from JNDI;

  * if `orm.connection.datasource` is missing, construct a connection
    pool using [c3p0](http://www.mchange.com/projects/c3p0)
    and following configuration parameters:

     * `orm.connection.url`
     * `orm.connection.username`
     * `orm.connection.password`

  * set _auto-commit_ for each connection to `false`

  * set the transaction isolation level to the value `orm.connection.isolation`
    (or use `READ COMMITTED` by default)

 If c3p0 data source is used you can fine tune it's configuration with `c3p0.properties`
 file (see [c3p0 documentation](http://www.mchange.com/projects/c3p0/index.html#configuration_properties)
 for more details).

 Though `SimpleConnectionProvider` is an optimal choice for most applications, you
 can create your own connection provider by implementing the `ConnectionProvider` trait
 and setting the `orm.connectionProvider` configuration parameter.

*/
class SimpleConnectionProvider(val driverClass: String,
                               val url: String,
                               val username: String,
                               val password: String,
                               val isolation: Int)
    extends ConnectionProvider {

  protected def createDataSource: DataSource =
    cx.get("orm.connection.datasource") match {
      case Some(jndiName: String) =>
        val ctx = new InitialContext
        val ds = ctx.lookup(jndiName).asInstanceOf[DataSource]
        ORM_LOG.info("Using JNDI datasource ({}).", jndiName)
        ds
      case _ =>
        ORM_LOG.info("Using c3p0 connection pool.")
        val ds = new ComboPooledDataSource()
        ds.setDriverClass(driverClass)
        ds.setJdbcUrl(url)
        ds.setUser(username)
        ds.setPassword(password)
        ds
    }

  protected var _ds: DataSource = null

  def dataSource: DataSource = {
    if (_ds == null)
      _ds = createDataSource
    _ds
  }

  def openConnection(): Connection = {
    val conn = dataSource.getConnection
    conn.setAutoCommit(false)
    conn.setTransactionIsolation(isolation)
    conn
  }

  def close() {
    DataSources.destroy(_ds)
    _ds = null
  }
}

/*!## Type converter

The `TypeConverter` trait is used to set JDBC prepared statement values for execution.
If you intend to use custom types, provide your own implementation.
*/
class TypeConverter {

  def write(st: PreparedStatement, parameter: Any, paramIndex: Int) {
    parameter match {
      case None | null => st.setObject(paramIndex, null)
      case Some(v) => write(st, v, paramIndex)
      case p: Date => st.setObject(paramIndex, new Timestamp(p.getTime))
      case x: Elem => st.setString(paramIndex, x.toString())
      case bd: BigDecimal => st.setBigDecimal(paramIndex, bd.bigDecimal)
      case bd: java.math.BigDecimal => st.setBigDecimal(paramIndex, bd)
      case ba: Array[Byte] => st.setBytes(paramIndex, ba)
      case v => st.setObject(paramIndex, v)
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy