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

今回扱うのはエンティティのライフサイクルの削除です。


3.2.2 Removal

あるエンティティXに適用されるremove操作のセマンティクスは次のとおりです。

  • Xが新しく(new)生成されたエンティティならば、remove操作は無視される。しかし、エンティティXから他のエンティティへのリレーションシップにcascade=REMOVEもしくはcascade=ALLがアノテートされている場合、remove操作はXに参照されるエンティティにカスケードされる。
  • Xが管理された(managed)エンティティならば、remove操作により削除される。エンティティXから他のエンティティへのリレーションシップにcascade=REMOVEもしくはcascade=ALLがアノテートされている場合、remove操作はXに参照されるエンティティにカスケードされる。
  • Xが切り離された(detached)オブジェクトならばIllegalArgumentExceptionがスローされる(すなわちトランザクションのコミットが失敗する)。
  • Xが削除された(removed)エンティティならば無視される。
  • 削除された(removed)エンティティXはトランザクションのコミット時もしくはコミット前にDBから削除される、またはflush操作の実行の結果としてDBから削除される。削除された(removed)エンティティへのアクセスは未定義。

2番目のセマンティクス(管理されたエンティティに対するremove操作)を試してみます。エンティティのコードは前回とまったく同じ。AddressがエンティティXでEmployeeがAddressから参照されるエンティティ。AddressからEmployeeへのリレーションシップに「cascade = CascadeType.ALL」を指定してます。

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

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

  private String name;

  @ManyToOne
  private Address address;
  
  // getter, setter省略

}
@Entity(access=AccessType.FIELD)
public class Address implements Serializable {
  @Id(generate=GeneratorType.AUTO)
  private int id;

  private String name;
  
  @OneToMany(mappedBy="address", cascade = CascadeType.ALL )
  private Collection employees = new HashSet(); 

  public void addEmployee(Employee employee) {
    this.employees.add(employee);
    employee.setAddress(this);
  }
  
  // getter, setter省略
}
@Stateless
public class ClientBean implements Client {

  @PersistenceContext
  private EntityManager em;

  public void main() {
    Address address = new Address();
    address.setName("京都");

    Employee employee1 = new Employee();
    employee1.setName("ゴン");
    address.addEmployee(employee1);

    Employee employee2 = new Employee();
    employee2.setName("うさはな");
    address.addEmployee(employee2);
    
    em.persist(address);

    print();
    
    em.remove(address);
    
    print();

  }

  private void print() {
    Query query = em.createQuery("from Employee");
    for (Object each : query.getResultList()) {
      Employee e = (Employee) each;
      System.out.println(e.getName() + " : " + e.getAddress().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.main();
    EJB3StandaloneBootstrap.shutdown();
  }

実行結果

Hibernate: insert into Address (name, id) values (?, null)
Hibernate: call identity()
Hibernate: insert into Employee (name, address_id, id) values (?, ?, null)
Hibernate: call identity()
Hibernate: insert into Employee (name, address_id, id) values (?, ?, null)
Hibernate: call identity()
Hibernate: select employee0_.id as id1_, employee0_.name as name1_, employee0_.address_id as address3_1_ from Employee employee0_
うさはな : 京都
ゴン : 京都
Hibernate: delete from Employee where id=?
Hibernate: delete from Employee where id=?
Hibernate: delete from Address where id=?
Hibernate: select employee0_.id as id1_, employee0_.name as name1_, employee0_.address_id as address3_1_ from Employee employee0_

OK。いったん追加されてから削除されています。


1番目のセマンティクス(新しく追加されたエンティティに対するremove操作)も試してみました。上のClientBeanからpersistするところをコメントアウトしただけです。新規のエンティティに対するremove操作は無視されるとあるんですが、実行するとorg.hibernate.StaleStateExceptionをラップしたEJBExceptionがおきます。なぜ。どこかでPersistent APIの仕様に従うかどうかを設定しなければいけない?Embeddable EJBがまだAlpha 1だから?


4番目のセマンティクス(削除されたエンティティに対するremove操作)も試してみました。これはOKでした。削除されたエンティティにremove操作をしても何もおこりません。仕様どおり無視されているようです。