Reading serialized object from mariadb with Anorm

时间:2018-01-15 18:17:03

标签: scala anorm

I have implemented reading serialized object with plain JDBC, and now want to use Anorm 2.3.8.

Plain JDBC Scala code is as bellow:

def loadModel(rName: String, rPdbCode: String) = {
  //Connection Initialization
    Class.forName("org.mariadb.jdbc.Driver")
    val jdbcUrl = s"jdbc:mysql://172.17.0.2:3306/db_profile?user=root&password=root"
    val connection = DriverManager.getConnection(jdbcUrl)

    //Reading Pre-trained model from Database
    var model: InductiveClassifier[MLlibSVM, LabeledPoint] = null
    if (!(connection.isClosed())) {

      val sqlRead = connection.prepareStatement("SELECT r_model FROM MODELS WHERE r_name = ? and r_pdbCode = ?")
      sqlRead.setString(1, rName)
      sqlRead.setString(2, rPdbCode)
      val rs = sqlRead.executeQuery()
      rs.next()

      val modelStream = rs.getObject("r_model").asInstanceOf[Array[Byte]]
      val modelBaip = new ByteArrayInputStream(modelStream)
      val modelOis = new ObjectInputStream(modelBaip)
      model = modelOis.readObject().asInstanceOf[InductiveClassifier[MLlibSVM, LabeledPoint]]

      rs.close
      sqlRead.close
      connection.close()
    } else {
      println("MariaDb Connection is Close")
      System.exit(1)
    }
    model
}

Now I want to load my serialized model with amAnorm so everything is consistent with rest of my application and I only use the default connection.

Following is my effort but I can't convert Stream[Row] to Array[Byte] as it raises an exception:

ClassCastException: scala.collection.immutable.Stream$Cons cannot be cast to [B]

The Anorm 2.3.8 code is as bellow.

def loadModel(rName: String, rPdbCode: String) = {
    //Connection Initialization
    var model: InductiveClassifier[MLlibSVM, LabeledPoint] = null
    DB.withConnection { implicit c =>
      val results = SQL(
        """
          | SELECT r_model
          | FROM MODELS
          | WHERE r_name={r_name} 
          | AND r_pdbCode={r_pdbCode};
        """.stripMargin).on(
          "r_name" -> rName,
          "r_pdbCode" -> rPdbCode).apply()
      val byteArray = results.asInstanceOf[Array[Byte]]
      val modelBaip = new ByteArrayInputStream(byteArray)
      val modelOis = new ObjectInputStream(modelBaip)
      model = modelOis.readObject().asInstanceOf[InductiveClassifier[MLlibSVM, LabeledPoint]]

    }

    model
}

1 个答案:

答案 0 :(得分:0)

此代码来自Anorm 2.5,但希望它与之前的版本没有什么不同。假设我们将Model定义为

case class Model(i: Int, s: String) extends Serializable

这里是loadModel和test-wrapper testLoadModel使用简化表调用它

import anorm._
import java.io._

def loadModel(rName: String): Model = {
  db.withConnection { implicit c =>
    val result = SQL"""
        SELECT r_model
        FROM MODELS
        WHERE r_name=${rName}
      """.as(SqlParser.byteArray("r_model").single)

    Logger.info(s"result ${result.getClass} => $result")
    deserialize[Model](result)
  }
}

def serialize(obj: Serializable): Array[Byte] = {
  val outBuf = new ByteArrayOutputStream()
  val out = new ObjectOutputStream(outBuf)
  out.writeObject(obj)
  out.flush()
  outBuf.toByteArray
}

def deserialize[T](byteArray: Array[Byte]): T = {
  val ois = new ObjectInputStream(new ByteArrayInputStream(byteArray))
  ois.readObject().asInstanceOf[T]
}

def testLoadModel(): Unit = {
  db.withConnection { implicit c =>
    val createRes = SQL(
      """
        |DROP TABLE   IF EXISTS MODELS;
        |
        |CREATE TABLE MODELS(
        | r_name  VARCHAR(50) PRIMARY KEY NOT NULL,
        | r_model  VARBINARY NOT NULL
        |         );
      """.
        stripMargin).execute()
    Logger.info(s"Create result = $createRes")
    val rName = "rName"
    val m0 = Model(42, "Abc")
    val ser0 = serialize(m0)

    val insertRes = SQL(
      """
        | insert into MODELS values ({r_name},{r_model})
      """
        .stripMargin).on("r_name" -> rName, "r_model" -> ser0).executeInsert()
    Logger.info(s"Insert result = $insertRes")

    val m1 = loadModel(rName)
    Logger.info(s"m0 = $m0")
    Logger.info(s"m1 = $m1")
  }
}

主要技巧似乎是使用.as(SqlParser.byteArray("r_model").single)

请注意,如果无法保证记录的存在,您可能希望使用.singleOpt。 此外,在查询中使用LIMIT 1 SQL clause以获得更好的性能也是有意义的。