====== Entity Operation ======
ORM서비스를 이용하여 특정 DB에 데이터를 입력,수정,조회,삭제,배치입력하는 방법에 대해 알아보도록 한다.
===== 입 력 =====
EntityManager의 persist()메소드를 호출하여 DB에 단건의 데이터를 추가할 수 있다. persist() 메소드 호출시 대상이 되는 Entity를 입력 인자로 전달해야 한다.
=== Sample Source ====
private Department addDepartment() throws Exception {
// 1. insert a new Department information
Department department = new Department();
String DepartmentId = "DEPT-0001";
department.setDeptId(DepartmentId);
department.setDeptName("SaleDept");
department.setDesc("판매부서");
em.persist(department);
...
return department;
}
위의 예를 보면 EntityManager의 persist() 메소드에 department라는 Entity를 입력인자로 전달하여 처리하였다.
===== 수 정 =====
수정을 하고자 할때는 두가지 방법으로 가능한데 우선 EntityManager의 merge()메소드를 호출하여 DB에 단건의 데이터를 수정할 수 있고, 특정 객체가 Persistent 상태이고, 동일한 트랜잭션 내에서 해당 객체의 속성 값에 변경이 발생한 경우 merge() 메소드를 직접적으로 호출하지 않아도 트랜잭션 종료 시점에 변경 여부가 체크되어 변경 사항이 DB에 반영된다.
=== merge 호출 Sample Source ====
public void testUpdateDepartment() throws Exception {
// 1. insert a new Department information
Department department = addDepartment();
// 2. update a Department information
department.setDeptName("Purchase Dept");
// 3. 명시적인 메소드 호출
em.merge(department);
}
위의 예를 보면 EntityManager의 merge() 메소드에 department라는 Entity를 입력인자로 전달하여 처리하였다.
=== merge 호출없는 Sample Source ====
public void testUpdateDepartment() throws Exception {
// 1. insert a new Department information
Department department = addDepartment();
// 2. update a Department information
department.setDeptName("Purchase Dept");
// commit. successful update!!!
}
위의 예를 보면 setDeptName()을 통해서 변경하고 Commit 처리시 변경된다.
===== 조 회 =====
EntityManager의 find()메소드를 호출하여 DB에서 원하는 한건의 데이터를 조회할 수 있다. find() 메소드 호출시 대상이 되는 Entity의 Id를 입력 인자로 전달해야 한다.
=== Sample Source ====
private void assertDepartmentInfo(String departmentId, Department department)
throws Exception {
Department result = (Department) em.find(Department.class, departmentId);
//...
}
위의 예를 보면 EntityManager의 find() 메소드에 departmentId라는 Entity Id를 입력인자로 전달하여 처리하였다.
===== 삭 제 =====
EntityManager의 remove()메소드를 호출하여 DB에서 원하는 한건의 데이터를 조회할 수 있다. remove() 메소드 호출시 대상이 되는 Entity를 입력 인자로 전달해야 한다.
=== Sample Source ====
public void testDeleteDepartment() throws Exception {
// 1. insert a new Department information
Department department = addDepartment();
// 2. delete a Department information
em.remove(department);
}
위의 예를 보면 EntityManager의 remove() 메소드에 department라는 Entity를 입력인자로 전달하여 처리하였다. 그러나 주의할 점은 위의 예에서는 department 객체는 DB에 등록처리한 객체와 동일 객체이기에 그대로 remove를 쓸 수 있지만 키만 동일하게 신규로 객체를 만든경우에는 remove를 바로 쓸수 없다. 그럴 경우에는 아래와 같은 방법으로 처리해야 한다.
=== Sample Source ====
public void testDeleteDepartment() throws Exception {
Department department = new Department();
department.setDeptId = "DEPT_1";
// 2. delete a Department information
em.remove(em.getReference(Department.class, department.getDeptId()));
}
위의 예에서는 getReference 메소드를 호출하여 Entity의 Id에 해당하는 객체 정보를 추출하여 그정보를 입력인자로 해서 remove를 호출하였다.
===== 배치입력 =====
EntityManager의 persist()메소드를 호출하여 DB에 입력하고 loop를 통해 반복적으로 수행한다. OutOfMemoryException 방지를 위해서 일정한 term을 두고 flush(),clear()을 호출하여 메모리에 있는 사항을 삭제한다.
=== Sample Source ====
public void testMultiSave() throws Exception {
for (int i = 0; i < 900 ; i++) {
Department department = new Department();
String DeptId = "DEPT-000" + i;
department.setDeptId(DeptId);
department.setDeptName("Sale" + i);
department.setDesc("판매부" + i);
em.persist(department);
logger.debug("=== DEPT-000"+i+" ===");
// OutOfMemoryException 피하기 위해서
if (i != 0 && i % 9 == 0) {
em.flush();
em.clear();
}
}
}
===== Callback Methods =====
엔티티 클래스에 정의하거나 EntityListener를 지정하여 콜백함수를 정의하여 실제 엔티티 Operation 직전 직후에 비지니스 로직 체크등의 로직을 별도 분리하여 처리하도록 지원한다.
=== Callback Methods ===
^ 메소드명 ^ 설 명 ^ 관련 Operation ^
| PrePersist | Persist이전 시점에 수행 | [[.:entity_operation#입 력|persist]]|
| PostPersist | Persist이후 시점에 수행 | [[.:entity_operation#입 력|persist]]|
| PreRemove | Remove이전 시점에 수행 | [[.:entity_operation#삭 제|remove]]|
| PostRemove | Remove이후 시점에 수행 | [[.:entity_operation#삭 제|remove]]|
| PreUpdate | Update이전 시점에 수행 | [[.:entity_operation#수 정|merge]]|
| PostUpdate | Update이후 시점에 수행 | [[.:entity_operation#수 정|merge]]|
| PostLoad | Find 이후 시점에 수행 | [[.:entity_operation#조 회|find]]|
==== Entity에 직접 정의 ====
콜백 함수를 Entity 클래스에 직접 Annotation을 기재하여 Method를 정의할 수 있다.
=== Define Source - Entity 클래스에 정의 ====
@Entity
public class User {
@PrePersist
@PreUpdate
protected void validateCreate() throws Exception {
if (getSalary() < 2000000 )
throw new Exception("Insufficient Salary !");
}
}
위의 예를 보면 Persist, Update 이전 시점에 Salary가 2,000,000 이하가 되는지 여부를 체크를 하도록 한다. Update의 경우는 EntityManager의 merge()를 이용하여 하는 것과 ql을 이용하여 Update 수행하는 것 모두에 해당한다.
=== Sample Source - merge() 이용 케이스 ====
@Test
public void testUpdateFailUser() throws Exception {
newTransaction();
User getUser = (User) em.find(User.class, "User1");
assertEquals(getUser.getSalary(), sal );
user.setSalary(1000000);
em.merge(user);
// 2. Update User , 위의 merge() 호출이 아닌 Transaction 종료시 Update수행됨.
try{
closeTransaction();
}
catch( Exception e ){
e.printStackTrace();
assertTrue("fail to PreUpdate.",e instanceof Exception);
}
}
위의 예를 보면 salary가 2000000 이하로 설정되어 Update될 경우 Exception 이 발생하는 것을 알 수 있다.
=== Sample Source - ql을 통한 Update 케이스 ====
@Test
public void testUpdateFail2User() throws Exception {
newTransaction();
User getUser = (User) em.find(User.class, "User1");
StringBuffer ql = new StringBuffer();
ql.append("UPDATE User user ");
ql.append("SET user.salary = :salary ");
ql.append("WHERE user.userId = :userId ");
Query query = em.createQuery(ql.toString());
query.setParameter("salary", 1000000);
query.setParameter("userId",getUser.getUserId());
// 2. Update User , 위의 merge() 호출이 아닌 Transaction 종료시 Update수행됨.
try{
closeTransaction();
}
catch( Exception e ){
e.printStackTrace();
assertTrue("fail to PreUpdate.",e instanceof Exception);
}
}
위의 예를 보면 salary가 2000000 이하로 설정되어 Update될 경우 Exception 이 발생하는 것을 알 수 있다. ql를 이용한 Update를 처리할 때도 동일하게 Exception이 발생함을 확인할 수 있다.
==== EntityListener 정의 ====
엔티티 클래스에 EntityListener를 지정하고 EntityListener에서 Annotation을 기재하여 Method를 정의할 수 있다.
=== Define Source - EntityListener 클래스에 정의 ====
@Entity
@EntityListeners(egovframework.sample.model.callback.AlertMonitor.class)
public class User {
}
//위에서 정의한 AlertMonitor 클래스
public class AlertMonitor {
@PostPersist
public void newUserAlert(User user) {
System.out.println(user.getUserName()+" Created!");
}
@PostLoad
public void usrGetAlert(User user) {
System.out.println(user.getUserName()+" Get!");
}
}
정의하는 위치가 다르고 원래 엔티티를 매개변수로 넘겨야 하는 부분이 차이가 있지만 엔티티 클래스에 지정하는 것과 동일하게 작동한다.