LoginSignup
1
7

More than 5 years have passed since last update.

Java 8 用 O/R マッピング・ライブラリ Lightsleep の柔軟なデータ型変換の仕組み

Last updated at Posted at 2017-02-19

Lightsleep に関しては、紹介記事を Qiita に投稿していますので、下記をご覧ください。


Java Runtime と JDBC ドライバーだけで動作する O/R マッピング・ライブラリ Lightsleep の紹介

Lightsleep には、柔軟なデータ型変換を行う仕組みがあり、それを内部で使用しています。Lightsleep を使用する上で、その詳細を知る事は必須ではありませんが、どのような変換が行われるかを知っておくと役に立つと思います。

データ型変換の使用箇所

主に以下の2箇所で使用します。

  1. SQL を生成するために、エンティティ・オブジェクトのプロパティ値をリテラル文字列に変換する所

    例えば、

    • "Yukari's apples"'Yukari''s apples'
    • 2017/2/12 (java.sql.Date) ➔ DATE'2017-02-17'
  2. DBから取得した値をエンティティ・オブジェクトに設定する所

    例えば、

    • BigDecimal ➔ Integer (カラム型が NUMBER(9) / Oracle)
    • Long ➔ java.sql.Date (カラム型が BIGINT)

上記の 2 が本来のデータ型変換の目的で、格納する値とそれを格納する変数の型が異なる場合でも格納できるようにします。

1 は、この仕組みを利用して SQL のリテラル文字列の生成を行っています。通常の文字列と変換内容が異なるため、変換先データ型は SqlString クラスとしています。データベース・ハンドラ毎にデータ型変換マップを持つ事で、DBMS 固有の SQL の生成を可能にしています。

データ型変換を行うクラス

org.lightsleep.helper.TypeConverter がデータ型変換を行うクラスです。Lightsleep を利用する場合に、このクラスを直接利用する事は、あまりないと思いますが簡単に説明します。

このクラスには以下の2つのコンストラクタがあります。

  1. TypeConverter(Class<ST> sourceType, Class<DT> destinType, Function<ST, DT> function)
  2. <MT> TypeConverter(TypeConverter<ST, MT> typeConverter1, TypeConverter<MT, DT> typeConverter2)

コンストラクタ1の引数は、変換元のデータ型(sourceType)、変換先のデータ型(destinType)、変換を行う Function オブジェクト(function)です。function は、ラムダ式で記述します。

Long->java.sql.Date
    new TypeConverter<>(Long.class, Date.class, object -> new Date(object))

コンストラクタ2の引数は、他の TypeConverter オブジェクト2つです。

java.util.Date->Integer
    new TypeConverter<>(
        TypeConverter.get(typeConverterMap, java.util.Date.class, Long.class),
        TypeConverter.get(typeConverterMap, Long.class, Integer.class)
    )

上記例での static TypeConverter.get メソッド は、マップに登録してある TypeConverter オブジェクトを取得するメソッドです。この TypeConverter オブジェクトの Function オブジェクトは、typeConverter1.function.andThen(typeConverter2.function)` (合成関数) になります。

TypeConverter オブジェクトは、変換元データ型と変換先データ型から生成したキーを持っているため、マップ登録の際は、Map#put ではなく、static TypeConverter#put メソッドを使用します。

Long->java.sql.Date
TypeConverter.put(typeConverterMap,
    new TypeConverter<>(Long.class, Date.class, object -> new Date(object))
);
java.util.Date->Integer
TypeConverter.put(typeConverterMap,
    new TypeConverter<>(
        TypeConverter.get(typeConverterMap, java.util.Date.class, Long.class),
        TypeConverter.get(typeConverterMap, Long.class, Integer.class)
    )
);

データ型の変換処理は、static TypeConverter#convert メソッドで行います。このメソッドは以下の手順で変換処理を行います。

  1. 引数の変換元オブジェクトの型と変換先データ型からマップのキーを生成。
  2. 引数のデータ変換型用マップから TypeConverter オブジェクトを取得。
  3. TypeConverter オブジェクトが持つ Function オブジェクトの apply メソッドをコールしてデータ型を変換。

ただし引数の変換元オブジェクトが null または変換先データ型にキャスト可能である場合は、変換処理を行わずにそのまま変換元オブジェクトを返します。

2 では、static TypeConverter#get が使用されますが、指定の変換元データ型と変換先データ型の組み合わせで見つからない場合は、変換元データ型をその型のインタフェースやスーパークラスで見つからないを試します。
見つかった場合は、その変換用関数を使用できるので、次回の変換処理で単純に TypeConverter オブジェクト検索できるように見つかったデータ型と変換先データ型からキーを作成してマップに登録します。
最終的に変換可能な TypeConverter オブジェクト見つからない場合は、ConvertException をスローします。

TypeConverter オブジェクトの格納場所

TypeConverter オブジェクトは、以下のクラスの Map<String, TypeConverter<?, ?>> 型の変数に格納されています。

  1. TypeConverter クラス
  2. Standard クラスおよびそのサブクラス (MySQL, Oracle, PostgreSQL, SQLite, SQLServer)

1 は静的変数のマップを持ち、2 はインスタンス変数のマップを持っています。

Standard クラスおよびそのサブクラスは、シングルトン・クラスで、クラス毎に単一のオブジェクトしか存在しないため、1 と同様にクラス毎に1つのデータ型変換マップが存在する事になります。

データ型変換処理で通常使用されるのは、2 の方で、そのうちのどれが使用されるかは、lightsleep.propeties の Database プロパティの指定で決まります。

各クラスのデータ型変換マップに格納される TypeConverter オブジェクト

TypeConverter クラス

このクラスのデータ型変換マップには、O/R マッピングとは無関係な、一般的なデータ型変換を行う以下の TypeConverter オブジェクトが格納されています。

変換元データ型 変換先データ型 変換内容
Byte Boolean 0 ➔ false
1 ➔ true
その他の場合 ConvertException をスロー
Short
Integer
Long
Float
Double
BigDecimal
Character '0' ➔ false
'1' ➔ true
その他の場合 ConvertException をスロー
String "0" ➔ false
"1" ➔ true
その他の場合 ConvertException をスロー
Boolean Byte false ➔ 0
true ➔ 1
Short 範囲外の場合 ConvertException をスロー
Integer
Long
Float
Double
BigDecimal
Character
String 非数値または範囲外の場合 ConvertException をスロー
Boolean Short false ➔ 0
true ➔ 1
Byte
Integer 範囲外の場合 ConvertException をスロー
Long
Float
Double
BigDecimal
Character
String 非数値または範囲外の場合 ConvertException をスロー
Boolean Integer false ➔ 0
true ➔ 1
Byte
Short
Long 範囲外の場合 ConvertException をスロー
Float
Double
BigDecimal
Character
String 非数値または範囲外の場合 ConvertException をスロー
java.util.Date 範囲外の場合 ConvertException をスロー
Boolean Long false ➔ 0
true ➔ 1
Byte
Short
Integer
Float 範囲外の場合 ConvertException をスロー
Double
BigDecimal
Character
String 非数値または範囲外の場合 ConvertException をスロー
java.util.Date long 値を取得
Boolean Float false ➔ 0.0F
true ➔ 1.0F
Byte
Short
Integer
Long
Double
BigDecimal
Character
String 非数値の場合 ConvertException をスロー
Boolean Double false ➔ 0.0D
true ➔ 1.0D
Byte
Short
Integer
Long
Float
BigDecimal
Character
String 非数値の場合 ConvertException をスロー
Boolean BigDecimal false ➔ BigDecimal.ZERO
true ➔ BigDecimal.ONE
Byte
Short
Integer
Long
Float
Double
Character
String 非数値の場合 ConvertException をスロー
Boolean Character false ➔ '0'
true ➔ '1'
Byte
Short
Integer 範囲外の場合 ConvertException をスロー
Long
Float
Double
BigDecimal
String String の長さが1以外の場合 ConvertException をスロー
BigDecimal String toPlainString() で変換
java.uitl.Date "yyyy-MM-dd"
java.sql.Date
Time "HH:mm:ss"
Timestamp "yyyy-MM-dd HH:mm:ss.SSS"
Object toString() で変換
Integer java.util.Date
Long
BigDecimal Long への変換で範囲外の場合 ConvertException をスロー
String "yyyy-MM-dd" ➔ String
フォーマット不正の場合 ConvertException をスロー
Integer java.sql.Date
Long
BigDecimal Long への変換で範囲外の場合 ConvertException をスロー
java.util.Date
String "yyyy-MM-dd" ➔ String
フォーマット不正の場合 ConvertException をスロー
Integer Time
Long
BigDecimal Long への変換で範囲外の場合 ConvertException をスロー
java.util.Date
String "HH:mm:ss" ➔ String
フォーマット不正の場合 ConvertException をスロー
Long Timestamp
Integer
BigDecimal Long への変換で範囲外の場合 ConvertException をスロー
java.util.Date
String "yyyy-MM-dd HH:mm:ss" または
"yyyy-MM-dd HH:mm:ss.SSS" ➔ String
フォーマット不正の場合 ConvertException をスロー
Enum Byte ordinal() で変換
範囲外の場合 ConvertException をスロー
Short
Integer ordinal() で変換
Long

Standard クラス

このクラスのデータ型変換マップには、TypeConverter クラスが持つすべての TypeConverter オブジェクトに、以下の O/R マッピングに固有の TypeConverter オブジェクトが追加されます。

変換元データ型 変換先データ型 変換内容
Clob String 長さが Integer.MAX_VALUE を超える場合 ConvertException をスロー
内容の取得時に SQLException がスローされた場合 ConvertException をスロー
Blob byte[]
java.sql.Array boolean[] 各要素を TypeConverter で配列要素のデータ型に変換
byte[]
short[]
int[]
long[]
float[]
double[]
BigDecimal[]
String[]
java.util.Date[]
java.sql.Date[]
Time[]
Timestamp[]
Boolean SqlString false ➔ FALSE
true ➔ TRUE
Object '...'
Character
BigDecimal
String '...'
長い場合は ? (SQLパラメータ)
java.util.Date DATE'yyyy-MM-dd'
java.sql.Date
Time TIME'HH:mm:ss'
Timestamp TIMESTAMP'yyyy-MM-dd HH:mm:ss.SSS'
Enum '...' (toString() で変換)
byte[] X'...'
長い場合は ? (SQLパラメータ)
boolean[] ARRAY[x,y,z,...]
各要素を TypeConverter で SqlString に変換
char[]
byte[][]
short[]
int[]
long[]
float[]
double[]
BigDecimal[]
String[]
java.util.Date[]
java.sql.Date[]
Time[]
Timestamp[]
Iterable (x,y,z,...)
各要素を TypeConverter で SqlString に変換

MySQL クラス

このクラスのデータ型変換マップには、Standard クラスが持つすべての TypeConverter オブジェクトに、MySQL に固有の TypeConverter オブジェクトが追加されます。

変換元データ型 変換先データ型 変換内容
Boolean SqlString false ➔ 0
true ➔ 1
String '...'
制御文字はエスケープ・シーケンスに変換
長い場合は ? (SQLパラメータ)

Oracle クラス

このクラスのデータ型変換マップには、Standard クラスが持つすべての TypeConverter オブジェクトに、Oracle Database に固有の TypeConverter オブジェクトが追加されます。

変換元データ型 変換先データ型 変換内容
Boolean SqlString false ➔ 0
true ➔ 1
String '...'
制御文字は '...'||CHR(n)||'...' に変換
長い場合は ? (SQLパラメータ)
Time TO_TIMESTAMP('1970-01-01 HH:mm:ss','YYYY-MM-DD HH24:MI:SS.FF3')
byte[] ? (SQLパラメータ)
oracle.sql.TIMESTAMP java.util.Date 値の取得時に SQLException がスローされた場合 ConvertException をスロー
java.sql.Date
java.sql.Time
java.sql.Timestamp

PostgreSQL クラス

このクラスのデータ型変換マップには、Standard クラスが持つすべての TypeConverter オブジェクトに、PostgreSQL に固有の TypeConverter オブジェクトが追加されます。

変換元データ型 変換先データ型 変換内容
String SqlString '...'
制御文字は エスケープ・シーケンスに変換
長い場合は ? (SQLパラメータ)
byte[] E'\\x...'
長い場合は ? (SQLパラメータ)

SQLite クラス

このクラスのデータ型変換マップには、Standard クラスが持つすべての TypeConverter オブジェクトに、SQLite に固有の TypeConverter オブジェクトが追加されます。

変換元データ型 変換先データ型 変換内容
Boolean SqlString false ➔ 0
true ➔ 1
java.util.Date 'yyyy-MM-dd'
java.sql.Date
Time 'HH:mm:ss'
Timestamp 'yyyy-MM-dd HH:mm:ss.SSS'
byte[] ? (SQLパラメータ)

SQLServer クラス

このクラスのデータ型変換マップには、Standard クラスが持つすべての TypeConverter オブジェクトに、Microsoft SQL Server に固有の TypeConverter オブジェクトが追加されます。

変換元データ型 変換先データ型 変換内容
Boolean SqlString false ➔ 0
true ➔ 1
java.sql.Date CAST('yyyy:MM:dd' AS DATE)
Time CAST('HH:mm:ss' AS DATE)
Timestamp CAST('yyyy-MM-dd HH:mm:ss.SSS' AS DATETIME2)
String '...'
制御文字は '...'+CHAR(n)+'...' に変換
長い場合は? (SQLパラメータ)
byte[] ? (SQLパラメータ)
1
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
7