2008년 12월 2일 화요일
MS의 시장지배력은 어느선까지 떨어질까?... 꿈꾸는자의 생각의 파편들...
[SA강좌] Part 4-13 Data Mapper패턴
데이터 매퍼(Data Mapper) 패턴
데이터 매퍼 패턴의 정의
매퍼의 레이어는 개체와 데이터베이스 사이에 데이터를 이동시킨다.
그림 Ⅴ-16. 데이터 매퍼 패턴의 구조
데이터 매퍼 패턴의 설명
개체와 관계형 데이터베이스는 데이터를 구조화하는 메커니즘이 서로 다르다. 집합과 상속과 같은 개체의 많은 부분들이 관계형 데이터베이스에는 존재하지 않는다.
데이터 매퍼는 데이터베이스에서 메모리 위에 있는 개체를 분리하는 소프트웨어 레이어 이다.
데이터 매퍼 패턴은 언제 사용하는가?
데이터 매퍼를 위한 주요한 이슈는 데이터베이스 스키마와 독립적으로 포함된 개체 모델을 원할 때이다.
데이터 매퍼 패턴의 예제 : 간단한 데이터베이스 매퍼
class Person
private String lastName;
private String firstName;
private int numberOfDenedents;
데이터베이스 스키마는 다음과 같다.
cateate table people (
ID int primary key,
lastname varchar,
firstname varchar,
number_of_dependnets int
)
class PersonMapper ...
protected String findStatement() {
return "SELECT " + COLUMNS +
" FROM people " +
" WHERE id = ? ";
}
public static final String COLUMNS = " id, lastname, firstname, number_of_dependents ";
public Person find(Long id) {
return (Person) abstractFind(id);
}
public find(long id) {
return find(new Long(id));
}
class AbstractMapper ...
protected Map loadedMap = new HashMap();
abstract protected String findStatment();
protected DomainObject abstractFind(Long id) {
DomainObject result = (DomainObject) loadedMap.get(id);
if (result != null) return result;
PreparedStatment findStatement = null;
try {
findStatement = DB.prepare(findStatment);
findStatement.setLong(1, id.longValue();
ResultSet rs = findStatement.executeQuery();
rs.next();
result = load(rs);
return result;
} catch(SQLException e) {
throw new ApplicationException(e);
} finally {
DB.cleanUp(findStatement);
}
}
검색 메서드는 로드 메서드를 호출한다.
class AbstractMapper ...
protected DomainObject load(ResultSet rs) throws SQLException {
Long id = new Long(re.getLong(1));
if (loadedMap.contractKey(id) return (DoaminObject) loadedMap.get(id);
DomainObject result = doLoad(id, rs);
loadedMap,put(id, result);
return result;
}
abstract protected DomainObject doLoad(Long id, ResultSet rs) throws SQLExecption;
class PersonMapper ...
protected DomainObject doLoad(Long id, ResultSet rs) throws SQLException {
String lastNameArg = rs.getString(2);
String firstNameArg = rs.getString(3);
int numDependentsArg = rs.getInt(4);
return new Person(id, lastNameArg, firstNameArg, numDependentsArg);
}
일치 맵(Identity Map) 패턴이 abstractFind와 load 메서드에 의해 두번 확인됨을 주의한다.
class PersonMapper ...
private static String findLastNameStatment =
" SELECT " + COLUMNS +
" FROM people " +
" WHERE UPPER(lastName) LIKE UPPER(?) " +
" ORDER BY lastname ";
public List findByLastName(String name) {
PreparedStatement stmt = null;
ResultSet rs = null;
try {
stmt = DB.prepare(findLastNameStatement);
stmt.setString(1, name);
rs = stmt.executeQuery();
return loadAll(rs);
} catch(SQLException e) {
throw new ApplicationException(e);
} finally {
DB.cleanUp(stmt, rs);
}
}
class AbstractMapper ...
protected loadAll(ResultSet rs) throws SQLException {
List result = new Array();
while (rs.next())
result.add(load(rs));
return result;
}
class AbstractMapper ...
public List findMany(StatementSource source) {
PreparedStatment stmt = null;
ResultSet rs = null;
try {
stmt = DB.prepare(source.sql());
for (int i = 0; i < source.paraments().length; i++)
stmt.setObject(i+1, source.paraments()[i]);
rs = stmt.executeQuery();
return loadAll(rs);
} catch(SQLException e) {
throw new ApplicationException(e);
} finally {
DB.cleanUp(stmt, rs);
}
interface StatmentSource ...
String sql();
Object[] paraments();
class PersonMapper ...
public List findByLastName2(String pattern) {
return findMany(new FindByLastName(pattern));
}
static class FindByLastName impletements StatemnetSource {
private String lastname;
public FindByLastName(String name) {
this.lastName = lastName;
}
public String sql() {
return
" SELECT + " COLUMNS +
" FROM people " +
" WHERE BY lastname ";
}
public object[] parameters() {
Object[] result = { lastName; }
return result;
}
}
class PersonMapper ..
private static final String updateStatementString =
" UPDATE people " +
" SET lastname = ?, firstname = ?, number_of_dependents = ?" +
" WHERE id = ? ";
public void update(Person subject) {
PreparedStatement updateStatement = null;
try {
updateStatement = DB.prepare(updateStatementString);
updateStatement.setString(1, subject.getLastName());
updateStatement.setString(2, subject.getFirstName());
updateStatement.setInt(3, subject.getNumberOfDependents());
updateStatement.setInt(4, subject.getID(),intValue());
updateStatement.execute();
} catch(Exception e) {
throw new ApplicationException(e);
} finally {
DB.cleanUp(updateStatement);
}
}
입력을 위한 몇몇 코드는 레이어 수퍼형에 보관된다.
class AbstractMapper ...
public Long insert(DomainObject subject) {
PreparedStatment updateStatement = null;
try {
insertStatement = DB.prepare(insertStatment());
subject.setID(findNextDatabaseId());
insertStatement.setInt(1, subject.getID().intValue());
doInsert(subject, insertStatement);
insertStatement.execute();
loadMap.put(subject.getID(), subject);
return subject.getID());
} catch(SQLException e) {
throw new ApplicationException(e);
} finally {
DB.cleanUp(updateStatemet);
}
}
abstract protected String insertStatement();
abstract protected void doInsert(DomainObject subject, PreparedStatement insertStatement)
throws SQLException;
class PersonMapper ...
protected String insertStatement() {
return "INSERT INTO people VALUES(?,?,?,?)";
}
protected void doInsert(DomainObject abstractSubject,
PreparedStatement stmt)
throws SQLException
{
Person subject = (Person) abstractSubject;
stmt.setString(2, subject.getLastName());
stmt.setString(3, subject.getFirstName());
stmt.setInt(4, subject.getNumberOfDependents());
}
2008년 12월 1일 월요일
작년 크리스마스 파티음식들... 올해에는 과연 어떤 것들을?
올해의 크리스마스에는 어떤 파티음식들이 만들어지려나...
크리스마스를 위한 케익과...
개인용 스테이크...
와인잔..
그리고, 셀러드...
나름 이쁘게 올라갔죠?
~.~
올해에도 멋진 크리스마스가 되어야 할텐데...
~.~
올해에는...
준비된 와인도 있겠다...
다른 메뉴들은 어떤 것들을 준비해야할려나...
스테이크 위에...
생크림도..
~.~
게맛살이 올라간...
맛깔스러운 셀러드도...
나름 향초로...
분위기를 만들어 볼려고 했는데...
~.~
이 메뉴는...
아이들을 위한 메뉴죠..
~.~
하나참치에서 나오는 참치초밥...
정말 맛깔스러운 참치집...
그리고...
멋진 느낌의 참치초밥...
윤기가 자르르 흐르는...
멋진 참치 초밥 아닌가요?
여러 각도로 찍고 싶었지만...
한자리에서 찍은 관계로...
~.~
좀더 줌인...
(후루룩... ㅇㅇ )
멋진 느낌이죠?
ㅇㅇ...
ㅡㅡ;
참치...
초밥...
먹고프다...