EJB 3.0(Public Draft)入門記 Java Persistence API Chapter3 その7

Chapter3 その4に続いて3.2.3 Synchronization to the Databaseのあたりをもう一度やってみます。
リンク先の日記を書いたときはFlushModeアノテーションというものがあるというのは意識していていたのですが、EntityManagerにsetFlushModeメソッドというものがあることをすっかり忘れていました。ということで今回はsetFlushModeメソッドにFlushModeType.AUTO, FlushModeType.COMMIT, FlushModeType.NEVERをそれぞれ渡して挙動を確かめたいと思います。

使うエンティティは1つだけにします。

@Entity(access=AccessType.FIELD)
public class Employee {

  @Id(generate=GeneratorType.AUTO)
  private int id;

  private String name;
  
  // getter, setter省略
}

クライアント。prepareメソッドでデータを用意しmainメソッド内でsetFlushModeしてからデータのupdateしてます。

@Stateless
public class ClientBean implements Client {

  @PersistenceContext
  private EntityManager em;

  public void prepare() {
    Employee e = new Employee();
    e.setName("ゴン");
    em.persist(e);
  }
  
  public void main() {
    //ここで渡す値をFlushModeType.AUTO, FlushModeType.COMMIT, FlushModeType.NEVERに変えて動かします
    em.setFlushMode(FlushModeType.COMMIT);
    Employee employee1 = (Employee) em.createQuery("from Employee").getSingleResult();
    employee1.setName("うさはな");
    Employee employee2 = (Employee) em.createQuery("from Employee").getSingleResult();
    System.out.println(employee2.getName());
  }

  public static void main(String[] args) throws Exception {
    EJB3StandaloneBootstrap.boot(null);
    EJB3StandaloneBootstrap.deployXmlResource("ejb3-deployment.xml");
    InitialContext ctx = new InitialContext();
    Client client = (Client) ctx.lookup(Client.class.getName());
    client.prepare();
    client.main();
    EJB3StandaloneBootstrap.shutdown();
  }
}

使用するFlushModeTypeを変えて実行してみた出力結果です。
1. FlushModeType.AUTOを使った場合。2回目のクエリの前にflushされてます。

Hibernate: insert into Employee (name, id) values (?, null)
Hibernate: call identity()
Hibernate: select employee0_.id as id0_, employee0_.name as name0_ from Employee employee0_
Hibernate: update Employee set name=? where id=?
Hibernate: select employee0_.id as id0_, employee0_.name as name0_ from Employee employee0_
うさはな

2. FlushModeType.COMMITを使った場合。2回目のクエリの後(commit時)にflushされてます。

Hibernate: insert into Employee (name, id) values (?, null)
Hibernate: call identity()
Hibernate: select employee0_.id as id0_, employee0_.name as name0_ from Employee employee0_
Hibernate: select employee0_.id as id0_, employee0_.name as name0_ from Employee employee0_
うさはな
Hibernate: update Employee set name=? where id=?

3. FlushModeType.NEVERを使った場合。java.lang.IllegalStateExceptionがおきて「You cannot set FlushModeType to NEVER for a TRANSACTION persistence context」といわれます。通常の永続コンテキストにはNEVERは指定できないということらしいです(たぶん拡張永続コンテキスト(extended persistent context)ならばOKということですね)。ということは通常の永続コンテキストを使う場合はリードオンリーなトランザクションでもNEVERは駄目なんですね。

FlushModeType.AUTOとFlushModeType.COMMITを使った場合の結果からわかることは、flushのタイミングに関係なく更新されるべき値(例でいうと「うさはな」)がちゃんとクエリで返ってくるということです。おもしろい。JDBCの感覚で考えてしまうと、FlushModeType.COMMITを使った場合のようにupdate前にupdateされるべき値が返ってくるのは不思議に思えてしまいますね。