자바의 DAO 패턴

1. 개요

DAO (Data Access Object) 패턴은 추상 API를 사용하여 지속성 레이어 (일반적으로 관계형 데이터베이스이지만 다른 지속성 메커니즘 일 수 있음)에서 애플리케이션 / 비즈니스 레이어분리 할 수있는 구조적 패턴입니다 .

이 API의 기능은 기본 스토리지 메커니즘에서 CRUD 작업을 수행하는 데 관련된 모든 복잡성을 애플리케이션에서 숨기는 것입니다. 이를 통해 두 레이어는 서로에 대해 전혀 알지 못해도 개별적으로 진화 할 수 있습니다.

이 튜토리얼에서는 패턴의 구현에 대해 자세히 살펴보고 JPA 엔티티 관리자에 대한 호출을 추상화하는 데 사용하는 방법을 배웁니다.

2. 간단한 구현

DAO 패턴이 어떻게 작동하는지 이해하기 위해 기본적인 예제를 만들어 보겠습니다.

사용자를 관리하는 애플리케이션을 개발하고 싶다고 가정 해 보겠습니다. 응용 프로그램의 도메인 모델이 데이터베이스에 대해 완전히 독립적 으로 유지하기 위해 이러한 구성 요소를 서로 깔끔하게 분리 할 수 있는 간단한 DAO 클래스를 만듭니다 .

2.1. 도메인 클래스

응용 프로그램이 사용자와 함께 작동하므로 도메인 모델을 구현하기 위해 하나의 클래스 만 정의해야합니다.

public class User { private String name; private String email; // constructors / standard setters / getters }

사용자 가 다른 행동 가치 강조를 구현하지 않도록 클래스는 사용자 데이터 단지 일반 컨테이너입니다.

물론 여기서 우리가해야 할 가장 적절한 디자인 선택은이 클래스를 사용하는 애플리케이션을 어떤 시점에서 구현할 수있는 지속성 메커니즘으로부터 격리하는 방법입니다.

이것이 바로 DAO 패턴이 해결하려는 문제입니다.

2.2. DAO API

기본 DAO 계층을 정의 하여 도메인 모델을 지속성 계층에서 완전히 분리 하는 방법을 살펴 보겠습니다 .

다음은 DAO API입니다.

public interface Dao { Optional get(long id); List getAll(); void save(T t); void update(T t, String[] params); void delete(T t); }

조감도에서 Dao 인터페이스가 T 유형의 객체에 대해 CRUD 작업을 수행하는 추상 API를 정의 한다는 것이 분명합니다 .

인터페이스가 제공하는 높은 수준의 추상화로 인해 사용자 개체 와 함께 작동하는 구체적이고 세분화 된 구현을 쉽게 만들 수 있습니다.

2.3. UserDao 클래스

Dao 인터페이스 의 사용자 별 구현을 정의 해 보겠습니다 .

public class UserDao implements Dao { private List users = new ArrayList(); public UserDao() { users.add(new User("John", "[email protected]")); users.add(new User("Susan", "[email protected]")); } @Override public Optional get(long id) { return Optional.ofNullable(users.get((int) id)); } @Override public List getAll() { return users; } @Override public void save(User user) { users.add(user); } @Override public void update(User user, String[] params) { user.setName(Objects.requireNonNull( params[0], "Name cannot be null")); user.setEmail(Objects.requireNonNull( params[1], "Email cannot be null")); users.add(user); } @Override public void delete(User user) { users.remove(user); } }

UserDao의 클래스가 구현하는 모든 기능은 가져 오는 업데이트 및 제거에 필요한 사용자 개체를.

간단하게하기 위해 사용자 목록 은 생성자에 있는 두 개의 User 개체로 채워진 메모리 내 데이터베이스처럼 작동 합니다 .

물론 다른 방법을 리팩토링하기가 쉽기 때문에 예를 들어 관계형 데이터베이스에서 작동 할 수 있습니다.

UserUserDao 클래스가 동일한 애플리케이션 내에서 독립적으로 공존 하지만 , 애플리케이션 로직에서 지속성 레이어를 숨기는 데 후자가 어떻게 사용될 수 있는지 확인해야합니다.

public class UserApplication { private static Dao userDao; public static void main(String[] args) { userDao = new UserDao(); User user1 = getUser(0); System.out.println(user1); userDao.update(user1, new String[]{"Jake", "[email protected]"}); User user2 = getUser(1); userDao.delete(user2); userDao.save(new User("Julie", "[email protected]")); userDao.getAll().forEach(user -> System.out.println(user.getName())); } private static User getUser(long id) { Optional user = userDao.get(id); return user.orElseGet( () -> new User("non-existing user", "no-email")); } }

이 예제는 고안된 것이지만 간단히 말해서 DAO 패턴의 동기를 보여줍니다. 이 경우 기본 메서드는 UserDao 인스턴스를 사용하여 몇 가지 User 개체 에 대해 CRUD 작업을 수행 합니다.

이 프로세스의 가장 관련성있는 측면은 UserDao 가 개체가 유지, 업데이트 및 삭제되는 방법에 대한 모든 하위 수준 세부 정보를 응용 프로그램에서 숨기는 방법 입니다.

3. JPA에서 패턴 사용

패턴이 JPA의 엔티티 관리자가 제공하는 것 위에 구현 된 추상화 및 복잡성의 또 다른 계층이되기 때문에 JPA의 릴리스가 DAO 패턴의 기능을 제로로 다운 그레이드한다고 생각하는 개발자 사이의 일반적인 경향이 있습니다.

의심 할 여지없이 일부 시나리오에서는 이것이 사실입니다. 그럼에도 불구하고 때때로 우리는 엔티티 관리자의 API의 몇 가지 도메인 특정 메소드 만 애플리케이션에 노출하려고합니다. 이러한 경우 DAO 패턴이 그 자리를 차지합니다.

3.1. JpaUserDao 클래스

즉, Dao 인터페이스 의 새로운 구현을 만들어 JPA의 엔티티 관리자가 즉시 제공하는 기능을 캡슐화하는 방법을 살펴 보겠습니다.

public class JpaUserDao implements Dao { private EntityManager entityManager; // standard constructors @Override public Optional get(long id) { return Optional.ofNullable(entityManager.find(User.class, id)); } @Override public List getAll() { Query query = entityManager.createQuery("SELECT e FROM User e"); return query.getResultList(); } @Override public void save(User user) { executeInsideTransaction(entityManager -> entityManager.persist(user)); } @Override public void update(User user, String[] params) { user.setName(Objects.requireNonNull(params[0], "Name cannot be null")); user.setEmail(Objects.requireNonNull(params[1], "Email cannot be null")); executeInsideTransaction(entityManager -> entityManager.merge(user)); } @Override public void delete(User user) { executeInsideTransaction(entityManager -> entityManager.remove(user)); } private void executeInsideTransaction(Consumer action) { EntityTransaction tx = entityManager.getTransaction(); try { tx.begin(); action.accept(entityManager); tx.commit(); } catch (RuntimeException e) { tx.rollback(); throw e; } } }

JpaUserDao의 클래스는 JPA 구현에서 지원하는 관계형 데이터베이스로 작업 할 수 있습니다.

또한 클래스를 자세히 살펴보면 컴포지션 및 종속성 주입을 사용하여 애플리케이션에 필요한 엔티티 관리자 메서드 만 호출 할 수있는 방법을 알 수 있습니다.

간단히 말해, 전체 엔티티 관리자의 API가 아닌 도메인 별 맞춤형 API가 있습니다.

3.2. 사용자 클래스 리팩토링

이 경우 Hibernate를 JPA 기본 구현으로 사용할 것이므로 이에 따라 User 클래스를 리팩토링 할 것입니다 .

@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; private String name; private String email; // standard constructors / setters / getters }

3.3. 프로그래밍 방식 으로 JPA 엔티티 관리자 부트 스트랩

로컬 또는 원격으로 실행되는 MySQL의 작업 인스턴스 와 일부 사용자 레코드로 채워진 데이터베이스 테이블 "사용자" 가 이미 있다고 가정하면 JPA 엔티티 관리자를 가져 와서 CRUD 작업을 수행 하는 데 JpaUserDao 클래스를 사용할 수 있습니다 . 데이터 베이스.

대부분의 경우 표준 접근 방식 인 일반적인 "persistence.xml" 파일을 통해이를 수행합니다 .

이 경우 "xml-less" 접근 방식을 취하고 Hibernate의 편리한 EntityManagerFactoryBuilderImpl 클래스를 통해 일반 Java로 엔티티 관리자를 얻습니다 .

Java로 JPA 구현을 부트 스트랩하는 방법에 대한 자세한 설명은이 기사를 확인하십시오.

3.4. UserApplication 클래스

마지막으로 초기 UserApplication 클래스를 리팩터링하여 JpaUserDao 인스턴스와 함께 작동하고 User 엔터티 에서 CRUD 작업을 실행할 수 있습니다 .

public class UserApplication { private static Dao jpaUserDao; // standard constructors public static void main(String[] args) { User user1 = getUser(1); System.out.println(user1); updateUser(user1, new String[]{"Jake", "[email protected]"}); saveUser(new User("Monica", "[email protected]")); deleteUser(getUser(2)); getAllUsers().forEach(user -> System.out.println(user.getName())); } public static User getUser(long id) { Optional user = jpaUserDao.get(id); return user.orElseGet( () -> new User("non-existing user", "no-email")); } public static List getAllUsers() { return jpaUserDao.getAll(); } public static void updateUser(User user, String[] params) { jpaUserDao.update(user, params); } public static void saveUser(User user) { jpaUserDao.save(user); } public static void deleteUser(User user) { jpaUserDao.delete(user); } }

예제가 실제로 매우 제한적 일지라도 DAO 패턴의 기능을 엔티티 관리자가 제공하는 기능과 통합하는 방법을 보여주는 데 유용합니다.

대부분의 애플리케이션 에는 UserApplication 클래스에 JpaUserDao 인스턴스 를 삽입하는 DI 프레임 워크가 있습니다 . 단순화를 위해이 프로세스의 세부 사항은 생략했습니다.

여기서 강조해야 할 가장 관련성있는 점 JpaUserDao 클래스 가 어떻게 지속성 계층이 CRUD 작업을 수행하는지에 대해 UserApplication 클래스를 완전히 무관 하게 유지하도록 돕는 것 입니다.

또한, MySQL을 다른 RDBMS (및 플랫 데이터베이스의 경우에도)로 교체 할 수 있지만 Dao 인터페이스와 엔티티 관리자가 제공하는 추상화 수준 덕분에 애플리케이션이 예상대로 계속 작동 합니다. .

4. 결론

이 기사에서는 DAO 패턴의 핵심 개념, Java로 구현하는 방법, JPA의 엔티티 관리자에서 사용하는 방법에 대해 자세히 살펴 보았습니다.

평소처럼이 문서에 표시된 모든 코드 샘플은 GitHub에서 사용할 수 있습니다.