스프링 2의 JPA 지원을 통해 데이터베이스 접근하기
이번 절에서는 JPA를 지원하는 스프링 2의 기능을 이용해 Employee와 Address POJO의 데이터베이스 접근 기능을 추가한다.
JPA ORM을 이용한 객체 영속성
정의한 서비스 인터페이스를 구현하기 위해 스프링 2의 기능 일부분과 이것을 지원하는 라이브러리를 가져올 필요가 있다. 물론 대안도 있다. 인터페이스 구현을 JDBC와 같은 표준 자바 데이터베이스 접근 기술을 사용해 각 메서드 별로 시작할 수 있다. 그렇지만 어떻게 스프링 2가 JPA와 함께 작동하는 지를 본 후에는, 작업을 스프링에 위임하는 것이 실질적으로 더 쉽다는 것에 매우 감사해 할 것이다.
스프링 2에서 사용하는 JPA 영속성은 EJB 3.0과 자바 EE 5 명세에 통합되어 있다. 스프링에서 데이터베이스 접근을 가능하게 해주는 가장 간단한 방법 중 하나지만 아직 표준적인 방법은 아니다.
스프링 프레임워크는 다른 객체-관계형 매핑(ORM) 기술을 통해 영속성을 항상 지원하지만, 매핑 작업은 써드 파티 비표준 영속성 라이브러리에 대해 상당히 까다롭고 깊은 지식을 필요로 한다. JPA에 이미 익숙한 사람과 JPA 표준을 지원하는 기반을 갖고 있는 규모가 큰 벤더들에게 비표준 써드 파티 영속성 라이브러리 지원은 크게 중요하지는 않을 수도 있다.
스프링 2의 JPA 지원은 객체(POJO)와 관계형 데이터베이스의 쓰고, 읽고, 검색하고, 수정하고, 삭제하는 지루한 작업들을 투명하게 만들어준다. POJO를 사용해 작업함으로써 자바 언어의 객체 지향 문법을 계속 사용할 수 있고, JPA ORM 레이어는 데이터베이스 테이블을 만들고, 조회하고, 수정하고, 삭제하는 코드를 책임지게 된다.
투명한 데이터베이스 오퍼레이션 외에도 스프링 2의 JPA 지원은 특정 데이터베이스 벤더에 특화된 예외의 모음(potpourri)에서, 예외 처리 코드를 매우 간단하게 만들어주는 정제된 예외의 집합으로 변환해 준다. 그림 5는 스프링 2의 JPA 지원을 설명하고 있다.
그림 5. 스프링 2의 JPA 지원 | 자바 SE 5 어노테이션과 더불어 JPA는 외부 XML 파일을 통해 매핑 정보 명세를 지원한다. 이 기술은 자바 SE 5를 사용할 수 없을 경우 필요하나 이 튜토리얼의 범위에서 벗어난다. |
|
그림 5에서 어떻게 관계형 테이블과 매핑을 시킬 것인지에 대한 일부 정보(메타데이터)와 함께 객체들을 스프링 엔진에 제공한다. 스프링 JPA 지원은 다른 부분들에도 신경을 쓰고 있다. 자바 5 어노테이션이나 외부 XML 정의 파일(JDK 1.4와 호환되는)로 JPA 엔진에 매핑 정보를 제공할 수 있다.
JPA 구현체는 ORM 제품과 데이터베이스에 따라 다양하게 존재하기 때문에, 여러분이 구현한 코드를 다른 벤더의 솔루션에 (필요하다면) 이식할 수 있다.
매핑된 객체에 대한 오퍼레이션은 JPA 엔티티 매니저(entity manager)를 통해 수행된다. 예를 들어, em
이라는 엔티티 매니저로 관련 객체의 트리를 관계형 데이터베이스로 기록하려면 코드는 다음과 같다.
em.persists(myObjectTree);
|
그리고 나서 JPA 엔티티 매니저는 제공한 매핑 힌트를 검사하고 myObjectTree
를 통해 객체 트리의 모든 맵된 필드를 관계형 데이터베이스로 저장한다.
스프링 DAO를 사용한 도메인 서비스 구현에서 살펴보겠지만 스프링은 더 나아가 JPA 엔티티 매니저로 하는 작업을 단순화한다.
JPA ORM 매핑 메타 데이터 제공하기
Employee
객체를 데이터베이스에 어떻게 저장할 것인지에 대한 정보를 제공하기 위해, Employee.java 소스 코드에 자바 SE 5 어노테이션을 추가할 수 있다. 이 정보는 데이터를 기술하는 데이터이기 때문에 흔히 메타 데이터로 불린다.
Listing 6은 Employee
객체의 어노테이션 버전을 보여준다. 어노테이션은 굵은 글씨로 강조했다.
Listing 6. Employee POJO에 JPA 어노테이션 추가하기
package com.ibm.dw.spring2;
import java.util.Date;
import javax.persistence.CascadeType;
...
import javax.persistence.TemporalType;
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private long empid;
@Column(length = 6)
private String empno;
@Column(name = "FIRSTNME")
private String firstName;
@Column(name = "MIDINIT")
private String midInitial;
private String lastName;
@Column(name = "PHONENO")
private String phoneNumber;
@Column(length = 8)
private String job;
@Column(name = "EDLEVEL")
private int educationLevel;
@Column(length = 1)
private char sex;
@Column(precision=12, scale=2)
private double salary;
@Column(precision=12, scale=2)
private double bonus;
@Column(name = "COMM", precision=12, scale=2)
private double commission;
@OneToOne(cascade = CascadeType.ALL)
private Address addr;
@Temporal(TemporalType.DATE)
private Date hiredate;
@Temporal(TemporalType.DATE)
private Date birthdate;
...
|
여기에서 쓰인 모든 어노테이션은 필드 수준이다. 이 방법이 JPA 어노테이션의 가장 보편적인 사용방법이다. 또 필드의 대응되는 getter 메서드에 어노테이션을 붙일 수 있다. 데이터베이스에 저장하고 싶은 값이 객체의 필드가 하는 것 대신에 연산되는 경우에 필요하게 된다.
Employee POJO에 적용된 JPA 어노테이션
표 1은 Listing 6에서 각 필드에 적용된 어노테이션과 이 영속성 정보가 스프링 2 엔진에 어떤 의미를 주는지 설명하고 있다.
표 1. Employee POJO에 적용된 JPA 어노테이션필드 / 엘리먼트 | 어노테이션 | 설명 |
---|
Employee | @Entity | 이 클래스가 데이터베이스에 저장될 것을 나타냄. 기본적으로 클래스 이름이 테이블 이름으로 사용됨 |
empid | @Id | 이 필드가 테이블의 주 키 필드가 됨을 나타냄 |
empid | @GeneratedValue(strategy = GenerationType.TABLE) | 유일한 주 키 ID를 할당하는 영속성 엔진(persistence engine)에 의해 사용되는 전략을 기술함. GenerationType.TABLE 은 이식성 있고 테이블 기반 시퀀싱의 유일한 ID가 사용되어야 함을 나타낸다. 다른 데이터베이스 기반 옵션도 사용할 수 있으나 여러 개의 데이터베이스에서는 작동하지 않을 수 있다. |
empno | @Column(length = 6) | 이 필드에는 회사에서 할당한 직원 번호가 들어있다. 이 어노테이션은 이 필드가 주 키가 아님에 주의하라. 이 애플리케이션에서 주 키는 엔진에 의해 (자동) 생성되며, 관리됨. @Column 태그는 이 필드는 길이가 6자리 문자까지 될 수 있다는 것을 나타냄. 적절하게 길이가 기술된 필드는 테이블 크기를 작게 유지하는 것을 도와줌. |
firstName | @Column(name = "FIRSTNME") | 데이터베이스 테이블의 필드에 사용될 필드 이름을 기술함 |
midInitial | @Column(name = "MIDINIT") | 데이터베이스 테이블의 필드에 사용될 필드 이름을 기술함. 자바 필드 이름과는 차이가 있다는 것을 기억해야 함 |
lastName |
| 어노테이션이 없으므로, 필드 이름 "LASTNAME" 이 자바 필드 이름에 맞춰 사용될 것이다. |
phoneNumber | @Column(name = "PHONENO") | 데이터베이스 테이블에서 사용될 필드 이름을 기술함 |
job | @Column(length = 8) | 데이터베이스 필드의 길이를 기술함 |
educationLevel | @Column(name = "EDLEVEL") | 데이터베이스 필드 이름을 기술함 |
sex | @Column(length = 1) | 데이터베이스 필드의 길이를 기술함 |
salary | @Column(precision=12, scale=2) | 데이터베이스 필드 소수점의 십진수 정밀성을 기술함 |
bonus | @Column(precision=12, scale=2) | 데이터베이스 필드 소수점의 십진수 정밀성을 기술함 |
commission | @Column(precision=12, scale=2) | 데이터베이스 필드 소수점의 십진수 정밀성을 기술함 |
addr | @OneToOne(cascade = CascadeType.ALL) | 이 테이블과 또 다른 테이블에 매핑된 Address 객체의 관계를 기술함. cascade=ALL 은 추가, 수정, 삭제, 리프레시 가 될 경우 관련된 테이블에 모두 영향을 미친다는 것을 의미한다. |
hiredate | @Temporal(TemporalType.DATE) | 이 필드는 date 필드라는 것을 기술함(time이나 timestamp 필드가 아닌) |
birthdate | @Temporal(TemporalType.DATE) | 이 필드는 date 필드라는 것을 기술함(time이나 timestamp 필드가 아닌) |
Employee
와 Address
인스턴스가 일대일 관계(@OneToOne(casecade=CascadeType.ALL)
) 어노테이션으로 기술되어)를 맺고 있는 것을 기억하자. 이 어노테이션은 Employee
객체를 대상으로 하는 모든 엔티티 매니저 오퍼레이션이 그 객체와 관련된 Address
객체에도 영향을 준다는 것을 의미한다. 이 말은 RDBMS에 Employee
기록을 추가하는 어느 작업이든지 그에 대응하는 Address
기록도 만들어지는 것을 뜻한다. 이것은 RDBMS에서 종종 찾을 수 있는 연속되는 삭제 참조 무결성 개념의 확장된 개념이라 보면 되겠다. 실제로 연속되는 오퍼레이션을 수행할 때 자바 코드 양이 상당히 줄어드는 것을 쉽게 발견할 수 있으므로 더 이상 다수의 테이블에 걸쳐 영향을 미치는 오퍼레이션이 필요하지 않을 것이다.
어노테이션이 붙은 Employee
소스 코드는 JPA 엔티티 매니저가 Employee
와 Address
객체의 영속성 인스턴스를 관리하는 데 청사진(blueprint)이 된다. 이 방법이 실제 RDBMS와 직접적으로 작업하면서 생성해야 하는 지루하고, 에러가 나기 쉬운 물리적인 코드보다 엄청날 정도로 더 간단함을 발견할 것이다.
위 어노테이션을 기반으로 생성된 Employee
객체에 해당하는 데이터베이스 테이블은 Listing 7과 스키마가 비슷하다.
Listing 7. Employee POJO를 위한 동일한 데이터베이스 스키마
CREATE TABLE EMPLOYEE (
EMPID INTEGER NOT NULL,
EDLEVEL INTEGER,
SEX CHAR(1),
FIRSTNME VARCHAR(255),
SALARY DOUBLE,
LASTNAME VARCHAR(255),
BONUS DOUBLE,
JOB VARCHAR(8),
COMM DOUBLE,
MIDINIT VARCHAR(255),
HIREDATE DATE,
EMPNO VARCHAR(6),
BIRTHDATE DATE,
PHONENO VARCHAR(255),
ADDR_ID INTEGER,
PRIMARY KEY (EMPID),
FOREIGN KEY (ADDR_ID)
)
;
|
| 문자열 기반 필드의 길이
@Column(length=?) 어노테이션을 붙이지 않은 String 필드의 기본값은 VARCHAR(255) 임을 기억하자. 이것은 각 행마다 짧은 길이의 필드로 될 것을, 기본값으로 하면 저장 공간을 허비할 수 있다는 것을 보여준다. 실제 제품에서는 관리해야 하는 테이블 내의 저장 공간 할당을 좀 더 엄격하게 관리하고 싶을 것이다.
|
|
스프링 엔진에 의한 테이블을 생성하는 어노테이션의 효과를 보기 위해 Listing 7을 Listing 6에 있는 어노테이션이 붙은 Employee
클래스와 비교해 보자.
JPA에서 가능한 모든 어노테이션에 대한 더 자세한 설명과 기술을 보길 원한다면, JSR 220, EJB 3.0 명세의 최종 릴리스 문서를 참조하라(참고자료).
Address 객체를 위한 JPA 어노테이션
Listing 8 처럼, Address
POJO에도 비슷한 방법으로 어노테이션을 붙였다.
Listing 8. Address POJO를 위한 JPA 어노테이션
package com.ibm.dw.spring2;
import javax.persistence.Column;
...
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private long id;
@Column(name = "NUM")
private int number;
@Column(name = "STNAME", length=25)
private String street;
...
|
지금까지 모든 어노테이션을 잘 이해했을 것이다. Listing 8에 기술된 어노테이션과 매핑되는 테이블이 Listing 9에서 보여주는 스키마를 갖게 된다는 것은 놀랄 일도 아니다.
Listing 9. Address POJO에 동일하게 매핑되는 데이터베이스 스키마
CREATE TABLE ADDRESS (
ID INTEGER NOT NULL,
NUM INTEGER,
STNAME VARCHAR(25),
PRIMARY KEY (ID)
)
;
|
스프링 2와 자바 EE 5와의 관계
JPA 영속성(persistence)은 EJB 3.0의 일부이자 뒤이어 자바 EE 5 명세의 일부가 됨으로써 호환되는 모든 자바 EE 5 서버(상용, 오픈 소스, 기타)는 이에 따르는 구현을 갖게 되었다. 이 덕분에 가까운 장래에 견고한 고품질의 JPA 구현체를 사용할 수 있음이 실질적으로 보장됐다.
비록 스프링 2가 EJB 3.0 명세에 있는 JPA 영속성을 활용은 하지만, 스프링 2 사용자들이 EJB 3.0이나 자바 EE 5 명세의 다른 요소에 따라야 하는 의무는 없음에 주의하라.
JPA는 전통적인 EJB 컨테이너 외부에서 독립적으로 편리하게 쓸 수 있도록 고안되었다. 구체적인 예로서 이 튜토리얼의 애플리케이션은 JPA 덕분에 혜택을 누리지만, EJB 3.0이나 자바 EE 5 애플리케이션은 분명히 아니다.
출처 - http://www.ibm.com/developerworks/kr/library/tutorial/j-spring2/section4.html
스프링 DAO를 사용한 도메인 서비스 구현
이번 절에서는 직원 정보 애플리케이션의 서비스 인터페이스 구현을 위해 스프링 DAO(Data Access Object) API를 사용한다.
EmployeeService
인터페이스 구현하기
일단 스프링 2 엔진이 Employee
와 Address
객체의 인스턴스를 영속화하는 방식을 알면, EmployeeService
인터페이스 구현 작업은 상당히 간단해진다.
서비스 구현에 스프링 DAO API를 가져와 사용할 수 있다. 스프링 DAO는 잘 알려진 DAO 디자인 패턴(참고자료 참조)을 구현했다. 이 패턴에서 DAO는 데이터 접근을 위한 일관된 퍼사드(facade) 객체를 제공한다. 데이터를 받아오고 수정하는 작업은 객체 전송을 통해 수행된다. DAO는 실제 데이터 소스를 캡슐화해 객체 전송으로 작업을 할 수 있는 메서드를 제공한다.
아키텍처 관점으로 보면, DAO API는 실제 데이터 영속성 API를 호출해야 하는 작업의 복잡성으로부터 개발자들을 보호한다(JPA 외에도 스프링은 JDO, Hibernate, iBatis SQL Maps, 아파치 OJB와 같은 ORM을 지원한다). 스프링의 DAO를 이용해 이러한 영속성 API에 쉽게 채택할 수 있는 데이터 접근 코드를 작성할 수 있다.
데이터 영속성 API에 대한 추상화 외에도 스프링의 DAO는 벤더에 종속된 많은 데이터 접근 예외(data access exceptions)를 문서로 잘 설명되어 있는 스프링 데이터 접근 예외의 집합으로 변경해준다.
또한 스프링 DAO API는 확장할 수 있는 지원 클래스(support classes)를 제공한다. 이 클래스들을 확장함으로써 ORM 데이터 접근 코드를 작성해야 하는 지루하고, 에러 발생 위험이 큰 작업에서 해방될 수 있다. 필수적으로 쓰이는 코드는 전부 기본 클래스에 캡슐화되어 있으며, 라이브러리 클래스를 지원하고, 완벽히 테스트되어 있다. 이 클래스들은 종종 애플리케이션 로직으로 뒤범벅된 커넥션과 트랜잭션 관리 코드를 캡슐화해 준다. JPA 지원 클래스의 경우 JPA 엔티티 매니저 사용이 지원 클래스 내에서 완벽하게 캡슐화되어 있어, 엔티티 매니저와 엔티티 매니저 팩토리와의 직접적으로 관계된 작업에서 자유롭게 해준다.
실제 사용되는 코드를 보면 스프링 DAO API의 다재 다능함을 확신할 수 있을 것이다. Listing 10은 스프링 2의 JpaDaoSupport
클래스를 사용해 EmployeeDAO
라는 EmployeeService
인터페이스를 구현한 것이다.
Listing 10. 스프링 2의 JPA 지원 클래스를 사용한 EmployeeService 인터페이스 구현
import java.util.List;
import org.springframework.orm.jpa.support.JpaDaoSupport;
public class EmployeeDAO extends JpaDaoSupport implements EmployeeService {
public Employee findById(long id) {
return getJpaTemplate().find(Employee.class, id);
}
public List<Employee> findAll() {
return getJpaTemplate().find("select e from Employee e");
}
public List<Employee> findByEmployeeNumber(String empno) {
return getJpaTemplate().find(
"select e from Employee e where e.empno = ?1", empno);
}
public List<Employee> findByAddressStreetName(String street) {
return getJpaTemplate().find(
"select e from Employee e where e.addr.street = ?1", street);
}
public List<Employee> findByEmployeeLastName(String lastName) {
return getJpaTemplate().find(
"select e from Employee e where e.lastName = ?1", lastName);
}
public List<Employee> findEmployeeWithSalaryOver(double sal) {
return getJpaTemplate().find(
"select e from Employee e where e.salary > ?1", sal);
}
public List<Employee> findEmployeeWithCommissionOver(double comm) {
return getJpaTemplate().find(
"select e from Employee e where e.commission > ?1", comm);
}
public Employee save(Employee emp) {
getJpaTemplate().persist(emp);
return emp;
}
public Employee update(Employee emp) {
return getJpaTemplate().merge(emp);
}
public void delete(Employee emp) {
getJpaTemplate().remove(emp);
}
}
|
Listing 10에서, 첫 번째 기억할 것은 각각의 메서드 구현이 매우 간단하다는 것이다. JpaDaoSupport
클래스는 문제와 관련이 없지만, 문제 해결을 위해 일상적으로 해야 하는 지루한 많은 작업을 직접 관리해 준다. JpaTemplate
는 헬퍼 클래스(helper class)다.
- API 차이를 내부적으로 숨김
- 예외를 번역
- JPA 엔티티 매니저 관리
- 트랜잭션 관리 감싸기(wrap)
- 일관되고(모든 스프링 DAO 구현에 걸쳐) 잘 정제된 메서드를 위해 데이터 접근 표준화
표 2는 Listing 10에서 자주 사용되는 JpaTemplate
메서드를 요약하고 있다.
표 2. EmployeeDAO에 있는 JpaTemplate 메서드메서드 | 설명 |
---|
find(Class <T> cls, Object id); | 주어진 주 키로 영속성 인스턴스를 찾아 온다. |
find(String query); | 질의 문자열을 사용해 영속성 객체들을 찾아 옴. 여기에서 사용되는 질의는 EJB QL의 확장 버전으로 매우 강력한 질의 언어이며, 더 자세한 설명은 JSR-220에 있다(참고자료 참조). |
persist(Object obj); | 데이터베이스에 인스턴스를 저장함. JPA에서는 JPA 엔티티 매니저를 사용해 인스턴스를 영속화한다고 말한다. |
merge(Object obj); | 객체의 저장된 인스턴스를, 제공된 인스턴스에 있는 정보로 수정함 |
remove(Object obj); | 데이터베이스에 있는 영속성 인스턴스를 제거함 |
내부적으로 JpaTemplate
헬퍼 클래스는 모든 오퍼레이션을 처리하기 위해 JPA 엔티티 매니저를 사용한다. 이 헬퍼 클래스는 데이터 접근 작업 동안 엔티티 매니저의 종료와 데이터를 반환하는 일상적인 작업을 다룬다.
JpaTemplate
클래스에 있는 다른 메서드는 특별히 사용할 필요가 있을 때 매우 유용하게 쓰일 수 있다. 스프링 DAO API에 대한 더 자세한 정보는 JavaDoc을 참조하자(참고자료 참조).
Employee
와 Address
인스턴스를 영속화하는 능력과 EmployeeService
의 구체적인 구현을 사용할 수 있기 때문에, 이제 실제 관계형 데이터베이스와 연동해 모든 것을 테스트할 때다.
스프링 빈 묶기
여기까지 살펴보면서도 언제 어떻게 스프링 프레임워크가 POJO와 작업할 수 있는 기회를 실제로 얻는지 여전히 분명하지 않을 것이다. 데이터 접근에 관련된 부분이 제자리에 있지만 두 가지 의문이 남아있다. 바로 무슨 일을 해야 하는지 스프링 2 엔진은 어떻게 아는가와 어떤 관계형 데이터베이스를 사용해야 할지 어떻게 지정하는가이다.
빈 와이어링(beans wiring) 템플릿을 스프링 엔진에 어떻게 제공하는지를 보면, 순식간에 이 두 미스테리를 풀 수 있을 것이다. 비밀은 dwspring-service.xml이라는 XML 빈 기술서(beans descriptor) 파일에 있다. 이 빈 기술서 파일은 '스프링 2 프레임워크 오퍼레이션 살펴보기'에서 본 스프링에 사용되는 청사진(blueprint)을 묶는 일을 한다. 이 파일은 스프링 애플리케이션에 있는 다양한 빈 사이의 관계를 기술한다. 이 파일은 Listing 11에 나와있다.
Listing 11. dwspring-service.xml 빈 기술서
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="employeeService" class="com.ibm.dw.spring2.EmployeeDAO">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="entityManagerFactory" class=
"org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="generateDdl" value="true"/>
<property name="databasePlatform"
value="oracle.toplink.essentials.platform.database.HSQLPlatform"/>
</bean>
</property>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.SimpleLoadTimeWeaver"/>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:mem:dwspring"/>
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
|
EmployeeDAO
구현을 테스트하기 위해 HSQLDB라는 인 메모리(in-memory) RDBMS를 사용한다(참고자료 참조). HSQLDB 바이너리는 "스프링 2 dependencies" 배포판에서 받을 수 있다.
Listing 11에서는 HSQLDB의 인스턴스를 구성하는 데 특별히 필요한 줄만 굵은 글씨로 보여준다. 나중에("RDBMS로 통합 테스트를 위한 DAO 작성하기"에서) HQSLDB 대신 DB2 Express-C로 통합 테스트를 수행하기 위해 어떻게 이 정보를 수정하는지 보게 될 것이다.
EmployeeDAO
는 JpaDaoSupport
클래스를 실제로 확장하고 있다는 것을 기억하자. 이 클래스는 로딩되는 시점에 JPA EntityManagerFactory
가 "주입(injected)"되는 것으로 예상된다. 그러므로 모든 데이터 접근 오퍼레이션에서 JPA EntityManager
를 얻기 위해 이 팩토리를 사용할 수가 있다.
그림 6은 빈이 dwspring2-service.xml 파일 내에서 어떻게 함께 묶이는지를 시각적으로 보여준다.
그림 6. 빈 와이어링(wiring)을 위한 개요도 본질적으로 Listing 11은 스프링 2 엔진에 의해 생성되어야 하는 객체를 위한 묶기 계획이고, 그림 6은 이 객체들을 시각적으로 도식화해 보여준 것이다. Listing 11과 그림 6에서 기억해야 할 중요한 점은 EmployeeDAO
인스턴스가employeeService
라는 빈을 통해 어떻게 얻어질 수 있느냐는 것이다. EmployeeDAO
인스턴스는 entityManagerFactory
라는 또 다른 빈을 갖고 있는 entityManagerFactory
로 불리는 속성을 갖고 있다.
<bean id="employeeService" class="com.ibm.dw.spring2.EmployeeDAO">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
|
ref=""
표기법은 문맥(context)-보통 같은 파일-내에 정의되어 있는 또 다른 빈을 참조하는 것을 의미한다.
의존성 주입(Dependency injection)
외부에서 생성된 객체에 속성을 채워주는 것을 주입(injection)이라 부른다. 좀더 명확하게 말하면 주입되는 객체가 종종 특유의 오퍼레이션에 의존성을 갖게 되기 때문에 의존성 주입(DI: Dependency Injection)이다. DI는 스프링의 전체 아키텍처를 통틀어 매우 비중 있게 쓰인다. DI는 의존성을 갖는 서비스를 룩업하거나 찾을(예를 들어, EntityManagerFactory
를 룩업하는 것) 필요 없이 컴포넌트 코드를 작성할 수 있게 해준다. 대신 마치 의존하고 있는 객체를 이미 사용할 수 있는 준비가 된 것처럼 컴포넌트 코드를 작성하고, 실제 의존성은 스프링 엔진이 코드가 실행되기 전에 컴포넌트 인스턴스에 주입한다.
의존성 주입 응용
entityManagerFactory
를 묶는 Listing 11 부터 내용을 잘 이해했다면, 스프링에 의해 다음과 같은 의존성이 주입된다는 것을 기억할 것이다.
dataSource
jpaVendorAdapter
loadTimeWeaver
dataSource
빈은 org.springframework.jdbc.datasource.DriverManagerDataSource
의 인스턴스로 HSQLDB RDBMS의 인 메모리 인스턴스로 구성된다.
jpaVendorAdapter
속성에는 스프링 애플리케이션용 실제 JPA 구현체와 연결하는 빈이 주입된다. 이 예제의 경우 org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter
클래스를 통해 접근되는 JPA 참조 구현체를 사용한다. 다음 번에 이 클래스는 databasePlatform property
속성으로 구성되어야 한다. 이 속성은 oracle.toplink.essentials.platform.database.HSQLPlatform
가 할당되며, 이 구성은 HQSLDB RDBMS에 대한 접근을 지원한다. 이 빈의 generateDdl
속성은 데이터 정의 언어(Data Definition Language) 스크립트를 생성하고, 실행할 것인지를 다룬다. 이 속성에 true
값이 할당된 경우, 데이터베이스 스키마는 이 빈이 로드될 때마다 다시 생성된다. 통합 테스트를 하기 위해 이 속성을 true
로 해두길 바란다.
dataSource
빈의 설정으로 org.springframework.jdbc.datasource.DriverManagerDataSource
의 인스턴스가 생성되었다. 이 빈에는 다음과 같은 정보가 매개변수화되어 있다.
- HSQLDB 데이터베이스 드라이버
- 생성한 인 메모리 데이터베이스 JDBC URL(JDBC URL의 mem 부분)
- 사용자 이름과 패스워드(HSQLDB에서 기본값은 각각 sa와 ""이다)
마지막으로 의존성이 없는 transactionManager
빈은 차후에 통합 테스트에서 구성된다. 나중에 쓰일 테스팅 기반 클래스는 이 빈을 타입으로 찾기 때문에 이 빈은 아직 묶을 필요가 없다.
스프링 2의 빈 묶기에 대해 충분히 감이 왔을 것이다. 다음 절(RDBMS에 대해 수행하는 DAO 통합 테스트 작성하기)에서 다룰 단계인, 데이터베이스를 HSQLDB에서 DB2 Express-C로 어떻게 변경할 것인지에 대해서도 아이디어를 갖게 되었을 것이다.
출처 - http://www.ibm.com/developerworks/kr/library/tutorial/j-spring2/section5.html