Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Clean up PreparedStatement handling for DB2

Refs #965

Refactored MetaMapper to de-duplicate code for setting
prepared statement values. Will work with reporter to
test against DB2.
  • Loading branch information...
commit 0e3669450f44ff296752c4960690b830036e4acb 1 parent 52c9825
@dchenbecker dchenbecker authored
View
22 persistence/db/src/main/scala/net/liftweb/db/Driver.scala
@@ -161,6 +161,7 @@ object DriverType {
case (SybaseASEDriver.name,_,_) => SybaseASEDriver
case (OracleDriver.name,_,_) => OracleDriver
case (MaxDbDriver.name,_,_) => MaxDbDriver
+ case (other, _, _) if other.startsWith(DB2Driver.name) => DB2Driver
case x => throw new Exception(
"Lift mapper does not support JDBC driver %s.\n".format(x) +
"See http://wiki.liftweb.net/index.php/Category:Database for a list of supported databases.")
@@ -168,6 +169,27 @@ object DriverType {
}
}
+object DB2Driver extends DriverType("DB2") {
+ def binaryColumnType = "LONG VARCHAR FOR BIT DATA"
+ def booleanColumnType = "SMALLINT"
+ def clobColumnType = "LONG VARCHAR"
+ def dateTimeColumnType = "TIMESTAMP"
+ def dateColumnType = "DATE"
+ def timeColumnType = "TIME"
+ def integerColumnType = "INT"
+ def integerIndexColumnType = "INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY"
+ def enumColumnType = "INT"
+ def longForeignKeyColumnType = "INT"
+ def longIndexColumnType = "BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY"
+ def enumListColumnType = "INT"
+ def longColumnType = "INT"
+ def doubleColumnType = "DOUBLE"
+
+ override def brokenLimit_? : Boolean = true
+
+ override def pkDefinedByIndexColumn_? = true
+}
+
object DerbyDriver extends DriverType("Apache Derby") {
def binaryColumnType = "LONG VARCHAR FOR BIT DATA"
def booleanColumnType = "SMALLINT"
View
71 persistence/mapper/src/main/scala/net/liftweb/mapper/MetaMapper.scala
@@ -758,6 +758,44 @@ trait MetaMapper[A<:Mapper[A]] extends BaseMetaMapper with Mapper[A] {
*/
def clean_?(toCheck: A): Boolean = mappedColumns.foldLeft(true)((bool, ptr) => bool && !(??(ptr._2, toCheck).dirty_?))
+ /**
+ * Sets a prepared statement value based on the given MappedField's type
+ *
+ * @param field The field corresponding to this prepared statement value
+ * @param columnName The column name for this value
+ * @param st The prepared statement
+ * @param index The index for this prepared statement value
+ * @param conn The connection for this prepared statement
+ */
+ private def setPreparedStatementValue(field: MappedField[_, A],
+ columnName: String,
+ st: PreparedStatement,
+ index: Int,
+ conn: SuperConnection) {
+ // Remap the type if the driver wants
+ val columnType =
+ if (field.dbIgnoreSQLType_?)
+ field.targetSQLType(columnName)
+ else
+ conn.driverType.columnTypeMap(field.targetSQLType(columnName))
+
+ // We generally use setObject for everything, but we've found some broken JDBC drivers
+ // which has prompted us to use type-specific handling for certain types
+ columnType match {
+ case Types.VARCHAR => st.setString(index, field.jdbcFriendly(columnName).asInstanceOf[String])
+
+ // Sybase SQL Anywhere and DB2 choke on using setObject for boolean data
+ case Types.BOOLEAN => field.jdbcFriendly(columnName) match {
+ case intData : java.lang.Integer => st.setBoolean(index, intData.intValue != 0)
+ case b : java.lang.Boolean => st.setBoolean(index, b.booleanValue)
+ case _ => st.setObject(index, field.jdbcFriendly(columnName), columnType) // If we can't figure it out, maybe the driver can
+ }
+
+ case _ =>
+ st.setObject(index, field.jdbcFriendly(columnName), columnType)
+ }
+ }
+
def save(toSave: A): Boolean = {
toSave match {
case x: MetaMapper[_] => throw new MapperException("Cannot save the MetaMapper singleton")
@@ -812,21 +850,11 @@ trait MetaMapper[A<:Mapper[A]] extends BaseMetaMapper with Mapper[A] {
st =>
var colNum = 1
+ // Here we apply each column's value to the prepared statement
for (col <- mappedColumns) {
val colVal = ??(col._2, toSave)
if (!columnPrimaryKey_?(col._1) && colVal.dirty_?) {
- colVal.targetSQLType(col._1) match {
- case Types.VARCHAR => st.setString(colNum, colVal.jdbcFriendly(col._1).asInstanceOf[String])
-
- case _ =>
- if (colVal.dbIgnoreSQLType_?)
- st.setObject(colNum, colVal.jdbcFriendly(col._1))
- else
- st.setObject(colNum, colVal.jdbcFriendly(col._1),
- conn.driverType.
- columnTypeMap(colVal.
- targetSQLType(col._1)))
- }
+ setPreparedStatementValue(colVal, col._1, st, colNum, conn)
colNum = colNum + 1
}
}
@@ -862,24 +890,7 @@ trait MetaMapper[A<:Mapper[A]] extends BaseMetaMapper with Mapper[A] {
for (col <- mappedColumns) {
if (!columnPrimaryKey_?(col._1)) {
val colVal = col._2.invoke(toSave).asInstanceOf[MappedField[AnyRef, A]]
- colVal.targetSQLType(col._1) match {
- case Types.VARCHAR =>
- st.setString(colNum, colVal.jdbcFriendly(col._1).asInstanceOf[String])
-
- // Sybase SQL Anywhere chokes on using setObject for boolean data
- case Types.BOOLEAN =>
- // For some reason MappedBoolean uses Ints to represent data
- st.setBoolean(colNum, colVal.jdbcFriendly(col._1) == 1)
-
- case _ =>
- if (colVal.dbIgnoreSQLType_?)
- st.setObject(colNum, colVal.jdbcFriendly(col._1))
- else
- st.setObject(colNum, colVal.jdbcFriendly(col._1),
- conn.driverType.
- columnTypeMap(colVal.
- targetSQLType(col._1)))
- }
+ setPreparedStatementValue(colVal, col._1, st, colNum, conn)
colNum = colNum + 1
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.