ラベル JPA の投稿を表示しています。 すべての投稿を表示
ラベル JPA の投稿を表示しています。 すべての投稿を表示

2016年2月19日金曜日

HibernateのColumnDefaultを使ってデフォルト値をDBへ登録する

Hibernateの独自実装のColumnDefaultアノテーションとGeneratedアノテーションを使うと、テーブルへのレコード追加時や更新時にデフォルト値を設定することができる。

ColumnDefaultアノテーション

このアノテーションに設定した値が、デフォルト値となる。
受け取れる値は文字列のみなので、数値型や日付型などのカラムでもすべて文字列で設定する必要がある。

Generatedアノテーション

このアノテーションで初期値を代入するタイミングを定義する。

Entityクラス

lastModified属性が、デフォルト値が代入されるカラムとなる。
ポイントは、Columnアノテーションのinsertableとupdatableにfalseを設定しているところ。
これを行わないと、insertやupdate時にデフォルト値が設定されなくなる。

ColumnDefaultアノテーションは、すでにデータベース上に作成されているテーブルに定義されていることを想定しているので、ここでは使用していない
@Entity
public class HogeEntity {

    @Id
    @GeneratedValue
    public Long id;

    @Column(insertable = false, updatable = false)
    @Generated(GenerationTime.ALWAYS)
    public Timestamp lastModified;

    @Override
    public String toString() {
        return "HogeEntity{" +
                "id=" + id +
                ", lastModified=" + lastModified +
                '}';
    }
}

Entityに対応したテーブル定義

lastModifiedカラムは、登録・更新時にデフォルト値がセットされる。
CREATE TABLE `HogeEntity` (
  `id` bigint(20) NOT NULL,
  `lastModified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

実行結果

Entityをデータベースに永続化する部分の実装例。
HogeEntity entity = new HogeEntity()
em.persist(entity)
em.flush()
System.out.println("entity = " + entity)
上のコードを実行した結果は、このようになる。
明示的にEntityに値を設定していないのに、最終更新日が登録されている。
entity = HogeEntity{id=1, lastModified=2015-12-13 01:29:35.981}

この機能は、既存のテーブルに対してHibernateを適用する際に使用する。
新規に構築する場合には、PrePersistアノテーションを使ってEntityでデフォルト値を設定するのがいいのではないかと思う。

2015年12月5日土曜日

Hibernate5のhibernate.auto_quote_keywordでキーワードを識別子に使えるように

hibernate.auto_quote_keywordプロパティの値をtrueに設定すると、キーワードを識別子に使った場合、自動的にクォートで囲ってくれるようになる。
これで、キーワードとかぶるエンティティを作ったとしても、@Tableアノテーションでテーブル名を明示的に指定しなくても良くなる。

Entityクラス

Oracleでは識別子に指定できないUserがテーブル名となるエンティティクラスを定義する。
@Entity
public class User {
    @Id
    public Long id;
    public String name;
}

hibernate.auto_quote_keywordの指定

persistence.xmlのproperties部にhibernate.auto_quote_keywordの設定を追加する。
設定値にtrueを指定すると、自動的にダブルクォートで囲われるようになる。
<properties>
  <property name="hibernate.auto_quote_keyword" value="true" />
</properties>

実行結果

ログ上で確認するとこんな感じにクォートで囲われているのがわかる。
Hibernate: create table "User" (id number(19,0) not null, name varchar2(255 char), primary key (id))

この設定値をfalseにした場合は、こんな結果になる。
Hibernate: create table User (id number(19,0) not null, name varchar2(255 char), primary key (id))
ERROR: HHH000389: Unsuccessful: create table User (id number(19,0) not null, name varchar2(255 char), primary key (id))
ERROR: ORA-00903: 表名が無効です。


JPA(Hibernate)経由でSQL実行しているだけだといいけど、自分でSQLを書く必要が出てきた時にはまりそうだなぁと思ったり。

2015年7月27日月曜日

eclipselinkでbatch insert

eclipselinkでは、jdbc.batch-writingjdbc.batch-writing.sizeの2つのプロパティを指定することでbatch insertが使用されます。

jdbc.batch-writingには、基本的にjdbcを指定すればよく、jdbc.batch-writing.sizeにはバッチサイズを指定する。

例えば、以下のように設定するとバッチサイズは100となります。

<properties>
  <property name="javax.persistence.schema-generation.database.action" value="drop-and-create" />

  <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>

  <property name="eclipselink.jdbc.batch-writing" value="JDBC" />
  <property name="eclipselink.jdbc.batch-writing.size" value="100" />
</properties>

結果を見ると、batch insertのほうが早くなっているのがわかります。

batch insertあり
---------- 1回目: 625ms ----------
---------- 2回目: 319ms ----------
---------- 3回目: 274ms ----------

batch insertなし
---------- 1回目: 1630ms ----------
---------- 2回目: 1342ms ----------
---------- 3回目: 1146ms ----------

2015年7月22日水曜日

hibernateでbatch-insert

hibernate.jdbc.batch_sizeに任意の数字を指定すると、その値が自動的にJDBCのバッチサイズになります。

例えば、以下の設定を行うとバッチサイズは100となります。

<properties>
  <property name="javax.persistence.schema-generation.database.action" value="drop-and-create" />
  <property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver" />
  <property name="javax.persistence.jdbc.url" value="url"/>
  <property name="javax.persistence.jdbc.user" value="user"/>
  <property name="javax.persistence.jdbc.password" value="password"/>

  <property name="hibernate.jdbc.batch_size" value="100" />
</properties>

試しに以下のコードでbatch insertの効果があるのか確認してみます。
private static void insert(EntityManager em) {
        final EntityTransaction transaction = em.getTransaction();
        transaction.begin();
        for (int i = 1; i <= 1000; i++) {
            final UserEntity entity = new UserEntity();
            entity.setName("name_" + i);
            em.persist(entity);
        }
        transaction.commit();
    }

結果をみると、batch insertの効果で5倍ぐらい早くなっているのがわかります。

batch insertあり
---------- 1回目: 766ms ----------
---------- 2回目: 390ms ----------
---------- 3回目: 317ms ----------
batch insertなし
---------- 1回目: 2369ms ----------
---------- 2回目: 2347ms ----------
---------- 3回目: 1589ms ----------

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年1月31日土曜日

[JPA]中間テーブルを作らずに直接結合させる

例えば1対多の依存があるエンティティをJPAで使用した場合、デフォルトでは中間テーブルを使った結合となる。

Entityの関連定義

    @OneToMany(cascade = {CascadeType.ALL})
    public List<Tel> getTels() {
        return tels;
    }

テーブル定義

上のEntityの関連定義だと下のようなテーブル構造となります。
中間テーブルのテーブル名やカラム名を変更したい場合には、OneToManyとセットでJoinTableアノテーションを定義して任意の値に変更します。

どうしても過去の資産などを使わないとダメな場合なので中間テーブルを使えない場合には、OneToManyとセットでJoinColumnを定義してあげる。JoinColumnのname属性には、子供側テーブルの外部キーとなる項目名を定義します。

Entityを次のように変更すると、Userテーブルの子テーブルのTelテーブルにはUserテーブルを参照するための外部キー(user_id)が作成される。
    @OneToMany(cascade = {CascadeType.ALL})
    @JoinColumn(name = "user_id")
    public List<Tel> getTels() {
        return tels;
    }

テーブル定義は次のようになります。

2015年1月25日日曜日

[JPA]ElementCollectionとCollectionTableアノテーション

JPAのEntityクラスに基本型のCollectionやListの属性を定義する場合は、ElementCollectionアノテーションを使用する。

ElementCollectionをつけることで、基本型(Entityじゃないクラス)をもつCollectionやListの属性を別のテーブルに保存してくれるようになる。
CollectionTableアノテーションを使うと、保存先のテーブルの名前などをカスタマイズできるようになる。

Entityクラスの定義です。ソースコードが長くなると見づらくなるので、JPAのアノテーションはpublicフィールドに設定してアクセッサは定義していません。
@Entity
public class User {

    @Id
    @GeneratedValue
    public Long id;

    public String name;

    @ElementCollection
    public Set mail;
}

上のEntity定義の場合、CollectionTableアノテーションを使用していないのでデフォルトの定義でテーブルが作られます。
テーブル名は、親テーブルのテーブル名とその属性の名前(user_mail)となり、カラム名は属性名(mail)となります。


テーブル名を変更したい場合には、CollectionTableアノテーションのname属性を使用します。
カラム名を変更したい場合には、Columnアノテーションを使用します。