2015年5月17日日曜日

[JPA2.1]DDL generation

JPA2.1で標準化されたDDL Generationを試してみました。

Entity

Entityクラスの実装
package sample;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "user")
public class UserEntity {

    private Long id;

    private String name;

    @Id
    @GeneratedValue()
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

persistence.xml

DDL Generationを使うための設定が入ったpersistence.xmlの例
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">

  <persistence-unit name="pu" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

    <class>sample.UserEntity</class>

    <properties>
      <property name="javax.persistence.schema-generation.database.action" value="drop-and-create" />
      <property name="javax.persistence.schema-generation.scripts.action" value="drop-and-create" />
      <property name="javax.persistence.schema-generation.scripts.create-target" value="create.sql" />
      <property name="javax.persistence.schema-generation.scripts.drop-target" value="drop.sql" />
      <property name="javax.persistence.sql-load-script-source" value="file:./load-data.sql" />

      <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
      <property name="javax.persistence.jdbc.url" value="jdbc:h2:file:/tmp/test-db" />
      <property name="javax.persistence.jdbc.user" value="sa" />
      <property name="eclipselink.logging.level" value="fine" />
    </properties>
  </persistence-unit>

</persistence>

この設定でアプリケーションを実行すると、カレントディレクトリ配下にdorp用とcreate用のDDLが作成されます。
また、データベースに対してもdropでDBクリーニング後にCREATEでデータベースがセットアップされます。
データベースセットアップ後には、javax.persistence.sql-load-script-sourceで指定したSQLスクリプトが実行されます。

javax.persistence.schema-generation.database.action
データベース上へのアクションを指定します。drop-and-createを指定しているので、Entityを元に既存オブジェクトの削除をしてから新しくオブジェクトが作られます。
その他にも、dropcreateなどを選択できます。

javax.persistence.schema-generation.scripts.action
オブジェクト作成用のSQLスクリプトファイルの生成方法を指定します。drop-and-createを指定しているので、dropとcreate用の2つのSQLファイルが出力されます。

create文は、javax.persistence.schema-generation.scripts.create-targetで設定したファイルに出力されます。
drop文は、javax.persistence.schema-generation.scripts.drop-targetで設定したファイルに出力されます。

javax.persistence.sql-load-script-source
データベースセットアップ後に実行するSQLスクリプトを設定します。
初期データのセットアップなどを行うINSERT文などを定義します。


2015年5月9日土曜日

[Oracle12C]拡張データ型でVARCHAR2やRAWの最大長をより大きい値にする

Oracle12cからの新機能の拡張データ型を使用すると、VARCHAR2NVARCHAR2RAWの最大サイズが32,767バイトまで指定可能となります。

※以前のバージョンでは、文字列(VARCHAR2、NVARCHAR2)は4000、RAWは2000が最大サイズだったかと思います。

拡張データ型の使用方法

拡張データ型は初期化パラメータのMAX_STRING_SIZEにより設定します。

この初期化パラメータには、以下の2種類の設定値があります。設定値を拡張データ型(EXTENDED)に変更するとSTANDARDに戻すことはできないので注意が必要です。
EXTENDED
上限が32,767バイトになります
STANDARD
Oracle12cより前のバージョンと同じ仕様となります。デフォルトはこの値となります。

現在値の確認

SQL> show parameters max_string_size

NAME                                        TYPE       VALUE
------------------------------------ ----------- ------------------------------
max_string_size                      string      STANDARD

UPGRADEモードでデータベースを再起動

非CDBの場合には、以下の手順でデータベースを再起動します。
SQL> shutdown
SQL> startup UPGRADE

拡張データ型の有効化

altert systemで拡張データ型を使用できるようにします。
SQL> alter system set MAX_STRING_SIZE = EXTENDED;

System altered.

SQL> show parameters max_string_size

NAME                                        TYPE        VALUE
------------------------------------ ----------- ------------------------------
max_string_size                      string      EXTENDED

rdbms/admin/utl32k.sqlを実行し、ノーマルモードでデータベースを再起動します。

テーブルを作ってみる

以下のように、VARCHAR2の4000バイトを突破したカラムを定義できるようになります。
CREATE TABLE TEST_TABLE
(
    EXTENDED_VARCHAR VARCHAR2(32767) NOT NULL
)

作成したテーブルの定義を確認すると、指定した桁数のVARCAR2列が定義されている事がわかります。
SQL> desc test_table;
 Name                                                Null?  Type
 ----------------------------------------- -------- ----------------------------
 EXTENDED_VARCHAR                   NOT NULL VARCHAR2(32767)

データを登録してみる

以下のように4000バイトを超えるデータを挿入、参照できていることがわかります。
SQL> insert into test_table values (rpad('1', 32767, '1'));

1 row created.

SQL> select length(extended_varchar) from test_table;

LENGTH(EXTENDED_VARCHAR)
------------------------
     32767

JDBCで扱ってみる


メータデータからカラム定義を取得

以下のコードでメタデータの情報を出力してみます。
final ResultSet rs = metaData.getColumns(null, null, "TEST_TABLE", null);
while (rs.next()) {
    System.out.println("rs.getString(\"column_name\") = " + rs.getString("column_name"));
    System.out.println("rs.getInt(\"data_type\") = " + rs.getInt("data_type"));
    System.out.println("Types.VARCHAR = " + Types.VARCHAR);
    System.out.println("rs.getString(\"type_name\") = " + rs.getString("type_name"));
    System.out.println("rs.getInt(\"column_size\") = " + rs.getInt("column_size"));
}

実行結果は、以下のようになります。ドキュメントには、4000バイトを超えるデータは内部的にはCLOBのテクノロジを使用して格納するとあったけど、テーブル作った時のデータタイプなどが取得できています。
table.getString("column_name") = EXTENDED_VARCHAR
table.getInt("data_type") = 12
Types.VARCHAR = 12
table.getString("type_name") = VARCHAR2
table.getInt("column_size") = 32767

データを参照してみる

JDBC経由での参照は特に何かを意識する必要はなさそうです。
型マッピングもVARCHAR2と同じですね。(CLOBになってなくてよかったです。)
final Statement statement = connection.createStatement();
final ResultSet rs = statement.executeQuery("select * from TEST_TABLE");
if (rs.next()) {
    final Object object = rs.getObject(1);
    System.out.println("String? " + (object instanceof String));
    final String str = String.class.cast(object);
    System.out.println("str.length() = " + str.length());
}
//-------------------
// 実行結果
//-------------------
String? true
str.length() = 32767