mybatis xml設定ファイル使わない場合の補足

mybatis xml設定ファイル使わない場合の方法として
まとめたが、、
mybatis XML設定を使わない場合 - Oboe吹きプログラマの黙示録

接続設定をXMLに記述しないとしても、
やはり複雑なSQLをアノテーションに記述しては読みにくいので、
SQL文は、SQL Map のXMLで、接続設定をXMLに記述しないとする方法、、

org.apache.ibatis.session.Configuration
の addMapper で指定するインターフェースClass と同じ名前のXMLファイル名で、SQL Map を用意することになる。

例)

DataSource dataSource = dsfactory.getDataSource();
Environment environment = new Environment("development", new JdbcTransactionFactory(), dataSource);
Configuration c = new Configuration(environment);
c.addMapper(OtherMapper.class);

この場合、、OtherMapper.xml というXMLファイル名で用意する。
ただし、このXMLの配置場所は、リソース読込みのクラスPATHルートではなく、
OtherMapper インターフェースクラスのパッケージ階層に、
XMLを置かないと読んでくれない。

mybatis がそのように SQL Map XML を探しにいくようになっているからだ。

Java Bean のコピー(2)、 Snake case ⇔ Camel case

昨日投稿の、Java Bean のコピー(1)、 Snake case ⇔ Camel case - Oboe吹きプログラマの黙示録
は、コピー先に Camele ⇔ Snale 対応の同じ型フィールドが存在しなくても属性値コピー生成する方法だ。
でも、厳しく、機能上より優れてる以下の属性値コピー生成を求めるなら、
・ コピー先に必須で、 Camele ⇔ Snale 変換名のフィールドが存在する。
・ コピー先に、コピー元に存在する属性が存在してコピー前に予めセットすることが可能(初期化コンストラクタで)

そこで、以下のように staticメソッドする。昨日投稿よりも処理が速いはずだ。

import java.util.function.Supplier;
import java.util.Arrays;
import java.util.regex.Pattern;
import org.yipuran.function.ThrowableBiConsumer;
import org.yipuran.function.ThrowableConsumer;
import org.yipuran.util.Fieldsetter;
import org.yipuran.util.GenericBuilder;
public static <R, T> R copySnakeToCamel(T t, Supplier<R> sup){
   GenericBuilder<R> builder = Arrays.stream(t.getClass().getDeclaredFields())
   .collect(ThrowableSupplier.to(()->GenericBuilder.of(sup))
      , ThrowableBiConsumer.of((r, f)->{
         f.setAccessible(true);
         String camel = Optional.of(Pattern.compile("_")
         .splitAsStream(f.getName().toLowerCase()).filter(e->e.length() > 0)
         .collect(StringBuilder::new, (rr, tt)->rr.append(tt.substring(0, 1).toUpperCase()).append(tt.substring(1))
         , (v, w)->{}).toString()).filter(e->e.length() > 1)
         .map(e->e.substring(0, 1).toLowerCase() + e.substring(1)).get();
      r.with(Fieldsetter.of((p, u)->camel), f.get(t));
   }), (r, u)->{});
   return builder.build();
}
public static <R, T> R copyCamelToSnake(T t, Supplier<R> sup){
   GenericBuilder<R> builder = Arrays.stream(t.getClass().getDeclaredFields())
   .collect(ThrowableSupplier.to(()->GenericBuilder.of(sup))
      , ThrowableBiConsumer.of((r, f)->{
         f.setAccessible(true);
         String snake = f.getName().replaceAll("([a-z0-9]+)([A-Z]+)", "$1_$2").toLowerCase();
         r.with(Fieldsetter.of((p, u)->snake), f.get(t));
   }), (r, s)->{});
   return builder.build();
}

このように、static メソッド、ユーティリティを用意するのではなく、
Java インターフェースの default メソッドで実装して、
コピー元のクラスで、インターフェース実装(implements)させる手段もあるだろう。

public interface SnakeCamel{

   default <R> R toSnake(Supplier<R> sup){
      GenericBuilder<R> builder = Arrays.stream(this.getClass().getDeclaredFields())
      .collect(ThrowableSupplier.to(()->GenericBuilder.of(sup))
         , ThrowableBiConsumer.of((r, f)->{
            f.setAccessible(true);
            String snake = f.getName().replaceAll("([a-z0-9]+)([A-Z]+)", "$1_$2").toLowerCase();
            r.with(Fieldsetter.of((p, u)->snake), f.get(this));
      }), (r, s)->{});
      return builder.build();
   }

   default <R> R toCamel(Supplier<R> sup){
      GenericBuilder<R> builder = Arrays.stream(this.getClass().getDeclaredFields())
      .collect(ThrowableSupplier.to(()->GenericBuilder.of(sup))
         , ThrowableBiConsumer.of((r, f)->{
            f.setAccessible(true);
            String camel = Optional.of(Pattern.compile("_")
            .splitAsStream(f.getName().toLowerCase()).filter(e->e.length() > 0)
            .collect(StringBuilder::new
            , (rr, tt)->rr.append(tt.substring(0, 1).toUpperCase()).append(tt.substring(1))
            , (v, w)->{}).toString()).filter(e->e.length() > 1)
            .map(e->e.substring(0, 1).toLowerCase() + e.substring(1)).get();
         r.with(Fieldsetter.of((p, u)->camel), f.get(this));
      }), (r, u)->{});
      return builder.build();
   }
}

(使用例)
 FooCamel ・・・属性名が Camel case で記述されたクラス
 FooSnake ・・・属性名が Snale case で記述されたクラス
コピー元のクラスは、SnakeCamel を実装している。
  public class FooSnake implements SnakeCamel
で、
Snake case → Camel case

FooSnake fs;
// インスタンスが生成されてフィールド値が設定される処理の後、
FooCamel fc = fs.toCamel(FooCamel::new);

あるいは、

FooCamel fc = fs.toCamel(()->{ 
   FooCamel f = new FooCamel();
   // FooSnake に存在しない属性設定などを実行
   return f;
});

コピー元のクラスは、SnakeCamel を実装している。
  public class FooCamel implements SnakeCamel
で、
Camel case → Snake case

FooCamel fc;
// インスタンスが生成されてフィールド値が設定される処理の後、
FooSnake fs = fc.toSnake(FooSnake::new);

あるいは、

FooSnake fs = fc.toSnake(()->{
   FooSnake f = new FooSnake();
   // FooCamel に存在しない属性設定などを実行
   return f;
});

Java Bean のコピー(1)、 Snake case ⇔ Camel case

Field 属性が、Snake case で記述された Java のオブジェクト(Bean)と
Camel case で記述された Java のオブジェクト(Bean)
相互に値をコピー生成する方法、
つまり、Snake case で記述されたフィールド属性値を対応する Camel case で記述されたフィールド属性値にセットして
オブジェクトを生成する。また、その逆である。
昨日の投稿、
Java で、Snake case ⇔ Camel case - Oboe吹きプログラマの黙示録
から、思いつくコードである。

フィールド属性値 Get / Set や、Throwable な関数型インターフェースを使用、
GitHub - yipuran/yipuran-core: Java application framework
を利用する。

Snake case → Camel case

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import org.yipuran.function.ThrowableBiConsumer;
import org.yipuran.function.ThrowableConsumer;
import org.yipuran.function.ThrowableSupplier;
import org.yipuran.util.Fieldsetter;
import org.yipuran.util.GenericBuilder;
public static <R, T> R copySnakeToCamel(T t, Class<R> cls){
   Map<String, String> fmap = Arrays.stream(cls.getDeclaredFields())
   .map(e->e.getName())
   .collect(()->new HashMap<String, String>(), (r, s)->r.put(s, s), (r, s)->{});
   GenericBuilder<R> builder = Arrays.stream(t.getClass().getDeclaredFields())
   .collect(ThrowableSupplier.to(()->GenericBuilder.of(ThrowableSupplier.to(()->cls.newInstance())))
      , ThrowableBiConsumer.of((r, f)->{
         f.setAccessible(true);
         String camel = Optional.of(Pattern.compile("_")
         .splitAsStream(f.getName().toLowerCase()).filter(e->e.length() > 0)
         .collect(StringBuilder::new
               , (rr, tt)->rr.append(tt.substring(0, 1).toUpperCase()).append(tt.substring(1))
         , (v, w)->{}).toString()).filter(e->e.length() > 1)
         .map(e->e.substring(0, 1).toLowerCase() + e.substring(1)).get();
      if (fmap.containsKey(camel)){
         r.with(Fieldsetter.of((p, u)->camel), f.get(t));
      }
   }), (r, s)->{});
   return builder.build();
}

Camel case → Snake case

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.yipuran.function.ThrowableBiConsumer;
import org.yipuran.function.ThrowableSupplier;
import org.yipuran.util.Fieldsetter;
import org.yipuran.util.GenericBuilder;
public static <R, T> R copyCamelToSnake(T t, Class<R> cls){
   Map<String, String> fmap = Arrays.stream(cls.getDeclaredFields())
   .map(e->e.getName())
   .collect(()->new HashMap<String, String>(), (r, s)->r.put(s.toLowerCase(), s), (r, s)->{});
   GenericBuilder<R> builder = Arrays.stream(t.getClass().getDeclaredFields())
   .collect(ThrowableSupplier.to(()->GenericBuilder.of(ThrowableSupplier.to(()->cls.newInstance())))
      , ThrowableBiConsumer.of((r, f)->{
         f.setAccessible(true);
         String snake = f.getName().replaceAll("([a-z0-9]+)([A-Z]+)", "$1_$2").toLowerCase();
      if (fmap.containsKey(snake)){
         r.with(Fieldsetter.of((p, u)->snake), f.get(t));
      }
   }), (r, s)->{});
   return builder.build();
}

Java で、Snake case ⇔ Camel case

先日、Python で処理する Snake case ⇔ Camel case を書いた。
Python で snake case → camel case - Oboe吹きプログラマの黙示録

Python で camel case → snake case - Oboe吹きプログラマの黙示録

では、Java の場合、これもネット検索すると沢山いろんな方法が紹介されてる。
google guava なんか使わずに、Java8 で書くと、、、

Camel case ⇒ Snake case
これは、正規表現の処理で簡単に済む。
例)

String camel = "firstName2ByAbc";
String snake = camel.replaceAll("([a-z0-9]+)([A-Z]+)", "$1_$2").toUpperCase();

結果

FIRST_NAME2_BY_ABC

Snake case ⇒ Camel case
"_" で split して処理するのが最も素直であろう。ただ処理ループがいろんな方法あると思うが、
Java8 Stream で処理するように書くと、
以前、投稿した、
Stream で処理する正規表現 - Oboe吹きプログラマの黙示録
あるいは、
Git-hub に入れた
https://github.com/yipuran/yipuran-core/blob/master/src/main/java/org/yipuran/regex/RegExpress.java
の中の replace メソッドを参考に、 Camel case への変換用メソッドを用意することができる。
Snake case 文字列を対象にするのが前提。。

public static String toCamelcase(String string){
   Matcher m = Pattern.compile("_[a-zA-Z]").matcher(string);
   BiFunction<String, Integer, String> f = (s, i)->i==0 ? s.replaceAll("_", "")
                                            : s.substring(1, 2).toUpperCase();
   AtomicInteger i = new AtomicInteger(0);
   AtomicInteger x = new AtomicInteger(0);
   return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<String>(){
      @Override
      public boolean hasNext(){
         return m.find();
      }
      @Override
      public String next(){
         return string.substring(i.getAndSet(m.end()), m.start())
               + f.apply(m.group(), x.getAndIncrement());
      }
   }, Spliterator.ORDERED), false).collect(Collectors.joining()) + string.substring(i.get());
}

もう1つ、↑の方法ではなく、読みにくいけど、正規表現⇒splitAsStream⇒Optional の方法

String camel = Optional.of(Pattern.compile("_").splitAsStream(snake.toLowerCase())
.filter(e->e.length() > 0).collect(StringBuilder::new
, (r, t)->r.append(t.substring(0, 1).toUpperCase()).append(t.substring(1))
, (r, t)->{}).toString())
.map(e->e.substring(0, 1).toLowerCase() + e.substring(1)).get();

これなら、わざわざメソッドを用意しなくて良い。

Snake case ⇒ Camel case が意外と複雑になってしまう。

Excel VBA から Python 実行で、Snake case ⇔ Camel case

Python インストールしてないPCでは動作しない。
xlwings のインストールも必要。

ということで、Python インストールしている人にしかお奨めしない Excel VBA から実行するもの。
Excel VBA は、筆者はまったく理解していない≒初心者≒素人です。

今回、Excel の作成を紹介します。
Excel 準備
ボタンを配置してVBAマクロを記述するために、Excel のオプションを開いて、
「リボンのユーザ設定」で、「メインタブ」の「開発」をチェックONにします。
f:id:posturan:20191003114603j:plain
メニューに「開発」が出るので、「開発」→ VisualBasic で、VBA のウィンドウが開きます。
開いたら、Python の xlwings モジュールをインポートします。
 (前提:  xlwings  は、 pip でインストール済 )
f:id:posturan:20191003115046j:plain
xlwings がインストールされているフォルダ、
Python の Lib の下の site-packages
Pythonインストールフォルダ\Lib\site-packages  
に、xlwings フォルダがあり、その下に、xlwings.bas があるはずです。
xlwings.basをインポートします。
f:id:posturan:20191003115644j:plain
VBA プロジェクトの標準モジュールフォルダに、xlwings が追加されます。
f:id:posturan:20191003115820j:plain

Python コードの準備
snakecamel.py
変換対象、書込み対象の文字列は、B列とD列で書いてます。

# -*- coding: UTF-8 -*-
import xlwings as xw

def toCamelCase(string, titleCase=False):
    import re
    if titleCase:
        return ''.join(x.title() for x in string.split('_'))
    else:
        return re.sub("_(.)", lambda m:m.group(1).upper(), string.lower())
def toSnakeCase(string, upper=False):
    import re
    if upper:
        return re.sub("(.[A-Z])", lambda x: x.group(1)[0] + "_" + x.group(1)[1], string).lower().upper()
    else:
        return re.sub("(.[A-Z])", lambda x:x.group(1)[0] + "_" +x.group(1)[1], string).lower()
    
def writeCamel():
    sheet = xw.sheets[0]
    if sheet.range('B1').value:
        for i in range(4, sheet.range('B3').current_region.last_cell.row + 1):
            snakestr = sheet.range("B%d" % i).value
            sheet.range("D%d" % i).value = toCamelCase(snakestr, True)
    else:
        for i in range(4, sheet.range('B3').current_region.last_cell.row + 1):
            snakestr = sheet.range("B%d" % i).value
            sheet.range("D%d" % i).value = toCamelCase(snakestr)
def writeSnake():
    sheet = xw.sheets[0]
    if sheet.range('D1').value:
        for i in range(4, sheet.range('D3').current_region.last_cell.row + 1):
            camelstr = sheet.range("D%d" % i).value
            sheet.range("B%d" % i).value = toSnakeCase(camelstr, True)
    else:
        for i in range(4, sheet.range('D3').current_region.last_cell.row + 1):
            camelstr = sheet.range("D%d" % i).value
            sheet.range("B%d" % i).value = toSnakeCase(camelstr)

Excel のつづき、
ボタンを用意します。
f:id:posturan:20191003121929j:plain
f:id:posturan:20191003122024j:plain
ボタンクリックの関数名を設定して、「新規作成」を押して、
Python 呼出しの記述を行います。
Call RunPython で、
import Pythonファイル名拡張子を除く。
↑の名称 + "." ドット + メソッド
で呼び出します。
f:id:posturan:20191003122436j:plain

Excel 全体は以下のようなものです。
f:id:posturan:20191003123259j:plain
チェックボックスは、コントロールの書式設定
→ コントロールタブで反映先を設定すると、
真偽値(TRUE/FALSE)が入るので反映先は、隠れる場所に配置します。

Python で camel case → snake case

Python で camel case → snake case もネット検索するとたくさん方法が紹介されてる。
しかし、よく紹介されてる方法は、本当に期待どおりか疑問がのこる。

# -*- coding: UTF-8 -*-
import re
str = "abcDefGhi2j"
str2 = "AbcDefGhi2j"

res = re.sub("([A-Z])",lambda x:"_" + x.group(1), str).lower()
res2 = re.sub("([A-Z])",lambda x:"_" + x.group(1), str2).lower()

print("%s → %s" % (str , res))
print("%s → %s" % (str2, res2))

結果は、、

abcDefGhi2j → abc_def_ghi2j
AbcDefGhi2j → _abc_def_ghi2j

2番目、str2 の "A"が、"_a" になってしまう。これを "a" にする方法は、
大文字1文字正規表現ではなく、任意1文字+大文字1文字で処理する方法にすることだ。

res2 = re.sub("(.[A-Z])",lambda x:x.group(1)[0] + "_" +x.group(1)[1], str2).lower()
print("%s → %s" % (str2, res2))

結果、、

AbcDefGhi2j → abc_def_ghi2j

ラムダなんて使わない方法では、、

res2 = re.sub('([a-z0-9])([A-Z])', r'\1_\2', re.sub('(.)([A-Z][a-z]+)', r'\1_\2', str2)).lower()

という手段もある。

変換後の snake case 文字列が全て小文字か?大文字か?どちらが良いか選択できる
方が良いので、メソッド化する。

def toSnakeCase(string, upper=False):
    import re
    if upper:
        return re.sub("(.[A-Z])",
                      lambda x: x.group(1)[0] + "_" + x.group(1)[1],
                      string).lower().upper()
    else:
        return re.sub("(.[A-Z])",
                      lambda x:x.group(1)[0] + "_" +x.group(1)[1],
                      string).lower()

第2引数=True なら、結果全て大文字だ。

res = toSnakeCase(str2, True)
print("%s → %s" % (str2 , res))

結果、、

AbcDefGhi2j → ABC_DEF_GHI2J