Skip to content

JavaオブジェクトとJDBC型のパラメータマッピング

uroboroSQLでは、JavaオブジェクトとJDBC間の型変換を自動的に行う仕組みを提供しています。
この機能により、アプリケーション層で使用する型(Java 8 Date and Time API、Optional、Enum、カスタムドメインオブジェクトなど)とデータベース層で使用する型(java.sql.*クラス、プリミティブ型など)を相互に変換できます。

マッピングは2つの方向で行われます:

  1. Javaオブジェクト → JDBC型の変換: バインドパラメータ設定時にBindParameterMapperが使用されます
  2. JDBC型 → Javaオブジェクトの変換: ResultSet取得時にPropertyMapperが使用されます

1. Javaオブジェクト → JDBCオブジェクトの変換(BindParameterMapper)

バインドパラメータを設定する際、BindParameterMapperManagerが適切なBindParameterMapperを選択し、JavaオブジェクトをJDBCが受け入れ可能な型に変換します。

BindParameterMapperの選択順序

  1. ユーザ定義Mapper
    1. ServiceLoaderで登録されたユーザ定義Mapper
    2. BindParameterMapperManager#addMapperで追加されたユーザ定義Mapper
  2. Javaプリミティブ型とラッパークラス他
    1. short, int, long, double, boolean, byte とそのラッパークラス
    2. String, BigDecimal, byte[]
    3. java.sql.*型
  3. 標準Mapper
    1. DateTimeApiParameterMapper
    2. その他の標準Mapper(Date, BigInteger, Optional, Domain, Enum, 配列型)
  4. 変換なし

Mapperの優先度制御

ユーザ定義Mapperは標準Mapperより優先されるため、標準の変換動作をオーバーライドすることができます。
複数のMapperが同じ型を処理できる場合、最初にマッチしたMapperが使用されます。

主要インターフェース

java
public interface BindParameterMapper<T> {
    // 変換対象の型を返す
    Class<T> targetType();

    // 変換可能かどうかを判定
    boolean canAccept(Object object);

    // JDBCが受け入れ可能な型に変換
    Object toJdbc(T original, Connection connection, BindParameterMapperManager parameterMapperManager);
}

標準で提供されるMapper

uroboroSQLは以下のBindParameterMapperを標準で提供しています。

Mapper名標準Mapper登録対象Java型変換後JDBC型説明
DateParameterMapperjava.util.Datejava.sql.Timestampjava.util.DateをTimestampに変換
BigIntegerParameterMapperBigIntegerBigDecimalBigIntegerをBigDecimalに変換
DateTimeApiParameterMapperTemporalAccessor(各種)Timestamp/Date/TimeJava 8 Date and Time APIの各型をSQL型に変換
OptionalParameterMapperOptional<T>Tの変換結果またはnullOptionalの値を取り出して変換
OptionalIntParameterMapperOptionalIntInteger/nullOptionalIntの値を抽出
OptionalLongParameterMapperOptionalLongLong/nullOptionalLongの値を抽出
OptionalDoubleParameterMapperOptionalDoubleDouble/nullOptionalDoubleの値を抽出
DomainParameterMapper@Domain付きクラスドメインの値型ドメインオブジェクトから値を抽出
EnumParameterMapperEnumStringEnum値をtoString()メソッドで文字列に変換
StringArrayParameterMapperString[]java.sql.ArrayString配列をSQL Arrayに変換
IntArrayParameterMapperint[]java.sql.Arrayint配列をSQL Arrayに変換
IntWrapperArrayParameterMapperInteger[]java.sql.ArrayInteger配列をSQL Arrayに変換
LongArrayParameterMapperlong[]java.sql.Arraylong配列をSQL Arrayに変換
LongWrapperArrayParameterMapperLong[]java.sql.ArrayLong配列をSQL Arrayに変換
DoubleArrayParameterMapperdouble[]java.sql.Arraydouble配列をSQL Arrayに変換
DoubleWrapperArrayParameterMapperDouble[]java.sql.ArrayDouble配列をSQL Arrayに変換
EmptyStringToNullParameterMapperStringString/null空文字列をnullに変換
DateToStringParameterMapperjava.util.DateStringyyyyMMdd形式の文字列に変換
LocalDateToStringParameterMapperjava.time.LocalDateStringyyyyMMdd形式の文字列に変換
TimeToStringParameterMapperLocalTime/OffsetTimeStringHHmmss形式の文字列に変換
DateTimeToStringParameterMapperLocalDateTime/OffsetDateTime/ZonedDateTimeStringyyyyMMddHHmmssSSS形式の文字列に変換
SqlTimeToStringParameterMapperjava.sql.TimeStringHHmmss形式の文字列に変換
YearToStringParameterMapperjava.time.YearStringyyyy形式の文字列に変換
YearMonthToStringParameterMapperjava.time.YearMonthStringyyyyMM形式の文字列に変換
MonthDayToStringParameterMapperjava.time.MonthDayStringMMdd形式の文字列に変換
MonthToStringParameterMapperjava.time.MonthStringMM形式(01〜12)の文字列に変換
DayOfWeekToStringParameterMapperjava.time.DayOfWeekString数字形式(1(月曜日)から7(日曜日))の文字列に変換

2. JDBC型 → Javaオブジェクトの変換(PropertyMapper)

ResultSetから値を取得する際、PropertyMapperManagerが適切なPropertyMapperを選択し、JDBC型をアプリケーションで使用するJavaオブジェクトに変換します。

PropertyMapperの選択順序

  1. ユーザ定義Mapper
    1. ServiceLoaderで登録されたユーザ定義Mapper
    2. PropertyMapperManager#addMapperで登録されたユーザ定義Mapper
  2. Javaプリミティブ型とラッパークラス他
    1. short, int, long, double, boolean, byte とそのラッパークラス
    2. String, BigDecimal, byte[]
    3. java.sql.*型
  3. 標準Mapper
    1. DateTimeApiParameterMapper
    2. その他の標準Mapper(BigInteger, Optional, Domain, Enum, 配列型)

Mapperの優先度制御

ユーザ定義Mapperは標準Mapperより優先されるため、標準の変換動作をオーバーライドすることができます。
複数のMapperが同じ型を処理できる場合、最初にマッチしたMapperが使用されます。

主要インターフェース

java
public interface PropertyMapper<T> {
    // 変換可能な型かどうかを判定
    boolean canAccept(Class<?> type);

    // ResultSetから値を取得して型変換
    T getValue(JavaType type, ResultSet rs, int columnIndex, PropertyMapperManager mapperManager) throws SQLException;
}

標準で提供されるMapper

uroboroSQLは以下のPropertyMapperを標準で提供しています。

Mapper名標準Mapper登録変換元JDBC型対象Java型説明
BigIntegerPropertyMapperBigDecimal/Long等BigInteger数値型をBigIntegerに変換
DateTimeApiPropertyMapperTimestamp/Date/Time/StringTemporalAccessor(各種)SQL型やString型をJava 8 Date and Time APIに変換
OptionalPropertyMapper任意Optional<T>Optional型に変換
OptionalIntPropertyMapperInteger/nullOptionalIntOptionalInt型に変換
OptionalLongPropertyMapperLong/nullOptionalLongOptionalLong型に変換
OptionalDoublePropertyMapperDouble/nullOptionalDoubleOptionalDouble型に変換
DomainPropertyMapperドメインの値型@Domain付きクラス値からドメインオブジェクトを生成
EnumPropertyMapperString/IntegerEnum文字列または数値からEnum値に変換
ArrayPropertyMapperjava.sql.Array配列型SQL ArrayをJava配列に変換

主要Mapperの詳細

DateTimeApiParameterMapper / DateTimeApiPropertyMapper

Java 8 Date and Time APIの各型とSQL型の相互変換を行います。

  • Java → JDBC変換(DateTimeApiParameterMapper)
Java型変換後のJDBC型
LocalDatejava.sql.Date
LocalTimejava.sql.Time
OffsetTimejava.sql.Time
LocalDateTimejava.sql.Timestamp
OffsetDateTimejava.sql.Timestamp
ZonedDateTimejava.sql.Timestamp
Instantjava.sql.Timestamp
YearString または Integer
YearMonthString または Integer
MonthDayString または Integer
MonthString または Integer
DayOfWeekString または Integer
EraString または Integer
ChronoLocalDate (JapaneseDateなど)java.sql.Date
  • JDBC → Java変換(DateTimeApiPropertyMapper)

変換元JDBC型のカラム値から指定されたJava型に合わせた型変換を行います。

変換元JDBC型
java.sql.Date
java.sql.Time
java.sql.Timestamp
String(変換可能日時フォーマット
変換可能Java型
LocalDate
LocalTime
OffsetTime
LocalDateTime
OffsetDateTime
ZonedDateTime
Year
YearMonth
MonthDay
Month
DayOfWeek
Era
ChronoLocalDate (JapaneseDateなど)

変換可能日時フォーマット

Java型受け入れ可能なStringフォーマット
LocalDateyyyyMMdd / yyyy-MM-dd20250131 / 2025-01-31
LocalTimeHH:mm / HH:mm:ss / HH:mm:ss.S…(小数1〜9桁)12:34 / 12:34:56 / 12:34:56.789
HHmm / HHmmss / HHmmssS…(小数1〜9桁, 区切りなし)1234 / 123456 / 123456789
LocalDateTime/OffsetDateTime/ZonedDateTimeyyyy-MM-dd'T'HH:mm:ss / yyyy-MM-dd'T'HH:mm:ss.S…(1〜9桁)2025-01-31T12:34:56 / 2025-01-31T12:34:56.789
yyyyMMddHHmmss20250131123456
yyyyMMddHHmmssSSS(ミリ秒)20250131123456789
yyyyMMddHHmmssSSSSSS(マイクロ秒)20250131123456789012
yyyyMMddHHmmssSSSSSSSSS(ナノ秒)20250131123456789012345
Year数値文字列(yyyy)2025
YearMonth数値文字列(yyyyMM)202501
MonthDay数値文字列(MMdd)0131
Month数値文字列(1〜12)1 / 12
DayOfWeek数値文字列(1〜7)1(月)〜7(日)
Era数値文字列(実装依存のコード)例:日本暦のJapaneseEra.of(1) = SHOWA など

使用例

java
// 検索結果格納用Beanクラス
public class ResultBean {
    private LocalDateTime eventDate;  // 検索結果の Timestampカラムの値がLocalDateTimeに変換されて設定
    // ...
}
List<ResultBean> results = agent.query("example/select_by_date")
    .param("targetDate", LocalDateTime.now()) // LocalDateTimeをバインドパラメータとして使用
    .collect(ResultBean.class);

ToStringParameterMapper

古いシステムでは、日付型や日時型の値を 20250131yyyyMMdd形式)や 20250131123456yyyyMMddHHmmss形式)といった文字列として格納することがあります。
このようなケースに対応するため uroboroSQL では日付型や日時型のバインドパラメータを文字列に変換するBindParameterMapperを標準で提供しています。

ToStringParameterMapper には以下の種類があります。

Mapper名対象Java型説明
DateToStringParameterMapperjava.util.DateyyyyMMdd形式の文字列に変換
LocalDateToStringParameterMapperjava.time.LocalDateyyyyMMdd形式の文字列に変換
TimeToStringParameterMapperLocalTime/OffsetTimeHHmmss形式の文字列に変換
DateTimeToStringParameterMapperLocalDateTime/OffsetDateTime/ZonedDateTimeyyyyMMddHHmmssSSS形式の文字列に変換
SqlTimeToStringParameterMapperjava.sql.TimeHHmmss形式の文字列に変換
YearToStringParameterMapperjava.time.Yearyyyy形式の文字列に変換
YearMonthToStringParameterMapperjava.time.YearMonthyyyyMM形式の文字列に変換
MonthDayToStringParameterMapperjava.time.MonthDayMMdd形式の文字列に変換
MonthToStringParameterMapperjava.time.MonthMM形式(01〜12)の文字列に変換
DayOfWeekToStringParameterMapperjava.time.DayOfWeek数字形式(1(月曜日)から7(日曜日))の文字列に変換

ToStringParameterMapper を有効にする場合、後述するユーザ定義Mapperの登録ToStringParameterMapper の登録を行ってください。

TIP

JDBC → Java変換 では DateTimeApiPropertyMapper が 文字列型から日付型や日時型への変換に対応しているため、ユーザ定義PropertyMapperの登録は不要です

EmptyStringToNullParameterMapper

データベースによって NULL空文字("") の扱いが異なります。

  • NULL空文字 を区別する : PostgreSQL、MySQL、SQL Server
  • 空文字NULL として扱う : Oracle

EmptyStringToNullParameterMapper を使用するとバインドパラメータとして空文字列が設定された際、それを自動的に NULL に変換することができます。 これにより、データベース間の差異を吸収し、一貫した動作を実現できます。

使用例

java
// EmptyStringToNullParameterMapperを登録
config.getExecutionContextProvider()
  .addBindParameterMapper(new EmptyStringToNullParameterMapper());

// 空文字列がnullに変換される
agent.update("user/update_user")
  .param("middleName", "")  // 内部的にnullとしてバインドされる
  .count();

注意

EmptyStringToNullParameterMapper は標準では登録されていません。必要に応じてユーザ定義Mapperの登録で登録してください。

EnumParameterMapper / EnumPropertyMapper

Enum型とデータベース値の相互変換を行います。

使用例

java
public enum Status {
    ACTIVE("01"), INACTIVE("02"), DELETED("03");

    private final String code;

    private Status(String code) {
      this.code = code;
    }

    @Override
    public String toString() {
      return code;
    }
}

// 検索結果を格納するBean
public class User {
    private Status status;  // "01"という文字列をStatus#toString()と比較し、一致するStatus.ACTIVEに変換される
    // ...
}

// バインドパラメータとして使用
List<User> users = agent.query("user/select_by_status")
    .param("status", Status.ACTIVE)  // "01"という文字列としてバインドされる
    .collect(User.class);

DomainParameterMapper / DomainPropertyMapper

@Domainアノテーションが付いたドメインオブジェクトの変換を行います。

TIP

@Domain アノテーションの記述方法については Entityアノテーション > @Domain を参照してください。

使用例

java
// ドメインオブジェクトの定義
@Domain(valueType = String.class)
public class UserId {
    private final String value;

    public UserId(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

// 検索結果を格納するBean
public class User {
    private UserId userId;  // VARCHAR値がUserIdオブジェクトに変換される
    // ...
}

// バインドパラメータとして使用
List<User> users agent.query("user/select_by_user_id")
    .param("userId", new UserId("USER001"))  // 内部的に"USER001"がバインドされる
    .collect(User.class);

OptionalParameterMapper / OptionalIntParameterMapper / OptionalLongParameterMapper / OptionalDoubleParameterMapper / OptionalPropertyMapper / OptionalIntPropertyMapper / OptionalLongPropertyMapper / OptionalDoublePropertyMapper

Optional型 / OptionalInt型 / OptionalLong型 / OptionalDouble型 とnull値の相互変換を行います。

使用例

java
// 検索結果を格納するBean
public class User {
    private Optional<String> middleName;  // NULLならOptional.empty()、値があればOptional.of(値)
    // ...
}

List<User> users = null;

// バインドパラメータとして使用
Optional<String> optionalName = Optional.of("John");
users = agent.query("user/select_by_name")
    .param("name", optionalName)  // "John"がバインドされる
    .collect(User.class);

Optional<String> emptyName = Optional.empty();
users = agent.query("user/select_by_name")
    .param("name", emptyName)  // nullがバインドされる
    .collect(User.class);

ユーザ定義Mapperの作成

以下のように独自の型を作成し、この型に対して独自の型変換が必要な場合、ユーザ定義Mapperを作成することができます。

java
// 電話番号型
public class PhoneNumber {
    private String countryCode;
    private String number;

    public String toFullNumber() {
        return countryCode + "-" + number;
    }
}

ユーザ定義BindParameterMapperの作成

java
// ユーザ定義BindParameterMapper
public class PhoneNumberParameterMapper implements BindParameterMapper<PhoneNumber> {

    @Override
    public Class<PhoneNumber> targetType() {
        return PhoneNumber.class;
    }

    @Override
    public Object toJdbc(PhoneNumber original, Connection connection, BindParameterMapperManager parameterMapperManager) {
        if (original == null) {
            return null;
        }
        return original.toFullNumber();  // 文字列に変換
    }
}

ユーザ定義PropertyMapperの作成

java
// ユーザ定義PropertyMapper
public class PhoneNumberPropertyMapper implements PropertyMapper<PhoneNumber> {
    @Override
    public boolean canAccept(Class<?> type) {
        return PhoneNumber.class.equals(type);
    }

    @Override
    public PhoneNumber getValue(JavaType type, ResultSet rs, int columnIndex, PropertyMapperManager mapperManager) throws SQLException {
        String fullNumber = rs.getString(columnIndex);
        if (fullNumber == null) {
            return null;
        }
        // 文字列をパースしてPhoneNumberオブジェクトを生成
        String[] parts = fullNumber.split("-");
        return new PhoneNumber(parts[0], parts[1]);
    }
}

ユーザ定義Mapperの登録

ユーザ定義Mapperの登録は、以下のいづれかの方法で行います。

1. ServiceLoaderを使用した自動登録

META-INF/servicesディレクトリに以下のファイルを作成することで、自動的にMapperが登録されます。

BindParameterMapperの登録

ファイル名: META-INF/services/jp.co.future.uroborosql.parameter.mapper.BindParameterMapper

com.example.mapper.PhoneNumberParameterMapper

PropertyMapperの登録

ファイル名: META-INF/services/jp.co.future.uroborosql.mapping.mapper.PropertyMapper

com.example.mapper.PhoneNumberPropertyMapper

2. SqlConfig生成時に登録

java
SqlConfig config = UroboroSQL.builder("jdbc:h2:mem:test", "sa", "")
    .build();

// BindParameterMapperの登録
config.getExecutionContextProvider()
    .addBindParameterMapper(new PhoneNumberParameterMapper());

// PropertyMapperの登録
config.getEntityHandler()
    .addPropertyMapper(new PhoneNumberPropertyMapper());