domaframework / doma

DAO oriented database mapping framework for Java
https://doma.readthedocs.io/
Apache License 2.0
440 stars 70 forks source link

SQLテンプレートで組み立てたSQLを別のSQLに組み込んで実行したい #178

Closed nakamura-to closed 7 years ago

nakamura-to commented 7 years ago

例えばAmazon RedshiftのUNLOADコマンドのselect-statement部分をDomaのSQLテンプレートで表現し、UNLOADコマンドを実行したい。

https://gist.github.com/newta/03584d39116a362efde0d72ee83e68f1 のコードと同等のことを実現する場合は、次のようにすれば良いと思われる。

@ HandleSqlアノテーションをDomaで新規に実装し、組み立てたSQLをアプリで受け取って任意に利用できるようにする。

@Dao(config = AppConfig.class)
public interface MyDao {

  @HandleSql
  void unload(LocalDateTime startAt, LocalDateTime endAt, BiFunction<Config, SqlText, Void> handler); 
}

SQLファイルは次のようなものを用意する。

SELECT 
  hoge,foo,bar
FROM
  data_log_table
WHERE
  created_at BETWEEN /*^ startAt */'2016-11-01 10:00:00' AND /*^ endAt */'2016-11-01 11:00:00'

利用するコードは次のようなものになる。

S3BucketPath s3bucket = ...
CustomAWSCredentials credenials = ...
MyDao dao = ...
dao.unload(startAt, endAt, (config, sqlText) -> {
  String sql = String.format("UNLOAD ('%s') to '%s' credentials 'aws_access_key_id=%s;aws_secret_access_key=%s' parallel off",
                        sqlText.getRawSql(), s3bucket.buildPath(), credenials.getAccessKey(), credenials.getSercretKey());

  DataSource dataSource = config.getDataSource();
  Connection connection = dataSource.getConnection();
  PreparedStatement ps = connection.preparedStatement(sql);
  ps.executeUpdate();
});

上のコードは冗長だがUnloadCommnadのようなクラスをアプリで用意してもらえれば簡潔に書ける。

S3BucketPath s3bucket = ...
CustomAWSCredentials credenials = ...
UnloadCommnad command = new UnloadCommnad(s3bucket, credenials);
MyDao dao = ...
dao.unload(startAt, endAt, command);
bakenezumi commented 7 years ago

明けましておめでとうございます。

面白い機能ですね。 ちょっと冗長にはなりますが、なんとなく下記のように使ってみたい欲望にかられました。

@Dao(config = AppConfig.class)
public interface MyDao {

  @HandleSql
  String getSelectSql(LocalDateTime startAt, LocalDateTime endAt, BiFunction<Config, Sql, String> handler); 

}
@Dao(config = AppConfig.class)
public interface UnloadDao {

  @HandleSql
  void unload(String innerSql, S3BucketPath s3bucket, CustomAWSCredentials credentials, BiFunction<Config, Sql, Void> handler);
}

UnloadDao/unload.sql

UNLOAD
  ( /*^ innerSql */'select hoge from xx' )
to
  /*^ s3bucket.buildPath() */'xxx'
credentials
  /*^ String.format("aws_access_key_id=%s;aws_secret_access_key=%s", 
        credenials.getAccessKey(),
        credenials.getSercretKey()) */'aws_access_key_id=xx;aws_secret_access_key=xx'
parallel off

汎用ハンドラ

public class Handlers {

  public static String getRowSql(Config config, Sql sql) {
    return sql.getRawSql();
  }

  public static void execute(Config config, Sql sql) {
    DataSource dataSource = config.getDataSource();
    Connection connection = dataSource.getConnection();
    PreparedStatement ps = connection.preparedStatement(sql.getRawSql());
    ps.executeUpdate();
  }
}

利用するコード

S3BucketPath s3bucket = ...
CustomAWSCredentials credenials = ...
MyDao dao = ...
UnloadDao unloadDao = ...

unloadDao.unload(
  dao.getSelectSql(startAt, endAt, Handlers::getRowSql), s3bucket, credenials, Handlers::execute);

また任意の場所でログ出力等、いろいろできそうですね。

nakamura-to commented 7 years ago

なるほど。組み立てたSQLをパラメータで渡して、さらに別のSQLを組み立てるということですか。確かにそういう使い方もできますね。