@Stateless @Local(value={BookDAO.class}) public class BookDAOBean implements BookDAO { }엔티티 빈에 @Entity을 지정하는 것처럼 @Stateless와 대응되는 @Stateful은 클래스를 세션 빈으로 표시한다. 그렇지만 한 가지 또 다른 요구사항은 해당 빈에서 서로 다른 요청에 대해 어느 메소드가 노출되어야 하는지를 컨테이너에 알려주는 인터페이스를 구현하는 것이다. 이러한 요청은 빈은 여러 서버에 걸쳐 배포될 수 있기 때문에 로컬이나 원격에서 수행될 수 있다. 빈은 각기 서로 다른 요청 타입에 어느 메소드를 사용할 수 있는지를 표시하는 하나 이상의 인터페이스를 포함할 수 있다. 우리는 로컬 접근에 대해서만 노출할 필요가 있으므로 어느 클래스의 로컬 인터페이스에 @Local 어노테이션이 지정되어 있는지를 컨테이너에 알려줄 것이다. 아래는 그러한 인터페이스를 보여준다:
package lcds.examples.bookie.dao; ... public interface BookDAO { public void persist(Book transientInstance); public void remove(Book persistentInstance); public Book merge(Book detachedInstance); public Book findById(int id); public List위 코드는 모두 하나의 책이나 책 모음에 대해 수행할 수 있는 것을 나열한 것이다. 아래는 책에 대한 세션 빈을 코드로 작성한 것이다:getAll(); public List findByAuthor(Person author); public List findBySubject(Subject subject); public Collection findByName(String title); public void removeDetached(Book detachedInstance); }
package lcds.examples.bookie.dao.beans; @Stateless @Local(value={BookDAO.class}) public class BookDAOBean implements BookDAO { @PersistenceContext private EntityManager entityManager; public void persist(Book transientInstance) { try { entityManager.persist(transientInstance); } catch (RuntimeException re) { throw re; } } public void remove(Book persistentInstance) { try { entityManager.remove(persistentInstance); } catch (RuntimeException re) { throw re; } }위의 두 persist와 remove 메소드는 책 클래스의 인스턴스를 인자로 받아들인다. persist 메소드는 아직 데이터베이스에 존재하지 않는 엔티티에 대해 호출되어야 하며, remove 메소드는 데이터베이스에서 엔티티를 삭제할 것이다. 이 두 메소드는 같은 이름을 가진 EntityManager 타입의 객체에 들어있는 메소드를 호출한다. 그렇지만 주의할 점은 그 객체는 결코 인스턴스화되지 않는다는 것이다. 그 대신 EntityManager에는 @PersistenceContext 어노테이션이 지정되어 있다. 이것은 세션 빈이 EJB 3.0에서 어떻게 처리되는지를 일부 보여준다. EntityManager는 런타임에 인스턴스화되며 컨테이너는 현재 빈이 실행되고 있는 영속화 컨텍스트(persistence context)와 일치하는 EntityManager 인스턴스를 해당 세션 빈에 넘겨준다. 만약 하나 이상의 인스턴스가 존재하면 어노테이션에 원하는 것을 지정할 수도 있지만, 예제에서는 영속화 컨텍스트가 단순히 하나의 영속화 단위이므로 예제의 영속화 단위에 들어있는 빈은 모두 동일한 엔티티 관리자를 획득하게 된다.
public Book merge(Book detachedInstance) { try { Book result = entityManager.merge(detachedInstance); return result; } catch (RuntimeException re) { throw re; } } public void removeDetached(Book detachedInstance) { remove(merge(detachedInstance)); }persist와 remove 메소드는 기본적으로 SQL의 insert와 delete 문에 매핑된다. merge는 두 가지 경우에 사용될 수 있다. 첫 번째 경우는 직접적으로 SQL의 update문에 해당되는 것인데, merge는 엔티티로부터 변경 내역을 받아들여 데이터베이스를 갱신하기 위해 대기열(queue)에 해당 변경 내역을 추가한다. 그런데 EJB에는 EntityManager에 의해 관리되는 엔티티의 개념이 포함되어 있다. 엔티티 관리자의 remove 메소드가 호출되었을 때 실제로 일어나는 일은 엔티티가 해당 엔티티를 데이터베이스에서 제거하기 위해 대기열에 추가한 관리자에서 제거되는 것이다. 그러나 그 객체는 여전히 존재할 수도 있는데, 코드에서 그 객체를 참조하고 있을 수도 있기 때문이다. 또한 그 객체에 merge를 수행하여 엔티티 관리자로 되돌려 보내는 것도 가능하다.
public Book findById(int id) { try { Book instance = entityManager.find(Book.class, id); return instance; } catch (RuntimeException re) { throw re; } } public List필자는 위 코드에 세션 빈을 어떻게 다룰 지에 대한 예제에서 했던 것과 같이 메소드를 몇 개 더 추가하였다. findById 메소드는 개체 관리자의 find(Class, int) 메소드를 호출한다. 이 메소드는 주어진 클래스가 어디에 매핑되는지를 확인한 다음 ID와 일치하는 행을 찾아온다. 간단하다.getAll() { Query q = entityManager.createQuery( "from Book b" + " order by b.title" ); List results = q.getResultList(); return results; } public List findByAuthor(Person author) { Query q = entityManager.createQuery( "from Book b" + " where b.author.id = :author_id" ); q.setParameter("author_id", author.getId()); List results = q.getResultList(); return results; }
최신 콘텐츠