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

  • 22
    いいね
  • 23
    コメント

Lightsleep は、軽量の O/R マッピング・ライブラリで、Java 8 で利用できます。Java 7 以前には対応していません。
また Java Persistence API (JPA) との互換性はありません。

更新履歴

  • 2017-01-14 - 3-1-15. SELECT COUNT(*) の例、SQL の例 (PostgrSQL, Oracle, SQL Server) を追加
  • 2017-01-29 - version 1.7.0 対応 (SQLite)
  • 2017-02-11 - version 1.8.0 対応 (@ColumnType, @ColumnTypeProperty)
  • 2017-02-18 - build.gradle の例の SQL Server の JDBC ドライバの記述内容を修正
  • 2017-02-26 - version 1.8.2 対応 (SQLite の場合も FOR UPDATE 句を生成して実行時にエラーとなるように変更)
  • 2017-04-16 - ダウンロードをリンクに変更

特徴

  • Java 8 で追加された機能 (関数型インタフェース、Optional クラス) を使用した API 。
  • メソッド名を SQL の予約語に似せてあるため、直観的に理解しやすい。
  • Java Runtime と JDBC ドライバー以外に依存するライブラリがないため、バッチ処理にも使用しやすい。
  • XML ファイル等によるマッピング定義ファイルは不要。
  • 大規模なライブラリではないため、学習が比較的容易。

リンク

build.gradle

記述例:

build.gradle
apply plugin: 'java'

repositories {
    jcenter()
}

dependencies {
    compile 'org.lightsleep:lightsleep:1.+'
}

ロギング・ライブラリ、JDBC ドライバー、コネクション・プール・ライブラリを追加した場合の記述例:

build.gradle
apply plugin: 'java'

repositories {
    jcenter()
}

dependencies {
    compile 'org.lightsleep:lightsleep:1.+'

    // ロギング・ライブラリを使用する場合
    runtime 'log4j:log4j:1.+'                         // Log4j
    runtime 'org.apache.logging.log4j:log4j-core:2.+' // Log4j2
    runtime 'org.slf4j:slf4j-api:1.+'                 // SL4J

    // 以下のいずれかまたは他の JDBC ドライバー
    runtime 'mysql:mysql-connector-java:5.+'          // MySQL
    runtime 'com.hynnet:oracle-driver-ojdbc:12.+'     // Oracle
    runtime 'org.postgresql:postgresql:9.+'           // PostgreSQL
    runtime 'org.xerial:sqlite-jdbc:3.+'              // SQLite
//  runtime 'com.microsoft.sqlserver:sqljdbc4:4.+'    // SQLServer
    runtime 'com.microsoft.sqlserver:mssql-jdbc:6.1.0.jre8' // SQLServer

    // コネクション・プール・ライブラリを使用する場合
    runtime 'com.mchange:c3p0:0.+'                    // C3p0
    runtime 'org.apache.commons:commons-dbcp2:2.+'    // Dbcp
    runtime 'com.zaxxer:HikariCP:2.+'                 // HikariCP
    runtime 'org.apache.tomcat:tomcat-jdbc:8.+'       // TomcatCP
}

使用方法

1. エンティティ・クラスの作成

テータベースの各テーブルに対応するエンティティ・クラス (データの入れ物) を定義します。

定義例:

Contact.java
package org.lightsleep.example.article.entity;

import java.sql.Date;
import org.lightsleep.entity.Key;

public class Contact {
    @Key
    public int    id;
    public String familyName;
    public String givenName;
    public Date   birthday;
}
Phone.java
package org.lightsleep.example.article.entity;

import org.lightsleep.entity.Key;

public class Phone {
    @Key
    public int    contactId;
    public short  childIndex;
    public String label;
    public String content;
}

上記例では、public フィールドを定義していますが、private フィールドと public getter/setter メソッドの組み合わせても OK です。getter メソッドは、get プレフィックス以外に is またはプレフィックスなしでも可能です。また setter メソッドは、set プレフィックス付きまたはプレフィックスなしに対応しています。

private int id;
public int getId() {return id;}
  // or
public int id() {return id;}

public void setId(int id) {this.id = id;}
  // or
public void id(int id) {this.id = id;}

クラス名と関連するテーブル名が異なる場合は、@Table をクラスに付けます。

import org.lightsleep.entity.Table;

@Table("CONTACTS")
public class Contact {

プライマリ・キーのカラムに対応するフィールドには @Key を付けます(複数可)。

カラム名とフィールド名が異なる場合は、@Column をフィールドに付けます。

Contact.java
import org.lightsleep.entity.Column;
    ...
    @Column("FAMILY_NAME")
    public String familyName;

カラムの型とフィールドの型が異なる種類の場合は、@ColumnType をフィールドに付けます。(since 1.8.0)

Contact.java
import org.lightsleep.entity.ColumnType;
    ...
    @ColumnType(Long.class)
    public Date birthday;

非static なフィールドは、自動的にカラムと対応付けされるため、カラムと関連しないフィールドには @NonColumn を付けます。

Contact.java
import java.util.ArrayList;
import java.util.List;
import org.lightsleep.entity.NonColumn;
    ...
    @NonColumn
    public List<Phone> phones = new ArrayList<>();

SQL にフィールドの値ではなく式を指定する場合は、@Select, @Insert, @Update を付けます。

また SQL に使用しない事を指定する場合は、@NonSelect, @NonInsert, @NonUpdateを付けます。

Contact.java
import java.sql.Timestamp;
import org.lightsleep.entity.Insert;
import org.lightsleep.entity.NonUpdate;
import org.lightsleep.entity.Update;
    ...
    @Insert("0")
    @Update("{updateCount}+1")
    public int updateCount;

    @Insert("CURRENT_TIMESTAMP")
    @NonUpdate
    public Timestamp createdTime;

    @Insert("CURRENT_TIMESTAMP")
    @Update("CURRENT_TIMESTAMP")
    public Timestamp updatedTime;

以下のアノテーションがあります。

アノテーション名 指定する内容 付与する対象
@Table テーブル名 クラス
@Key プライマリ・キーに対応 フィールド
@Column カラム名 フィールド
@ColumnType(since 1.8.0) カラムの型 フィールド
@NonColumn カラムに関連しない フィールド
@NonSelect SELECT SQL に使用しない フィールド
@NonInsert INSERT SQL に使用しない フィールド
@NonUpdate UPDATE SQL に使用しない フィールド
@Select SELECT SQL で使用する式 フィールド
@Insert INSERT SQL で使用する式 フィールド
@Update UPDATE SQL で使用する式 フィールド
@KeyProperty プライマリ・キーに対応 クラス
@ColumnProperty カラム名 クラス
@ColumnTypeProperty(since 1.8.0) カラムの型 クラス
@NonColumnProperty カラムに関連しない クラス
@NonSelectProperty SELECT SQL に使用しない クラス
@NonInsertProperty INSERT SQL に使用しない クラス
@NonUpdateProperty UPDATE SQL に使用しない クラス
@SelectProperty SELECT SQL で使用する式 クラス
@InsertProperty INSERT SQL で使用する式 クラス
@UpdateProperty UPDATE SQL で使用する式 クラス

@XxxxxProperty は、スーパークラスで定義されているフィールドに対して指定する場合に使用します。
同一アノテーションを1つのクラスに複数付与できます。

2. lightsleep.properties の定義

定義例:

lightsleep.properties
Logger             = Log4j2
Database           = PostgreSQL
ConnectionSupplier = Dbcp
url                = jdbc:postgresql://postgresqlserver/article
username           = article
password           = _article_
initialSize        = 10
maxTotal           = 100

Logger は、ログを出力する方法の指定で、以下から選択します。

指定内容 使用するロギング・ライブラリ ログ・レベル 定義ファイル
Jdk Java Runtime logging.properties に定義
Log4j Log4j 1.x.x log4j.properties または log4j.xml に定義
Log4j2 Log4j 2.x.x log4j2.xml に定義
SLF4J SLF4J 対象とするロギング・ライブラリ実装に依存
Std$Out$Trace System.out に出力 trace
Std$Out$Debug 同上 debug
Std$Out$Info 同上 info
Std$Out$Warn 同上 warn
Std$Out$Error 同上 error
Std$Out$Fatal 同上 fatal
Std$Err$Trace System.err に出力 trace
Std$Err$Debug 同上 debug
Std$Err$Info 同上 info
Std$Err$Warn 同上 warn
Std$Err$Error 同上 error
Std$Err$Fatal 同上 fatal

指定がない場合は、Std$Out$Info が選択されます。

Database は、対象とする DBMS の指定で、以下から選択します。

  • MySQL
  • Oracle
  • PostgreSQL
  • SQLite
  • SQLServer

上記以外の DBMS を使用する場合は、指定しないか Standard を指定します。
ただしその場合は、DBMS 固有の機能は使用できません。

ConnectionSupplier は、使用するコネクション・サプライヤー (コネクション・プール等) の指定で以下から選択します。

指定内容 コネクション・サプライヤー
C3p0 c3p0
Dbcp Apache Commons DBCP
HikariCP HikariCP
TomcatCP Tomcat JDBC Connection Pool
Jndi Java Naming and Directory Interface (JNDI) (Tomcat の場合)
Jdbc DriverManager#getConnection(String url, Properties info) メソッド

上記 lightsleep.properties の定義例の ConnectionSupplier より下 (url ~ maxTotal) は、各コネクション・サプライヤーに渡す定義内容です。

定義例 (Jdk / MySQL / C3p0):

lightsleep.properties
Logger             = Jdk
Database           = MySQL
ConnectionSupplier = C3p0
url                = jdbc:mysql://mysql57/article
user               = article
password           = _article_

url, user, password 以外の定義は、c3p0.properties または c3p0-config.xml に記述します。

定義例 (Log4j / Oracle / Dbcp):

lightsleep.properties
Logger             = Log4j
Database           = Oracle
ConnectionSupplier = Dbcp
url                = jdbc:oracle:thin:@oracle121:1521:article
username           = article
password           = _article_
initialSize        = 10
maxTotal           = 100
   ...

定義例 (Log4j2 / PostgreSQL / HikariCP):

lightsleep.properties
Logger             = Log4j2
Database           = PostgreSQL
ConnectionSupplier = HikariCP
jdbcUrl            = jdbc:postgresql://postgresql95/article
username           = article
password           = _article_
minimumIdle        = 10
maximumPoolSize    = 100
   ...

定義例 (SLF4J / SQLServer / TomcatCP):

lightsleep.properties
Logger             = SLF4J
Database           = SQLServer
ConnectionSupplier = TomcatCP
url                = jdbc:sqlserver://sqlserver13;database=article
username           = article
password           = _article_
initialSize        = 10
maxActive          = 100
   ...

定義例 (Log4j / MySQL / Jndi):

lightsleep.properties
Logger             = Log4j
Database           = MySQL
ConnectionSupplier = Jndi
dataSource         = jdbc/example

定義例 (Std$Out$Debug / SQLite / Jdbc):

lightsleep.properties
Logger             = Std$Out$Debug
Database           = SQLite
ConnectionSupplier = Jdbc
url                = jdbc:sqlite:C:/sqlite/test

3. データベース・アクセス

Transaction.execute メソッドの実行が1つのトランザクションの実行に相当します。
トランザクションの内容は、引数 transaction (ラムダ式) で定義します。
ラムダ式は、Transaction.executeBody メソッドの内容に相当し、このメソッドの引数は、Connection です。

Java
import org.lightsleep.Sql;
import org.lightsleep.Transaction;
import org.lightsleep.example.article.entity.Contact;
    ...
    // トランザクション定義例
    Transaction.execute(connection -> {
        // トランザクション内容開始
        new Sql<>(Contact.class).insert(connection, contact);
        ...
        // トランザクション内容終了
    });

トランザクション中に例外がスローされた場合は、Transaction.rollback メソッドが実行され、
そうでなければ Transaction.commit メソッドが実行されます。

以下 Java ソースの記述例と実行される SQL です。

3-1. SELECT

3-1-1. SELECT 1行 / 式条件

Java
Transaction.execute(connection -> {
    Optional<Contact> contactOpt = new Sql<>(Contact.class)
        .where("{id}={}", 1)
        .select(connection);
});
SQL
-- 各DBMS共通
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE id=1

3-1-2. SELECT 1行 / エンティティ条件

Java
Contact contact = new Contact();
contact.id = 1;
Transaction.execute(connection -> {
    Optional<Contact> contactOpt = new Sql<>(Contact.class)
        .where(contact)
        .select(connection);
});
SQL
-- 各DBMS共通
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE id=1

3-1-3. SELECT 複数行 / 式条件

Java
List<Contact> contacts = new ArrayList<Contact>();
Transaction.execute(connection ->
    new Sql<>(Contact.class)
        .where("{familyName}={}", "Apple")
        .select(connection, contacts::add)
);
SQL
-- 各DBMS共通
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE familyName='Apple'

3-1-4. SELECT サブクエリ条件

Java
List<Contact> contacts = new ArrayList<Contact>();
Transaction.execute(connection ->
    new Sql<>(Contact.class, "C")
        .where("EXISTS",
            new Sql<>(Phone.class, "P")
                .where("{P.contactId}={C.id}")
        )
        .select(connection, contacts::add)
);
SQL
-- 各DBMS共通
SELECT C.id AS C_id, C.familyName AS C_familyName, C.givenName AS C_givenName, C.birthday AS C_birthday, C.updateCount AS C_updateCount, C.createdTime AS C_createdTime, C.updatedTime AS C_updatedTime FROM Contact C WHERE EXISTS (SELECT * FROM Phone P WHERE P.contactId=C.id)

3-1-5. SELECT 式条件 / AND

Java
List<Contact> contacts = new ArrayList<Contact>();
Transaction.execute(connection ->
    new Sql<>(Contact.class)
        .where("{familyName}={}", "Apple")
        .and  ("{givenName}={}", "Akane")
        .select(connection, contacts::add)
);
SQL
-- 各DBMS共通
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE (familyName='Apple' AND givenName='Akane')

3-1-6. SELECT 式条件 / OR

Java
List<Contact> contacts = new ArrayList<Contact>();
Transaction.execute(connection ->
    new Sql<>(Contact.class)
        .where("{familyName}={}", "Apple")
        .or   ("{familyName}={}", "Orange")
        .select(connection, contacts::add)
);
SQL
-- 各DBMS共通
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE (familyName='Apple' OR familyName='Orange')

3-1-7. SELECT 式条件 / (A AND B) OR (C AND D)

Java
List<Contact> contacts = new ArrayList<Contact>();
Transaction.execute(connection ->
    new Sql<>(Contact.class)
        .where(Condition
            .of ("{familyName}={}", "Apple")
            .and("{givenName}={}", "Akane")
        )
        .or(Condition
            .of ("{familyName}={}", "Orange")
            .and("{givenName}={}", "Setoka")
        )
        .select(connection, contacts::add)
);
SQL
-- 各DBMS共通
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE ((familyName='Apple' AND givenName='Akane') OR (familyName='Orange' AND givenName='Setoka'))

3-1-8. SELECT カラムの選択

Java
List<Contact> contacts = new ArrayList<Contact>();
Transaction.execute(connection ->
    new Sql<>(Contact.class)
        .where("{familyName}={}", "Apple")
        .columns("familyName", "givenName")
        .select(connection, contacts::add)
);
SQL
-- 各DBMS共通
SELECT familyName, givenName FROM Contact WHERE familyName='Apple'

3-1-9. SELECT GROUP BY, HAVING

Java
List<Contact> contacts = new ArrayList<Contact>();
Transaction.execute(connection ->
    new Sql<>(Contact.class, "C")
        .columns("familyName")
        .groupBy("{familyName}")
        .having("COUNT({familyName})>=2")
        .select(connection, contacts::add)
);
SQL
-- 各DBMS共通
SELECT MIN(C.familyName) AS C_familyName FROM Contact C GROUP BY C.familyName HAVING COUNT(C.familyName)>=2

3-1-10. SELECT ORDER BY, OFFSET, LIMIT

Java
List<Contact> contacts = new ArrayList<Contact>();
Transaction.execute(connection ->
    new Sql<>(Contact.class)
        .orderBy("{familyName}")
        .orderBy("{givenName}")
        .orderBy("{id}")
        .offset(10).limit(5)
        .select(connection, contacts::add)
);
SQL
-- MySQL, PostgreSQL
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact ORDER BY familyName ASC, givenName ASC, id ASC LIMIT 5 OFFSET 10

-- Oracle, SQLite, SQL Server / 取得時に読み飛ばす
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact ORDER BY familyName ASC, givenName ASC, id ASC

3-1-11. SELECT FOR UPDATE

Java
Transaction.execute(connection -> {
    Optional<Contact> contactOpt = new Sql<>(Contact.class)
        .where("{id}={}", 1)
        .forUpdate()
        .select(connection);
});
SQL
-- MySQL, Oracle, PostgreSQL, SQLite
-- SQLite は FOR UPDATE を未サポートのためエラーになる
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE id=1 FOR UPDATE

-- SQL Server
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WITH (ROWLOCK,UPDLOCK) WHERE id=1

3-1-12. SELECT 内部結合

Java
List<Contact> contacts = new ArrayList<>();
List<Phone> phones = new ArrayList<>();
Transaction.execute(connection ->
    new Sql<>(Contact.class, "C")
        .innerJoin(Phone.class, "P", "{P.contactId}={C.id}")
        .where("{C.id}={}", 1)
        .<Phone>select(connection, contacts::add, phones::add)
);
SQL
-- 各DBMS共通
SELECT C.id AS C_id, C.familyName AS C_familyName, C.givenName AS C_givenName, C.birthday AS C_birthday, C.updateCount AS C_updateCount, C.createdTime AS C_createdTime, C.updatedTime AS C_updatedTime, P.contactId AS P_contactId, P.childIndex AS P_childIndex, P.label AS P_label, P.content AS P_content FROM Contact C INNER JOIN Phone P ON P.contactId=C.id WHERE C.id=1

3-1-13. SELECT 左外部結合

Java
List<Contact> contacts = new ArrayList<>();
List<Phone> phones = new ArrayList<>();
Transaction.execute(connection ->
    new Sql<>(Contact.class, "C")
        .leftJoin(Phone.class, "P", "{P.contactId}={C.id}")
        .where("{C.familyName}={}", "Apple")
        .<Phone>select(connection, contacts::add, phones::add)
);
SQL
-- 各DBMS共通
SELECT C.id AS C_id, C.familyName AS C_familyName, C.givenName AS C_givenName, C.birthday AS C_birthday, C.updateCount AS C_updateCount, C.createdTime AS C_createdTime, C.updatedTime AS C_updatedTime, P.contactId AS P_contactId, P.childIndex AS P_childIndex, P.label AS P_label, P.content AS P_content FROM Contact C LEFT OUTER JOIN Phone P ON P.contactId=C.id WHERE C.familyName='Apple'

3-1-14. SELECT 右外部結合

Java
List<Contact> contacts = new ArrayList<>();
List<Phone> phones = new ArrayList<>();
Transaction.execute(connection ->
    new Sql<>(Contact.class, "C")
        .rightJoin(Phone.class, "P", "{P.contactId}={C.id}")
        .where("{P.label}={}", "Main")
        .<Phone>select(connection, contacts::add, phones::add)
);
SQL
-- SQLite 以外
SELECT C.id AS C_id, C.familyName AS C_familyName, C.givenName AS C_givenName, C.birthday AS C_birthday, C.updateCount AS C_updateCount, C.createdTime AS C_createdTime, C.updatedTime AS C_updatedTime, P.contactId AS P_contactId, P.childIndex AS P_childIndex, P.label AS P_label, P.content AS P_content FROM Contact C RIGHT OUTER JOIN Phone P ON P.contactId=C.id WHERE P.label='Main'

-- SQLite は未サポート

3-1-15. SELECT COUNT(*)

Java
int[] rowCount = new int[1];
Transaction.execute(connection ->
    rowCount[0] = new Sql<>(Contact.class)
        .where("familyName={}", "Apple")
        .selectCount(connection)
);
SQL
-- 各DBMS共通
SELECT COUNT(*) FROM Contact WHERE familyName='Apple'

3-2. INSERT

3-2-1. INSERT 1行

Java
Contact contact = new Contact();
contact.id = 1;
contact.familyName = "Apple";
contact.givenName = "Akane";
Calendar calendar = Calendar.getInstance();
calendar.set(2001, 1-1, 1, 0, 0, 0);
contact.birthday = new Date(calendar.getTimeInMillis())
Transaction.execute(connection -> {
    new Sql<>(Contact.class).insert(connection, contact));
SQL
-- MySQL, Oracle, PostgreSQL
INSERT INTO Contact (id, familyName, givenName, birthday, updateCount, createdTime, updatedTime) VALUES (1, 'Apple', 'Akane', DATE'2001-01-01', 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)

-- SQLite
INSERT INTO Contact (id, familyName, givenName, birthday, updateCount, createdTime, updatedTime) VALUES (1, 'Apple', 'Akane', '2001-01-01', 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)

-- SQL Server
INSERT INTO Contact (id, familyName, givenName, birthday, updateCount, createdTime, updatedTime) VALUES (1, 'Apple', 'Akane', CAST('2001-01-01' AS DATE), 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)

3-2-2. INSERT 複数行

Java
List<Contact> contacts = new ArrayList<>();

Contact contact = new Contact();
contact.id = 2; contact.familyName = "Apple"; contact.givenName = "Yukari";
Calendar calendar = Calendar.getInstance();
calendar.set(2001, 1-1, 2, 0, 0, 0);
contact.birthday = new Date(calendar.getTimeInMillis());
contacts.add(contact);

contact = new Contact();
contact.id = 3; contact.familyName = "Apple"; contact.givenName = "Azusa";
calendar = Calendar.getInstance();
calendar.set(2001, 1-1, 3, 0, 0, 0);
contact.birthday = new Date(calendar.getTimeInMillis());
contacts.add(contact);

Transaction.execute(connection ->
    new Sql<>(Contact.class).insert(connection, contacts));
SQL
-- MySQL, Oracle, PostgreSQL
INSERT INTO Contact (id, familyName, givenName, birthday, updateCount, createdTime, updatedTime) VALUES (2, 'Apple', 'Yukari', DATE'2001-01-02', 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
INSERT INTO Contact (id, familyName, givenName, birthday, updateCount, createdTime, updatedTime) VALUES (3, 'Apple', 'Azusa', DATE'2001-01-03', 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)

-- SQLite
INSERT INTO Contact (id, familyName, givenName, birthday, updateCount, createdTime, updatedTime) VALUES (2, 'Apple', 'Yukari', '2001-01-02', 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
INSERT INTO Contact (id, familyName, givenName, birthday, updateCount, createdTime, updatedTime) VALUES (3, 'Apple', 'Azusa', '2001-01-03', 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)

-- SQL Server
INSERT INTO Contact (id, familyName, givenName, birthday, updateCount, createdTime, updatedTime) VALUES (2, 'Apple', 'Yukari', CAST('2001-01-02' AS DATE), 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
INSERT INTO Contact (id, familyName, givenName, birthday, updateCount, createdTime, updatedTime) VALUES (3, 'Apple', 'Azusa', CAST('2001-01-03' AS DATE), 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)

3-3. UPDATE

3-3-1. UPDATE 1行

Java
Transaction.execute(connection ->
    new Sql<>(Contact.class)
        .where("{id}={}", 1)
        .select(connection)
        .ifPresent(contact -> {
            contact.givenName = "Akiyo";
            new Sql<>(Contact.class).update(connection, contact);
        })
);
SQL
-- MySQL, Oracle, PostgreSQL
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE id=1
UPDATE Contact SET familyName='Apple', givenName='Akiyo', birthday=DATE'2001-01-01', updateCount=updateCount+1, updatedTime=CURRENT_TIMESTAMP WHERE id=1

-- SQLite
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE id=1
UPDATE Contact SET familyName='Apple', givenName='Akiyo', birthday='2001-01-01', updateCount=updateCount+1, updatedTime=CURRENT_TIMESTAMP WHERE id=1

-- SQL Server
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE id=1 
UPDATE Contact SET familyName='Apple', givenName='Akiyo', birthday=CAST('2001-01-01' AS DATE), updateCount=updateCount+1, updatedTime=CURRENT_TIMESTAMP WHERE id=1

3-3-2. UPDATE 複数行

Java
Transaction.execute(connection -> {
    List<Contact> contacts = new ArrayList<>();
    new Sql<>(Contact.class)
        .where("{familyName}={}", "Apple")
        .select(connection, contact -> {
            contact.familyName = "Apfel";
            contacts.add(contact);
        });
    new Sql<>(Contact.class).update(connection, contacts);
});
SQL
-- MySQL, Oracle, PostgreSQL
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE familyName='Apple'
UPDATE Contact SET familyName='Apfel', givenName='Akiyo', birthday=DATE'2001-01-01', updateCount=updateCount+1, updatedTime=CURRENT_TIMESTAMP WHERE id=1
UPDATE Contact SET familyName='Apfel', givenName='Yukari', birthday=DATE'2001-01-02', updateCount=updateCount+1, updatedTime=CURRENT_TIMESTAMP WHERE id=2
UPDATE Contact SET familyName='Apfel', givenName='Azusa', birthday=DATE'2001-01-03', updateCount=updateCount+1, updatedTime=CURRENT_TIMESTAMP WHERE id=3

-- SQLite
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE familyName='Apple'
UPDATE Contact SET familyName='Apfel', givenName='Akiyo', birthday='2001-01-01', updateCount=updateCount+1, updatedTime=CURRENT_TIMESTAMP WHERE id=1
UPDATE Contact SET familyName='Apfel', givenName='Yukari', birthday='2001-01-02', updateCount=updateCount+1, updatedTime=CURRENT_TIMESTAMP WHERE id=2
UPDATE Contact SET familyName='Apfel', givenName='Azusa', birthday='2001-01-03', updateCount=updateCount+1, updatedTime=CURRENT_TIMESTAMP WHERE id=3

-- SQL Server
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE familyName='Apple'
UPDATE Contact SET familyName='Apfel', givenName='Akiyo', birthday=CAST('2001-01-01' AS DATE), updateCount=updateCount+1, updatedTime=CURRENT_TIMESTAMP WHERE id=1
UPDATE Contact SET familyName='Apfel', givenName='Yukari', birthday=CAST('2001-01-02' AS DATE), updateCount=updateCount+1, updatedTime=CURRENT_TIMESTAMP WHERE id=2
UPDATE Contact SET familyName='Apfel', givenName='Azusa', birthday=CAST('2001-01-03' AS DATE), updateCount=updateCount+1, updatedTime=CURRENT_TIMESTAMP WHERE id=3

3-3-3. UPDATE 指定条件, カラム選択

Java
Contact contact = new Contact();
contact.familyName = "Pomme";
Transaction.execute(connection ->
    new Sql<>(Contact.class)
        .where("{familyName}={}", "Apfel")
        .columns("familyName")
        .update(connection, contact)
);
SQL
-- 各DBMS共通
UPDATE Contact SET familyName='Pomme' WHERE familyName='Apfel'

3-3-4. UPDATE 全行

Java
Contact contact = new Contact();
Transaction.execute(connection ->
    new Sql<>(Contact.class)
        .where(Condition.ALL)
        .columns("birthday")
        .update(connection, contact)
);
SQL
-- 各DBMS共通
UPDATE Contact SET birthday=NULL

3-4. DELETE

3-4-1. DELETE 1行

Java
Transaction.execute(connection ->
    new Sql<>(Contact.class)
        .where("{id}={}", 1)
        .select(connection)
        .ifPresent(contact ->
            new Sql<>(Contact.class).delete(connection, contact))
);
SQL
-- 各DBMS共通
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE id=1
DELETE FROM Contact WHERE id=1

3-4-2. DELETE 複数行

Java
Transaction.execute(connection -> {
    List<Contact> contacts = new ArrayList<>();
    new Sql<>(Contact.class)
        .where("{familyName}={}", "Pomme")
        .select(connection, contacts::add);
    new Sql<>(Contact.class).delete(connection, contacts);
});
SQL
-- 各DBMS共通
SELECT id, familyName, givenName, birthday, updateCount, createdTime, updatedTime FROM Contact WHERE familyName='Pomme'
DELETE FROM Contact WHERE id=2
DELETE FROM Contact WHERE id=3

3-4-3. DELETE 指定条件

Java
Transaction.execute(connection -> {
    List<Contact> contacts = new ArrayList<>();
    new Sql<>(Contact.class)
        .where("{familyName}={}", "Pomme")
        .select(connection, contacts::add);
    new Sql<>(Contact.class).delete(connection, contacts);
});
SQL
-- 各DBMS共通
DELETE FROM Contact WHERE familyName='Orange'

3-4-4. DELETE 全行

Java
Transaction.execute(connection ->
    new Sql<>(Phone.class).where(Condition.ALL).delete(connection));
SQL
-- 各DBMS共通
DELETE FROM Phone