0

我正在编写一个导出器,它将从数据库中获取结果并获取每个单独的记录并将其写入逗号分隔的文件。不同的查询将为其创建不同的工作人员,因为它们需要编写单独的 csv 文件。首先,我将任务分解为两个不同的参与者。Actor1 是一个 JdbcWorker,它通过一个查询参数查询数据库,而 Actor2 是一个 CSVWriter,它接收代表查询结果的案例类,该查询需要附加到 CSV 中。我的第一个问题是,尽管我喜欢这两个工作人员提供的关注点分离,但是将 jdbc 查询与 CSV 编写器分离是一种好的设计吗?

所以,我写了actor1如下:

class DataQueryWorker(csvExporterWorker: ActorRef) extends Actor with ActorLogging{

  private implicit def ModelConverter(rs: ResultSet): QueryModel = {
    QueryModel(
      id = rs.getString(0),
      name = rs.getString(1),
      age = rs.getString(2),
      gender = rs.getString(3))
}

  private def sendModelToCsvWorker(model: QueryModel): Unit = {
    csvExporterWorker ! model
  }

  private def startExport[T](queryString: String)(resultFunc: T => Unit)(implicit ModelConverter: ResultSet => T): Unit = {
    try {
      val connection = DriverManager.getConnection(DbConfig.connectionString,
        DbConfig.user,
        DbConfig.password)
      val statement = connection.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY)
      statement.setFetchSize(Integer.MIN_VALUE)
      val rs = statement.executeQuery(queryString)
      while (rs.next()) {
        resultFunc(ModelConverter(rs))
      }
    } catch {
      case e: Exception => //What to do in case of an exception???
    }
  }

  override def receive() = {
    case startEvent => startExport(DbConfig.ModelExtractionQuery)(sendModelToCsvWorker)
  }

}

我的下一个问题是,上面编写的代码是查询数据库、将其包装在模型中并将结果发送到 CSVWorker 的正确方法吗?我不确定我是否正确地遵循了 scala 习语。另外,在这种情况下处理异常的正确方法是什么?

在这方面获得一些指导会很棒。

谢谢

4

2 回答 2

2

我认为您的方法可以进行一些小的更改:

对于 DB 演员,您可能想要考虑制作这些长寿演员,汇集在Router. 让这个actor保持Connection它的状态,在启动时打开它一次,然后关闭,然后在由于失败重新启动的情况下重新打开。我认为这可能是一种更好的方法,因为您并不总是需要打开连接来调用导出数据。您只需要编写一些代码来检查连接状态(并重新连接),然后再调用它。

一旦您使 DB Actor 有状态且长期存在,您将无法CSVWorker通过构造函数传入。相反,您应该通过消息将其传递给此参与者,表明您想要导出。您可以通过这样的案例类来做到这一点:

case class ExportQuery(query:String, csvWorker:ActorRef)

改变你receive的样子:

def receive = {
  case ExportQuery(query, csvWorker) =>
    ...
}

最后,删除try/catch逻辑。除非您可以根据此故障做一些有意义的事情(例如调用一些备用代码路径),否则捕获它是没有意义的。让演员失败并重新启动(并关闭/重新打开连接)并继续前进。

于 2013-05-08T12:13:08.033 回答
-1

我认为在这里使用演员可能是矫枉过正。

当您想安全地使用多个线程对可变状态进行操作时,Actor 很有用。但是,在您的情况下,您说每个查询都写入一个单独的 CSV 文件(因此每个 CSV 文件只有一个线程)。我不认为 CSVWorker 演员是必要的。如果 DBWorker 比 CSVWorker 快得多,它甚至可能是有害的,因为参与者邮箱可能会增长并消耗大量内存。

就个人而言,我只是直接致电 CSV 编写器。

关于关注点分离的问题取决于您是否希望在不相关的上下文中重用此代码。如果您可能希望将您的 JDBC 工作者与其他编写器一起使用,那么它可能是值得的(尽管有一种观点认为您最好等到需要再重构 - 您不需要它或雅格尼)。否则,您最好简化一下。

如果您决定将 JDBC 代码直接附加到 CSV 代码,您可能还需要进行案例类转换。同样,如果这是将在其他地方重复使用的代码,那么最好保留它。

异常处理取决于您的应用程序,但在 Scala 中(与 Java 不同),如果您不知道如何处理异常,您可能不应该做任何事情。取出 try..catch 块,让异常传播——有东西会捕获它并报告它。

Java 强制您处理异常,这在理论上是一个好主意,但在实践中通常会导致错误处理代码没有任何实际用途(重新抛出,或者更糟的是吞下错误)。

哦,如果您正在编写大量将 ResultSets 转换为案例类的代码,反之亦然,您可能希望考虑使用对象关系映射框架,如 Slick 或 Squeryl。它们针对这个用例进行了优化。

于 2013-05-08T10:53:25.953 回答