0

データベースから結果を取得し、個々のレコードをすべて取得してカンマ区切りのファイルに書き込むエクスポーターを作成しています。クエリが異なれば、別の csv ファイルを書き込む必要があるため、別のワーカーが作成されます。まず、タスクを 2 つの異なるアクターに分離しました。Actor1 は、クエリ パラメータを指定してデータベースをクエリする JdbcWorker であり、Actor2 は、CSV に追加する必要があるクエリからの結果を表すケース クラスを受け取る CSVWriter です。私の最初の質問は、これら 2 つのワーカーによって提供される関心の分離が好きですが、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. このアクターにConnectionをそのままの状態で保持させ、起動時に一度開き、障害による再起動の場合に閉じてから再度開きます。データをエクスポートするための呼び出しのために常に接続を開く必要があるとは限らないため、これはより良いアプローチであると思います。呼び出しを行う前に、おそらく接続の状態をチェックする (および再接続する) ためのコードを書く必要があるだけです。

CSVWorkerDB アクターをステートフルで長寿命にすると、コンストラクター経由で渡すことができなくなります。代わりに、エクスポートが必要であることを示すメッセージを介してこのアクターに渡す必要があります。次のようなケースクラスを介してそれを行うことができます。

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

次のように変更receiveします。

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

最後に、try/catchロジックを削除します。この失敗に基づいて何か意味のあること (別のコード パスを呼び出すなど) を実行できない限り、それをキャッチしても意味がありません。アクターが失敗して再起動し (そして接続を閉じる/再度開く)、先に進みます。

于 2013-05-08T12:13:08.033 に答える
-1

ここでアクターを使用するのは、おそらくやり過ぎだと思います。

アクターは、複数のスレッドで可変状態を安全に操作したい場合に便利です。しかし、あなたの場合、各クエリは個別の CSV ファイルに書き込むと言います (したがって、CSV ファイルごとに 1 つのスレッドしかありません)。CSVWorker アクターは必要ないと思います。DBWorker が CSVWorker よりも大幅に高速な場合、攻撃者のメールボックスが大きくなり、大量のメモリを消費する可能性があるため、有害である可能性さえあります。

個人的には、CSV ライターに直接電話するだけです。

関心の分離に関する問題は、このコードが無関係なコンテキストで再利用されると予想されるかどうかによって異なります。JDBC ワーカーを他のライターと一緒に使用する可能性が高い場合は、それを行う価値があるかもしれません (ただし、リファクタリングの前に必要が生じるまで待った方がよいという考え方があります - You Aint Gonna Need It またはヤグニ)。それ以外の場合は、単純化したほうがよい場合があります。

JDBC コードを CSV コードに直接アタッチする場合は、ケース クラス変換も実行する必要があります。繰り返しますが、これが別の場所で再利用されるコードである場合は、そのままにしておくことをお勧めします。

例外処理はアプリケーションによって異なりますが、Scala では (Java とは異なり)、例外について何をすべきかわからない場合は、おそらく何もすべきではありません。try..catch ブロックを取り除き、例外を伝播させます。何かがそれをキャッチして報告します。

Java では例外を処理する必要があります。これは理論的には優れたアイデアですが、実際には、実際には何の役にも立たないエラー処理コードになることがよくあります (再スローするか、さらに悪いことに、エラーを飲み込みます)。

ああ、ResultSet をケース クラスに変換するコードを大量に作成している場合、またはその逆のコードを作成している場合は、Slick や Squeryl などのオブジェクト リレーション マッピング フレームワークの使用を検討することをお勧めします。それらはまさにこのユースケースに最適化されています。

于 2013-05-08T10:53:25.953 に答える