【VBA】Hibernate Toolsで生成したEntityクラスのプリミティブ型を対応する参照型に変換するツールを作ってみた

テーブル定義やVIEW定義をもとに対応するEntityクラスを瞬時に作成できるHibernate Tools。
とても便利なのですが、デフォルトでは数値型はプリミティブ型で作成されてしまいます。
DBの値にnullが入りうる場合、これだとNG。
対応する参照型（プリミティブ型と違ってnullも許容）に変換できる設定がないか探してみるも出てこない、、
これなら作ってしまったほうが速い！というわけで、上を実現するツールを作ってみました。

## 環境
- OS：Windows10
- VBA：7.1
- IDE：Eclipse2020-03
- Java：8
- MySQL：5.7
- Hibernate ORM：5.4
- Spring Boot：2.3.1

## Entityクラスの作成
Hibernate ToolsでのEntityクラスの作成方法は

- [Hibernate Toolsを使ってJPAのEntityクラスを生成する | Qiita](https://qiita.com/rubytomato@github/items/cb7c40cea2ff3225d5e3)

に詳しく記載されているため、本記事では割愛します。

## データ型の対応表
MySQLのデータ型(以下、MySQL）、Hibernate Toolsで出力されるJavaのデータ型(以下、Hibernate Tools）、対応する参照型は以下の通りです。
（実際にHibernate Toolsを動かしてみての出力結果になります。）

|MySQL|HibernateTools(Java)|対応する参照型(Java)|
|---|---|---|
|INTEGER|int|Integer|
|INT|int|Integer|
|TINYINT|byte|Byte|
|SMALLINT|short|Short|
|MEDIUMINT|int|Integer|
|BIGINT|BigInteger|-|
|DECIMAL|BigDecimal|-|
|NUMERIC|BigDecimal|-|
|FLOAT|float|Float|
|DOUBLE|double|Double|
|BIT|byte|Byte|

BigInteger, BigDecimalは参照型なので、変換不要ということで、対応する参照型は書いていません。
それにしても、INTEGERはInteger, BIGINTはlongで出力されるんじゃないんですね、、

## コード
上記をもとに、VBAのコードを作成しました。
(数値型以外の変換も少し入ってます。）

```vb:
Sub macro_for_entity_class_java()

    Dim filePath As String ' ファイルパス取得用
    Dim javaFile As String ' Javaファイル取得用
    
    Dim FSO As Object ' ファイル処理用
    
    Dim replaceContent As String  ' 文字列変換用
    
    Dim reg As Object '正規表現オブジェクト用（@JoinColumnの置換に使用）
    Set reg = CreateObject("VBScript.RegExp")  '正規表現オブジェクト用の設定
    
    ' Excelファイルのパスを取得
    filePath = ThisWorkbook.Path
    ' Excelファイルと同ファイル内にあるJavaファイルを取得
    javaFile = Dir(filePath & "\*.java")
    
    ' Javaファイルを順に開いて処理を実行
    Do While javaFile <> ""
    
        ' Javaファイルを開いて内容を読み込む
        Set FSO = CreateObject("Scripting.FileSystemObject")
        With FSO.GetFile(filePath & "\" & javaFile).OpenAsTextStream
            replaceContent = .ReadAll
            .Close
        End With
        
        ' boolean → Boolean
        replaceContent = replace(replaceContent, "private boolean", "private Boolean")
        replaceContent = replace(replaceContent, "public boolean get", "public Boolean get")
        replaceContent = replace(replaceContent, "(boolean", "(Boolean")
        
        ' byte → Byte
        replaceContent = replace(replaceContent, "private byte", "private Byte")
        replaceContent = replace(replaceContent, "public byte get", "public Byte get")
        replaceContent = replace(replaceContent, "(byte", "(Byte")
        
        ' short → Short
        replaceContent = replace(replaceContent, "private short", "private Short")
        replaceContent = replace(replaceContent, "public short get", "public Short get")
        replaceContent = replace(replaceContent, "(short", "(Short")
        
        ' int → Integer
        replaceContent = replace(replaceContent, "private int", "private Integer")
        replaceContent = replace(replaceContent, "public int get", "public Integer get")
        replaceContent = replace(replaceContent, "(int", "(Integer")
        
        ' long → Long
        replaceContent = replace(replaceContent, "private long", "private Long")
        replaceContent = replace(replaceContent, "public long get", "public Long get")
        replaceContent = replace(replaceContent, "(long", "(Long")
        
        ' float → Float
        replaceContent = replace(replaceContent, "private float", "private Float")
        replaceContent = replace(replaceContent, "public float get", "public Float get")
        replaceContent = replace(replaceContent, "(float", "(Float")
        
        ' double → Double
        replaceContent = replace(replaceContent, "private double", "private Double")
        replaceContent = replace(replaceContent, "public double get", "public Double get")
        replaceContent = replace(replaceContent, "(double", "(Double")
        
        ' Object → String（対JSON型；PK用のクラスは除く）
        If InStr(javaFile, "PK.java") = 0 Then
            replaceContent = replace(replaceContent, "private Object", "private String")
            replaceContent = replace(replaceContent, "public Object get", "public String get")
            replaceContent = replace(replaceContent, "(Object", "(String")
        End If
        
        ' @JoinColumnに「insertable = false, updatable = false」を追加
        reg.Pattern = "(.+?)@JoinColumn\((.+?)\)"
        reg.IgnoreCase = False
        reg.Global = True
        replaceContent = reg.replace(replaceContent, "$1@JoinColumn($2, insertable = false, updatable = false)")
        
        ' もとのファイルを削除して、置換後の内容を記述したファイルを出力する
        FSO.GetFile(filePath & "\" & javaFile).Delete
        FSO.CreateTextFile (filePath & "\" & javaFile)
        With FSO.GetFile(filePath & "\" & javaFile).OpenAsTextStream(8)
            .Write replaceContent
            .Close
        End With
        
        ' 次のJavaファイルを取得
        javaFile = Dir
        
    Loop
    
End Sub
```

## 補足事項
- 変換するJavaファイルはマクロを記述したExcelファイルと同階層に置いてください。
- 変換処理のところ、int → Integer, long → Longだけでいいのでは?と思われるかもしれませんが、introductionやlongitudeといった変数名が出てきた場合に変換されてしまうため、それを防ぐために上記のような書き方にしています。
- JSON型はString型として取得後、Javaオブジェクトに変換してあげる必要があります。以下のいずれかのライブラリを使って変換すればOK!<br>
①Jackson：【参考】[Jackson使い方メモ | Qiita](https://qiita.com/opengl-8080/items/b613b9b3bc5d796c840c)<br>
②JSONIC：【参考】[【Java】JSONの基本とJSONICを使用してJSONの変換をする方法 | TASK NOTES](https://www.task-notes.com/entry/20150919/1442639772)
- 本ツールを[GitHub](https://github.com/CHI-3/macro_for_entity_class_java)に公開しました。よろしければご使用ください!

## 更新履歴
- 2020/06/17：データ型の対応表を追加、一部内容を修正
- 2020/06/19：JSON → Javaオブジェクトへの変換方法を追記、今後の課題を削除
- 2020/07/05：JPAの「Repeated column in mapping for entity」エラーに対応する処理を追加, GitHubのリポジトリを追記

## 参考
### ■VBA
- [ブックのパスを取得する | Excel VBA @Workshop](http://excel.style-mods.net/tips_vba/tips_vba_4_09.htm)
- [あまり知られていないDir関数の特徴 | mougモーグ](https://www.moug.net/tech/exvba/0100048.html)
- [VBA:Dir関数を使用した特定のフォルダ内にある複数のファイルの処理に関するサンプルプログラム集 | SE Life Log - VBAを中心にその他IT備忘録 -](https://selifelog.com/blog-entry-293.html)
- [『Do While FileName <> “”マクロについて』（hiro） | エクセルの学校](http://www.excel.studio-kazu.jp/kw/20171207164726.html)
- [VBAでテキストファイルの読み書きを行う | Excel作業をVBAで効率化](https://vbabeginner.net/vba%E3%81%A7%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E8%AA%AD%E3%81%BF%E6%9B%B8%E3%81%8D%E3%82%92%E8%A1%8C%E3%81%86/)
- [テキストファイルを操作する(開く) | Office TANAKA](http://officetanaka.net/excel/vba/file/file08.htm)
- [テキストファイルの文字列置換方法 | VB初心者友の会 - Q&A掲示板過去ログ](http://www.gizcollabo.jp/vbtomo/log/archive/vbqanda_40587_0.html)
- [VBA で正規表現を使う（RegExp オブジェクト）| ExcelWork.info](https://excelwork.info/excel/vbaregexp/)
- [VBA で正規表現を使う（RegExp オブジェクトのプロパティ）| ExcelWork.info](https://excelwork.info/excel/vbaregexpproperty/)
- [文字列の一部だけを変更したい | すぐ使える正規表現サンプル集](https://hodade.com/seiki/page.php?r_ichibu)

### ■MySQL
- [11.1 Numeric Data Types(version5.7) | MySQL](https://dev.mysql.com/doc/refman/5.7/en/numeric-types.html)

### ■Java
- [プログラムにおける数の扱い方の常識 | atmarkIT](https://www.atmarkit.co.jp/ait/articles/0702/27/news100.html)
- [JPAの「Repeated column in mapping for entity」エラー | Qiita](https://qiita.com/pilot/items/2bb3b4457ac4ea307605)