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

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月7日月曜日

HibernateのImplicitNamingStrategyを使って識別子に任意のプレフィックス等を付加する

Hibernateでテーブル名やカラム名などに一律任意のサフィックスやプレフィックスを追加するには、ImplicitNamingStrategyを実装したクラスを作成し設定を追加しするだけでよい。
PhysicalNamingStrategyを使った識別子の変更とは異なり、明示的に識別子を指定した場合は、このクラスは呼び出されない。また、ImplicitNamingStrategyのほうが、命名ルールを細かく設定できる。

ImplicitNamingStrategyの実装クラス

JPAの場合はデフォルト実装のImplicitNamingStrategyJpaCompliantImplを継承する。
PhysicalNamingStrategyとくらべてかなり多くのメソッドが定義されているけど、テーブル名であればdeterminePrimaryTableNameをオーバライドしてあげるとよい。
サンプル実装では、テーブル名の後ろに「_TBL」を付加している。
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitEntityNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;

public class HogeImplicitNamingStrategy extends ImplicitNamingStrategyJpaCompliantImpl {

    @Override
    public Identifier determinePrimaryTableName(ImplicitEntityNameSource source) {
        Identifier tableName = super.determinePrimaryTableName(source);
        return Identifier.toIdentifier(tableName.getText() + "_TBL");
    }
}

persistence.xmlにImplicitNamingStrategy実装クラスを設定

hibernate.implicit_naming_strategyというプロパティに、作成したクラスのFQCNを設定する。

<properties>
  <property name="hibernate.implicit_naming_strategy" value="sample.HogeImplicitNamingStrategy" />
</properties>

結果

テーブル名の末尾に「_TBL」が設定されているのが確認できる。usersエンティティは、@Tableアノテーションが設定されているので、「_TBL」が付加されない。

Hibernate: create table Item_TBL (id bigint not null, primary key (id))
Hibernate: create table users (id bigint not null, name varchar(255), primary key (id))

2015年12月6日日曜日

HibernateのPhysicalNamingStrategyを使って識別子に任意のプレフィックス等を付加する

Hibernateでテーブル名やカラム名などに一律任意のサフィックスやプレフィックスを追加するには、PhysicalNamingStrategyを実装したクラスを作成し設定を追加しするだけでよい。

PhysicalNamingStrategyの実装クラス

PhysicalNamingStrategyのデフォルト実装のPhysicalNamingStrategyStandardImplを継承すると必要なメソッドだけ実装すればいいのでよい。
この例では、テブル名の先頭に「hoge_」を付加している。
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

public class HogeNameStrategy extends PhysicalNamingStrategyStandardImpl {
    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        return Identifier.toIdentifier("hoge_" + name.getText(), name.isQuoted());
    }
}

persistence.xmlへPhysicalNamingStrategy実装クラスを設定

hibernate.physical_naming_strategyというプロパティに、作成したクラスのFQCNを設定する。
<properties>
  <property name="hibernate.physical_naming_strategy" value="sample.HogeNameStrategy" />
</properties>

結果

一律プレフィックスが追加されているのがわかる
Hibernate: create table HOGE_Item (id bigint not null, primary key (id))
Hibernate: create table "HOGE_user" (id bigint not null, name varchar(255), primary key (id))

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月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 ----------