꿈꾸는자의 생각의 파편들... :: '소프트웨어아키텍처' 태그의 글 목록 (2 Page)

달력

10

« 2018/10 »

  •  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  •  
  •  
  •  

내재된 값(Embedded Value) 패턴

내재된 값 패턴의 정의

내재된 값 패턴은 개체를 다른 개체의 테이블의 몇몇 필드들에 매핑한다.

그림 -26. 내재된 패턴의 구조

내재된 값 패턴의 설명

많은 작은 개체들은 테이터베이스의 테이블들 처럼 의미적이지 않은 개체지향 시스템 안에서 의미있게 한다. 예로 동시 발생 인식 메모리 개체와 일자 범위를 포함한다. 비록 기본적 생각은 개체를 테이블과 같이 저장하고, 같지 않은 사원은 메모리 값을 테이블이 원한다.

내재된 값은 개체의 값을 개체 소유자의 레코드에 있는 필드에 매핑한다.

내재된 값 패턴의 예제: 값 개체

내포된 값과 매핑되는 값 개체의 예는 아래와 같다. 다음과 같은 필드들을 가진 간단한 요청 클래스로 시작한다.

 

class ProductOffering ...

 

private Product product;

private Money baseCost;

private Integer ID;

위의 필드에서 ID 필드는 일치 필드(Identtity Field) 패턴을 사용하고, product 는 일반적 레코드 매핑이다.

 

class ProductOffering ...

 

public static ProductOffering load(ResultSet rs) {

try {

Integer id = (Integer) rs.getObject("ID");

BigDecimal baseCostAmount = rs.getBigDecimal("base_cost_amount");

Currency baseCostCurrency = Registry.getCurrency(rs.getString("base_cost_currency"));

Money baseCost = new Money(baseCostAmount, baseCostCurrency);

Integer productID = (Integer) rs.getObject("product");

Product product = Product.find(Integer) rs.getObject("product"));

return new ProductOffering(id, product, baseCost);

} catch(SQLException e) {

throw new ApplicationExcetion(e);

}

}

아래는 업데이트 행동이다. 다시 업데이터에서 간단한 변수이다.

 

class ProductOffering ...

 

public void update() {

PreparedStatment stmt = null;

try {

stmt = DB.prepare(updateStatmentString);

stmt.setBigDecimal(1, baseCost.amount);

stmt.setString(2, baseCost.currency().code());

stmt.setInt(3, ID.intValue());

stmt.execute();

} catch(Exception e) {

throw new ApplicationException(e);

} finally { DB.cleanUp(stmt);

}

private String updateStatementString =

" UPDATE product_offerings " +

" SET base_cost_amount = ?, base_cost_currency = ? " +

" WHERE id = ? ";

Posted by 꿈꾸는자의 생각의파편들

의존 매핑(Dependent Mapping) 패턴

의존 매핑 패턴의 정의

의존 매핑 패턴은 자식 클래스를 위한 데이터베이스 매핑을 수행하는 하나의 클래스를 가진다.

그림 -24. 의존 매핑 패턴의 구조

 

의존 매핑 패턴의 설명

몇몇 개체들은 자연적으로 다른 개체의 컨텍스트에 나타난다. Album을 추적하면 album 진행시 적재되거나 저장될 때 적재되거나 저장된다. 데이터베이스에서 임의의 테이블에 의해서 참조되지 않는다면, 추적을 위한 매핑 수행을 하는 앨범 매퍼에 의해 매핑 절차를 단순화 할 수 있다. 의존 매핑은 매퍼로 처리한다.

의존 매핑 패턴은 언제 사용하는가?

다른 개체에 의해 참조되는 개체를 가지고 있으면 의존 매핑을 사용한다. 이경우 하나의 개체가 의존 집합을 가지고 있을 때 일반적으로 발생한다.

의존 매핑을 위한 사전 조건은 다음과 같다.

  • 의존은 반드시 한명의 소유자만 있어여 한다.
  • 의존을 위한 소유자 보다 임의 개체에서 참조가 없어야 한다.

의존 매핑 패턴의 예제: Album과 Tracks

아래의 그림과 같은 도메인 모델에서 Album은 Track들의 집합을 소유한다. 이 경우 간단한 애플리케이션은 Track를 참고하기 위한 어떤 것도 필요하지 않다. 의존 매핑을 위한 분명한 후보이다.

그림 -25. 의존 매핑 패턴의 예제

track은 단지 title만을 가진다. 아래에 클래스에 대한 정의가 있다.

 

class Track ...

 

private final String title;

public Track(String title) {

this.title = title;

}

 

public String getTitle() {

return title;

}

 

track들은 album 클래스 안에 포함된다.

 

class Album ...

 

private List tracks = new ArrayList();

public void addTrack(Track arg) {

tracks.add(arg);

}

public void removeTrack arg) {

tracks.remove(arg);

}

public void removeTrack(int i) {

tracks.remove(i);

}

public Track[] getTracks() {

return (Track[]) tracks.toArray(new Track[tracks.size()]);

}

앨범 매퍼 클래스는 트랙을 위한 모든 SQL를 조작하고, 트랙 테이블 접근을 하는 SQL 문을

저장한다.

 

class AlbumMapper ...

 

protected String findStatment() {

return

" SELECT ID, a.title, t.title as trackTitle" +

" FROM ablums a, tracks t " +

" WHERE a.ID = ? AND t.albumID = a.ID "+

" ORDER BY t.seq ";

트랙은 앨범이 적재될때 마다 앨범 안에 로딩된다.

 

class AlbumMapper ...

 

protected DomainObject doLoad(Long id, ResultSet rs) throws SQLException {

String title = rs.getString(2);

Album result = new Album(id, title);

loadTracks(result, rs);

return result;

}

public void loadTracks(Album arg, ResultSet rs) throws SQLException {

arg.addTrack(newTrack(rs));

while (rs.next()) {

arg.addTrack(newTrack(rs));

}

}

 

private Track newTrack(ResultSet rs) throws SQLException {

String title = rs.getString(3);

Track newTrack = new Track(title);

}

앨범이 모든 트랙이 업데이트 될때 삭제된다.

 

class AlbumMapper ...

 

public void update(DomainObject arg) {

PreparedStatment updateStatment = null;

try {

updateStatement = DB.prepare("UPDATE albums SET title = ? WHERE id = ?");

updateStatement.setLong(2, arg.getID().longValue();

Album album = (Album) arg;

updateStatement.setString(1, album.getTitle());

updateStatement.execute();

updateTracks(album);

} catch(SQLException e) {

throw new ApplicationException(e);

} finally { DB.cleanUp(updateStatement);

}

}

public void updateTracks(Album arg) throws SQLException {

PreparedStatement deleteTrackStatement = null;

try {

deleteTrackStatement = DB.prepare("DELETE from tracks WHERE albumID = ?");

deleteTrackStatement.setLong(1, arg.getID().longValue();

deleteTrackStatement.execute();

for (int i = 0; i < arg.getTacks().length; i++) {

Track track = arg.getTracks()[i];

insertTrack(track, i + 1, arg);

 

} catch(SQLException e) {

throw new ApplicationException(e);

} finally { DB.cleanUp(deleteTrackStatement);

}

}

public void insert(Track track, int seq, Album album) throws SQLException {

PreparedStatment insetTrackStatment = null;

try {

insetTrackStatment =

DB.prepare("INSERT INTO tracks(seq, albumID, title) VALUES(?,?,?)");

insetTrackStatment.setLong(1, seq);

insetTrackStatment.setLong(2, album.getID().longValue());

insetTrackStatment.setString(3, track.getTitle());

insetTrackStatment.exeute();

} catch(SQLException e) {

throw new ApplicationException(e);

} finally { DB.cleanUp(insetTrackStatment);

}

}

Posted by 꿈꾸는자의 생각의파편들

연관 테이블 매핑(Association Table Mapping) 패턴

연관 테이블 매핑 패턴의 정의

연관 테이블 매핑은 외래키를 갖는 테이블을 연관 관계의 테이블에 저장하는 패턴이다.

그림 -23. 연관 테이블 매핑 패턴의 구조

연관 테이블 매핑 패턴의 설명

개체는 다중값 필드들을 필드 값처럼 집합(collection)을 사용하여 쉽게 조작할 수 있다. 관계형 데이터베이스는 이러한 특성이 없고, 단일 필드 값에 의해서 제약된다. 일-대-일 관계를 매핑할 때는 외래키 매핑(Foreign Key Mapping)을 이용하여 처리 할 수 있다. 근본적으로 연관의 단일 값을 위한 외래키를 사용한다. 그러나, 다-대-다 관계는 이와 같이 할 수 없다. 왜냐하면 외래키를 가지는 단일 값이 없기 때문이다.

이러한 문제의 해결은 분해를 위해 관계의 데이터를 사용하는 전통적 해결 방법을 사용하는 것이다. 즉, 레코드 관계에서 추출 테이블을 생성하는 것이다. 이후 연관 테이블 매핑을 이러한 링크 테이블의 다중값 필드에 매핑에 사용하는 것이다.

연관 테이블 매핑 패턴은 언제 사용하는가?

연관 테이블 매핑을 위한 정규화의 경우는 다-대-다 관계이다. 이러한 이유는 이 경우에 대한 다른 어떤 대안이 없기 때문이다.

연관 테이블 매핑은 연관의 다른 형식을 위해 사용된다. 그러나, 외래키 매핑과 외부 조인을 포함하는 것 보다 더 복잡한하기 때문에 좋은 선택이 아니다.

관계형 데이터베이스에서 관계에 대한 정보를 수행하는 연관 테이블은 자주 설계된다. 예를 들어서 사원의 회사에 대한 고용에 대한 정보를 담고있는 사원/회사 관계 테이블이 있다. 이 경우 사원/회사 테이블은 도메인 개체에 대응된다.

연관 테이블 매핑 패턴의 예제: 직접 SQL 사용

데이터베이스에 직접적으로 접근을 할때, 질의를 최소화하는 것이 중요하다. 아래는 테이블을 위한 DDL 을 보여주고 있다.

 

create table employee(ID int primary key, firstname varchar, lastname varchar)

create table skills(ID int primary key, name varchar)

create table employeeSkills(empoyeeID int, skillID int, primary key(employeeID, skillID))

단일 사원 로딩를 위해 아래와 같은 비슷한 방법을 사용한다. 사원 매퍼는 레이어 수퍼형(Layer Sypertype)에서 추상 메서드를 위해 비슷한 지정을 정의한다.

 

class EmployeeMapper ...

 

public Employee find(long key) {

return find(new Long key);

}

public Employee find(Long key) {

retrun (Employee) abstractFind(key);

}

protected String findStatement() {

return "SELECT " + CLUMN_LIST + " FROM employee WHERE ID = ? ";

}

public static final String CLUMN_LIST = " ID, lastname, firstname ";

 

class AbstractMapper ...

 

protected DomainObject abstractFind(Long id) {

DomainObject result = (DomainObject) loadMap.get(id);

if (result != null) return result;

PreparedStatment stmt = null;

ResultSet rs = null;

 

try {

stmt = DB.prepare(findStatment());

stmt.setLong(1, id.longValue());

rs = stmt.executeQuery();

rs.next();

result = load(rs);

return result;

} catch (SQLException e) {

throw new ApplicationException(e);

} finally {DB.cleanUp(stmt, rs);

}

}

abstract protected String findStatment();

proteceted Map loadedMap = new HashMap();

이후 find 메서든 로드 메서드를 호출한다. 추상 로드 메서드는 employee를 위한 실제 데이터은 employee의 매퍼에서 로드되고 있는 동안ID 로딩을 처리한다.

 

class AbstractMapper ...

 

protected DomainObject load(ResultSet rs) throws SQLException {

Long id = new Long(rs.getLong(1));

return load(id, rs);

}

public DomainObject load(Long id, ResultSet rs) throws SQLException {

if (hashLoaded(id)) return (DomainObject) loadedMap.get(id);

DomainObject result = doLoad(id, rs);

loadedMap.put(id, result);

return result;

}

abstract protected DomainObject doLoad(Long id, ResultSet rs) throws SQLException;

 

class EmployeeMapper ...

 

protected DomainObject doLoad(Long id, ResultSet rs) throws SQLException {

Employee result = rew Employee(id);

result.setFirstName(rs.getString("firstname"));

result.setLastName(rs.getString("lastname"));

result.setSills(loadSills(id));

return result;

}

사원은 스킬 로드를 위해 다른 질의가 필요하다. 그러나, 이 질의는 단일 질의에서 모든 스킬들을 쉽게 로드할 수 있다. 이러한 일을 하는 것은 특정 스킬을 위한 데이터에서 로드를 위한 스킬 매퍼를 호출한다.

 

class EmployeeMapper ...

 

protected List loadSkills(Load employeeID) {

PreparedStatment stmt = null;

ResultSet rs = null;

 

try {

List result = new ArraryList();

stmt = DB.prepare(findSkilsStatment);

stmt.setObject(1, employeeID());

rs = stmt.executeQuery();

while (rs.next()) {

Long skillId = new Long(rs.getLong());

result.add(Skill) MapperRegistry.skill().loadRow(skillId, rs);

}

return rsult;

} catch(SQLException e) {

throw new ApplicationException(e);

} finally {DB.cleanUp(stmt, rs);

}

}

private static final String findSkillsStatment =

"SELECT skill.ID " + skillMapper.CULUM_LIST +

" FROM skills skill, employeeSkills es " +

" WHERE es.employeeID = ? AND skill.ID = es.skillID";

 

class SkillMapper ....

 

public static final String COLUMN_LIST = " skill.name skillname";

 

class AbstractMapper ...

 

protected DomainObjct loadRow(Long id, ResultSet rs) throws SQLException {

return load(id, rs);

}

 

class SkillMapper ...

 

protected DomainObject loadRow(Long id, ResultSet rs) throws SQLException {

return load(id, rs);

}

 

class SkillMapper ...

 

protected DomainObject doLoad(Long id, ResultSet rs) throws SQLException {

Skill result = new Skill(id);

result.setName(rs.getStrig("skillName");

return result;

}

추상 매퍼는 또한 employee 검색을 돕니다.

 

class EmployeeMapper ...

 

public List findAll() {

return findAll(findAllStatment);

}

 

private static find String findAllStatment =

" SELECT " + COLUMN_LIST +

" FROM employees employee " +

" ORDER BY employee.lastname ";

 

class AbstractMapper ...

 

protected List find(String sql) {

PreparedStatment stmt = null;

ResultSet ts = null;

 

try {

List result = new ArraryList();

stmt = DB.prepared(sql);

rs = stmt.executeQuery();

while (rs.next())

result.add(load(rs));

return result;

} catch(SQLException e) {

throw new ApplicationException(e);

}

}

연관 테이블 매핑 패턴의 예제:다중 사원을 위한 단일 질의를 사용한다.

 

class EmployeeMapper ...

 

protected String findStatment() {

return

"SELECT " + COLUMN_LIST +

" FROM employees employee, skills skill, employeeSkills es" +

" WHERE employee.ID = es.employeeID AND skill.ID = es.skillID AND employee.ID = ?";

}

 

public static final String COLUML_LIST =

" employee.ID, employee.lastname, employee.firstname " +

" es.skillID, es.employeeID, skill.ID skillID " +

SkillMapper.COLUMN_LIST;

 

class EmployeeMapper ...

 

protected DomainObject doLoad(Long id, ResultSet rs) throws SQLException {

Employee result = (Employee) loadRow(id, rs);

result.setFirstName(rs.getString("firstname");

result.setLastName(rs.getString("lastname");

return result;

}

 

private boolean rowIsForSameEmployee(Long id, ResultSet rs) throws SQLException {

return id.equals(new Long(rs.getLong(1)));

}

 

private void loadSkillData(Employee person, ResultSet rs) throws SQLException {

Long skillID = new Long(rs.getLong("skillID");

person.addSkill((skill)MapperRegistry.skill().loadRow(skillId, rs);

}

 

 

class EmployeeMapper ...

 

 

public List findAll() {

return findAll(findAllStatment);

}

private static final String findAllStatment =

"SELECT " + COLUMN_LIST +

" FROM employees employee, skills skill, employessSkills es " +

" WHERE employee.ID = es.employee AND skill.ID = es.skillID " +

" ORDER BY employee.lastname ";

proteceted List findAll(String sql) {

AssociationTableLoader loader = new AssociationTableLoader(this, new SkillAdder());

return loader.run(findAllStatment);

}

 

class AssociationTableLoader ...

 

private AbstractMapper sourceMapper;

private Adder targetAdder;

public AssociationTableLoader(AbstractMapper primaryMapper, Adder targetAdder) {

this.sourceMapper = primaryMapper;

this.targetAdder = targetAdder;

}

 

class AssociationTableLoader ...

 

protected List run(String sql) {

loadData(sql);

addAllNewObjectToIdentityMap();

return formResult();

}

 

class AssociationTableLoader ...

 

private ResultSet rs = null;

private void loadData(String sql) {

PreparedStatment stmt = null;

try {

stmt = DB.prepared(sql);

rs = stmt.executeQuery();

while (rs.next()) loadRow();

} catch(SQLException e) {

throw new ApplicationException(e);

} finally(DB.cleanUp(stmt, rs);

}

}

 

class AssociationTableLoader ...

 

private List resultIds = new ArraryList();

private Map inProcess = new Hashmap();

private void loadRow() throws SQLException {

Long ID = new Long(rs.getLong(1));

if (!resultIds.contains(ID)) resultIds.add(ID);

if (!sourceMapper.hasLoaded(ID)) {

if(!inProcess.keySet().contains(ID))

inProcess.put(ID, sourceMapper.loadRow(ID,rs);

}

}

 

class AbstractMapper ...

 

boolean hasLoaded(Long id) {

return loadedMap.containsKey(id);

}

 

class EmployeeMapper ...

 

private static class SkillAdder implements AssociationTableLoader.Adder {

public void add(DomainObject host, ResultSet rs) throws SQLException {

Employee emp = (Employee) host;

Long skillId = new Long(rs.getLong("skillId"));

emp.addSkill((skill)MapperRegistry.skill().loadRow(skillId, rs);

}

 

 

class AssociationTableLoader ...

 

private void addAllNewObjectsToIdentityMap() {

for (Iterator it = inProcess.values().iterator();it.hasNext();)

sourceMapper.putAsLoader((DomainObject)it.next());

}

 

class AbstractMapper ...

 

void putAsLoaded(DomainObject obj) {

loadedMap.put(obj.getID(), obj);

}

 

class AssociationTableLoader ...

 

private List fromResult() {

List result = new ArraryList();

for (Iterator it = resultIds.iterator(); it.hasNext();) {

Long id = (Long)it.next();

result.add(sourceMapper.lookUp(id));

}

return result;

}

 

class AbstractMapper ...

 

protected DomainObject lookUp(Long id) {

return (DomainObject) loadedMap.get(id);

}

Posted by 꿈꾸는자의 생각의파편들

외래키 매핑(Foreign Key Mapping) 패턴

외래키 매핑 패턴의 정의

외래키 매핑 패턴은 개체들 사이의 연관성을 테이블 사이의 외래 키 참조에 매핑한다.

그림 -21. 외래키 매핑 패턴의 구조

외래키 매핑 패턴의 설명

개체들은 개체 참조에 의해서 직접적으로 서로를 참조한다. 심지어 매우 간단한 개체지향 시스템은 의도된 서로 연결된 개체들을 포함한다. 이들 개체들을 데이터베이스에 저장하기 위해 개체 참조를 저장하는 것은 중요하다. 그러나, 데이터는 실행 프로그램의 한정된 인스턴스로 한정되므로 행 데이터 값을 단지 저장 할 수 없다. 좀더 복잡한 것은 개체들은 다른 개체들을 참조하는 집합을 포함한다. 외래키 매핑은 개체 참조를 데이터베이스에 있는 외래키와 매핑한다.

왜래키 매핑 패턴은 언제 사용하는가?

외래키 매핑은 클래스들 사이에서 거의 모든 연관성을 위해 사용된다. 가장 공통적인 경우는 다-대-다 연관성이다. 외래키는 단일 값이고, 첫번째 전형적인 형식은 단일 필드에 다중 외래키를 저장할 수 없다.

만일 후진 포인터 없는 필드를 포함하고 있다면 많은 면이 의존성 매핑(Dependent Mapping) 인지를 고려해야한다. 이 경우 집합 조작은 간단하다. 만일 관련된 개체가 값 개체 패턴(Value Object)이면 내포 값(Embedded Value) 패턴을 사용할 수 있다.

왜래키 매핑 패턴의 예제:단일 값 참조

아래의 예제는 비교적 간단한 경우이고, album은 artist를 단일 참조한다.

 

class Artist ...

 

private String name;

public Artist(Long ID, String name) {

super(ID);

this.name = name;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

 

class Alum ...

 

private String title;

private Artist artist;

public Album(Long ID, String title, Artist artist) {

super(ID);

this.title = title;

this.artist = artist;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

public Artist getArtist() {

return artist;

}

public void setArtist(Artist artist) {

this.artist = artist;

}

 

그림 -22. 단일 값 필드 로딩을 위한 sequence diagram

그림 -22은 어떻게 앨범이 로드를 하는지를 보여준다. 앨범 매퍼는 질의어에 대한 데이터베이스에서 특정한 앨범 로드를 알린다. 그리고, 결과 집합을 되돌려준다. 이후 개별 외래키 필드를 위한 결과 집합을 질의한다. 그리고, 해당 개체를 찾는다. 적절한 발견된 개체를 가진 앨범을 생성한다. 만일 아티스트 개체가 이미 메모리에 있다면 캐쉬에서 가져온다. 그러므로, 같은 방법으로 데이터베이스에서 로딩된다.

find 오퍼레이션은 구분 맵(Identity Map)을 조작하기 위해 추상적 행동을 사용한다.

 

class AlbumMapper ...

 

public Album find(Long id) {

return (Album) abstractFind(id);

}

protected String findStatement() {

return "SELECT ID, title, artistID FROM albums WHERE ID = ?";

}

 

class AbstractMapper ...

 

abstract protected String findStatement();

protected DomainObject abstractFind(Long id) {

DomainObject result = (DomainObject) loadedMap.get(id);

if (result != null) return result;

PreparedStatement stmt = null;

ResultSet rs = null;

try {

stmt = DB.prepare(findStatment);

stmt.setLong(1, id.longValue());

rs = stmt.executeQuery();

rs.next();

result = load(rs);

return result;

} catch(SQLException e) {

throw new ApplicationException(e);

} finally { cleanUp(stmt, rs); }

}

 

private Map loaderMap = new HashMap();

 

find 오퍼레이션은 album에 데이터를 로딩을 위해서 로딩 오퍼레이션을 호출한다.

 

class AbstractMapper ...

 

protected DomainObject load(ResultSet rs) throws SQLException {

Load id = new Long(rs.getLong(1));

if (loadedMap.containsKey(id)) return (DomainObject)loadeMap.get(id);

DomainObject result = doLoad(id,rs);

doRegister(id, result);

return result;

}

 

protected void doRegister(Long id, DomainObject result) {

Assert.isFalse(loadedMap.containsKey(id));

loadedMap.put(id, result);

}

abstract protected DomainObject doLoad(Long id, ResultSet rs) throws SQLException;

 

class AlbumMapper ...

 

protected DomainObject doLoad(Long id, ResultSet rs) throws SQLException {

String title = rs.getString(2);

long artisID = rs.getLong(3);

Artist artist = MapperRegistry.artist.find(artistID);

Album result = new Album(id, title, artist);

}

album 외래키 값을 업데이트하는 것은 연결된 artist 개체에서 얻어진다.

 

class AbstractMapper ...

 

abstract public void update(DomainObject arg);

 

class AlbumMapper ...

 

public void upate(DomainObject arg) {

PreparedStatement statement = null;

try {

statement = DB.prepare("UPDATE album SET title = ?, artistID = ? WHERE id = ?");

statement.setLong(3, arg.getID().longValue());

Album album = (Album) arg;

statement.setString(1, ablum.getTitle());

statement.setLong(2, album.getArtist().getID().longValue());

statement.execute();

} catch(SQLException e) {

throw new ApplicationException(e);

} finally {

cleanUp(statment);

}

}

Posted by 꿈꾸는자의 생각의파편들

일치 맵(Identity Map) 패턴

일치 맵 패턴의 정의

일치 맵 패턴은 맵에 로드된 모든 개체의 유지에 의하여 개별 개체는 한번만 로드됨을 확신한다. 이러한 개체들을 참고 할 때 맵을 사용한 개체 검색을 하는 패턴이다.

그림 -18. 일치 맵 패턴의 구조

일치 맵 패턴의 설명

일치 맵 패턴은 단일 비즈니스 트랜잭션에 있는 데이터베이스에서 읽은 모든 개체의 패턴을 유지 시킨다. 객체를 원하는 경우에는 이미 가지고 있다면 일치 맵 패턴을 처음에 확인한다.

일치 맵 패턴의 사용

  • 비일치성을 막는다.
  • 수행성을 향상시킨다.

일반적으로 일치 맵 패턴은 데이터베이스에서 가져온 개체를 관리하기 위해 사용한다. 중요한 이유는 메모리 상에 두개의 개체가 하나의 데이터베이스 레코드에 대응 되기를 원하지 않기 때문이다. 만일 비 일관성적으로 두개 레코드를 수정한다면 데이터베이스 매핑은 혼란을 가져온다.

다른 이유는 데이터베이스를 읽기 위한 캐쉬와 같은 행동을 한다.

 

일치 맵 패턴의 예제

일치 맵을 위한 맵 필드와 접근자들은 다음과 같다.

 

private Map people = new HashMap();

public static void addPerson(Person arg) {

soleInstance.people(arg.getId(), arg);

}

 

public static void getPerson(Long key) {

return (Person) soleInstance.people.get(key);

}

 

public static void addPerson(Person arg) {

return getPerson(new Long(key));

}

자바(Java)의 애로사항 중에 하나는 long 이 객체가 아니라는 것이다. 이러한 이유로 맵을 위한 인덱스로 사용할 수 없다.

Posted by 꿈꾸는자의 생각의파편들

작업 단위(Unit of Work) 패턴

 

작업 단위 패턴의 정의

비즈니스 트랜잭션의 영향 받은 개체의 리스트를 관리하고, 변화의 기록과 동시 발생 문제의 해결를 조정하는 패턴이다.

그림 -17. 작업 단위 패턴의 구조

 

작업 단위 패턴의 설명

데이터베이스 내부와 외부로 데이터를 가지고 오는 경우 변화를 추적하는 것은 중요하다. 즉, 데이터가 데이터베이스에 쓰이지 않은 경우 이다. 비슷하게 새로운 개체를 삽입하면 개체를 생성하고, 임의의 개체를 삭제하면 개체를 삭제해야 한다.

작업 단위 패턴은 데이터베이스에 영향 받을 수 있는 비즈니스 트랜잭션 동안 모든 작업의 추적을 유지한다. 작업의 결과와 같은 데이터베이스 변경에 필요한 모든 작업을 설명한다.

작업 단위 패턴의 사용 이유

  • 수행성 향성
  • 비일관성을 제거
  • 참조의 무결성 관리
  • 데드락을 막음
  • 배치 업데이트를 도움

작업 단위 패턴은 어떻게 작업되는가?

  • 작업 단위의 개체를 추적하는 기술
  • 호출자 기록
  • 개체 기록
  • 작업 단위 컨트롤

작업 단위 패턴을 언제 사용하는가?

작업 단위 패턴을 다루는 근본적인 문제는 다양한 개체를 추적하는 것이다.

 

작업 단위 패턴의 예제 : 개체 기록을 이용한 작업 단위 패턴

작업 단위 패턴은 제공된 비즈니스 트랜잭션의 모든 변화를 추적할 수 있고, 명령이 전달되면 데이터베이스에 저장한다. 도메인 레이어는 레이어 수퍼타입 패턴에 해당하는 DomainObject를 가지고 있다. 이 패턴은 작업 단위 패턴 사이에 상호 작용을 하도록 한다. 설정 변화를 기록하기 위해 세 가지의 리스트 도메인 개체(new, dirty, removed)를 사용한다.

 

class UnitOfWork …

 

private List newObjects = new ArrayList();

private List dirtyObjects = new ArrayList();

private List removeObjects = new ArrayList();

기록 메서드는 위의 리스트의 상태를 관리한다. 리스트는 ID가 널이 아닌지를 확인하는 것 또는 dirty 개체가 새로 등록이 되지 않은지 같은 기본적인 명령을 수행한다.

 

public registerNew(DomainObject obj) {

Assert.notNull("id not null", obj.getId());

Assert.isTrue("object not dirty", !dirtyObjects.contains(obj));

Assert.isTrue("object not removed", !dirtyObjects.contains(obj));

Assert.isTrue("object not already registered new ", !newObjects.contains(obj));

newObjects.add(obj);

}

 

public void registerDirty(DomainObject obj) {

Assert.notNull("id not null, obj.getId());

Assert.IsTrue("object not removed", !removedObjects.contain(obj));

if(!ditryObjects(obj) && !newObjects.contains(obj)) {

dirtyObjects.add(obj);

}

}

 

public void registerRemoved(DomainObject obj) {

Assert.notNull("id not null, obj.getId());

if(newObjects.remov(obj)) return;

dirtyObjects.remove(obj);

if(!removedObjects.contains(obj)) {

removedObjects.add(obj);

}

}

public void registerClean(DomainObjects obj) {

Assert.notNull("id not null, obj.getId());

}

registerClean() 메서드는 여기서 아무일도 하지 않음을 주목하자. 공통 활동은 작업 단위 패턴에서 구분 맵 확인 패턴에 위치한다. 맵 확인은 도메인 개체를 메모리에 저장하기 위해 시간이 필요하다. 왜냐하면 같은 개체의 다중 복사는 정의되지 않은 행동에 있기 때문이다.

cmmit() 메서드는 개별 개체를 위한 데이터 매퍼 패턴을 위치시키고, 적절한 매핑 메서드를 포함한다. updateDirty()와 deleteRemoved() 메서드는 보이지 않지만, 기대되는 insertNew() 메서드와 같은 행동은 하지 않는다.

 

class UnitOfWork ...

 

public void commit() {

insertNew();

updatedirty();

deleteDirty();

}

 

private void insertName() {

for(Iterator objects = newObjects.iterator(); objects.hasNext();) {

DomainObject obj = (DomainObject) objects.next();

MapperRegistry.getMapper(obj.getClass()).insert(obj);

}

}

단위 작업 패턴에 포함되지 않은 것은 비일관성 오류 확인을 위한 읽기를 하는 임의의 개체의 추적이다. 최적 오프라인 록(Optimistic Offline Lock) 패턴에서 설명된다.

다음으로 필요한 것은 개체 등록이다. 첫째로 각 도메인 개체는 현재 비즈니스 트랜잭션을 제공을 위한 단위 작업이 필요하다.

 

class UnitOfWork ...

 

private static ThreadLocal current = new ThreadLocal();

public static void newCurrent() {

setCurrent(new UnitOfWork());

}

public static void setCurrent(UnitOfWork uow) {

current.set(uow);

}

public static UnitOfWork getCurrent() {

return (UnitOfWork) current.get();

}

추상 도메인 개체의 확인 메서드는 현재 작업 단위 패턴의 등록을 제공한다.

 

class DomainObject ...

 

protected void markNew() {

UnitOfWork.getCurrent().registerClean(this);

}

protected void markClean() {

UnitOfWork.getCurrent().registerClean(this);

}

protected void markDirty() {

UnitOfWork.getCurrent().registerClean(this);

}

protected void markRemoved() {

UnitOfWork.getCurrent().registerClean(this);

}

new와 dirty 개체를 마크를 기억하기 위한 도메인 개체를 지정한다.

 

class Album ...

 

public static Album create(String name) {

Album obj = new Album(IdGenerator.nextId(), name);

obj.markNew();

return obj;

}

 

public void setTitle(String title) {

this.title = title;

mark.Dirty();

}

제거된 개체의 등록은 추상 도메인 개체에 있는 remove() 메서드에 의해 다룰 수 있는 것은 보이지 않는다. 또한, Data Mapper 패턴에 registerClean() 메서드를 구현하는 경우 임의 새로운 로드된 개체를 등록은 필요하다.

마지막 작업은 작업 단위 패턴을 적절한 시점에 등록하고, 커미트하는 것이다. 이러한 작업은 명확하거나 불명확할 수 있다. 아래는 작업 단위 패턴의 명확한 코드를 보여준다.

 

clsss EditAlbumScript ...

 

public static void updateTitle(Long album, String title) {

UnitOfWork.newCurrent();

Mapper mapper = MapperRegistry.getMapper(Album.class);

Album album = (Album) mapper.find(albumId);

album.setTitle(title);

UnitOfWork.getCurrent().commit();

}

위의 코드는 어플리케이션의 가장 간단한 예이다. 불명확한 작업 단위 관리는 반복적 코딩을 피하는 것 만큼이나 적절할 수 있다.

 

class UnitOfWorkServlet ...

 

final protected void doGet(HttpServlet request, HttpServletRequest response)

throws ServletException, IOException {

try {

UnitOfWork.newCurrent();

handleGet(request, response);

UnitOfWork.getCurrent.commit();

} finally {

UnitOfWork.setCurrent(null);

}

abstract void handleGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException;

위의 서블릿 예제는 시스템 트랜잭션 제어에서 분명히 매우 간단하다. 만일 Font Controller 패턴을 사용하면, doGet() 보다 명령어들 주위의 작업 단위 관리를 지정하기 쉬울 수 있다.

Posted by 꿈꾸는자의 생각의파편들

데이터 매퍼(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());

}

Posted by 꿈꾸는자의 생각의파편들

활성화 레코드(Active Record) 패턴

활성화 레코드 패턴의 정의

 

데이터베이스 테이블 또는 뷰에 있는 행을 포함하는 객체, 데이터베이스 접근을 포함하고, 데이터에 있는 도메인 로직을 추가한다.

 

그림 -15. 활성화 레코드 패턴의 구조

활성화 레코드 패턴의 설명

 

개체는 데이터와 행동 두 가지를 전달한다. 대부분의 이 데이터는 유지되고, 데이터베이스에 저장 되어 진다. 활동 레코드는 도메인 개체에 데이터 접근 로직을 추가하는 가장 분명한 접근으로 사용 된다. 이 방법은 모든 작업자들이 데이터베이스에서 데이터를 어떻게 읽고 쓰는지를 인식하고 있다.

활성화 레코드 패턴 패턴은 언제 사용하는가?

활동 레코드는 생성, 읽기, 수정, 삭제와 같이 너무 복잡하지 않은 도메인 로직에서 좋은 선택이다. 단일 레코드 기반의 차이와 검증은 이 구조에서 잘 작업이 된다.

활성화 레코드 패턴의 예제 : 간단한 사원

활동 레코드 작업의 중심이 어떻게 처리되는지를 보여주는 간단한 예는 다음과 같다. 아래는 사원 클래스로부터 시작 한다.

 

class Person ...

 

private String name;

private int numberOfDependents;

수퍼 클래스에 ID 필드가 있다. 데이터베이스는 같은 구조로 설정되어 있다.

 

create table people(

ID int primary key,

name varchar,

number_of_dependnets int

)

개체를 로드하기 위해서 사원 클래스는 검색자와 같이 행동하고, 로드를 수행한다. 이를 위해 사원 클래스에서 정적 메서드를 사용한다.

 

class Person ...

 

private final static String findStatmentString =

"SELECT id, name, number_of_depents " +

"FROM people " +

"WHERE id = ? ";

 

public static Person find(Long id) {

Person result = (Person) Registry.getPerson(id);

if (result != null) return result;

PreparedStatment findStatement = null;

ResultSet rs = null;

try {

findStatement = DB.prepare(findStatementString);

findStatement.setLong(1, id.longValue());

rs = findStatement.executeQuery();

rs.next();

result = load(rs);

return result;

} catch(SQLException e) {

throw new ApplicationException(e);

} finally { DB.cleanUp(findStatement, rs);

}

}

 

public static Person find(long id) {

return find(new Long(id));

}

 

public static Person load(ResultSet rs) throws SQLException {

Long id = new Long(rs.getLong(1);

Person result = (Person) Registry.getPerson(id);

if ( result != null) return result;

String nameArg = rs.getString(2);

int numDependentsArg = rs.getInt(4);

rsult = new Person(id, nameArg, numDependentsArg);

Resigery.addPerson(result);

return result;

}

개체 수정은 간단히 인스턴스 메소드를 얻게 된다.

 

class Person ...

 

private final static String updateStatementString =

" UPDATE people " +

" set name = ?, number_of_depents = ? " +

" where id = ? ";

 

public void update() {

PreparedStatement updateStatement = null;

 

try {

updateStatement = DB.prepare(updateStatementString);

updateStatement.setString(1, name);

updateStatement.setInt(2, numberOfDepentents);

updateStatement.setInt(3, getID().intValue());

updateStatement.execute();

catch(Execption e) {

throw new ApplicationException(e);

} finally { DB.cleanUp(UpdateStatement);

}

}

 

삽입은 가장 간단하다.

 

class Person ...

 

private static final String insertStatementString =

"INSERT INTO people VALUES(?, ?, ?, ?)";

 

public Long insert() {

PreparedStatment insertStatment = null;

try {

insertStatement = DB.prepare(insertStatementString);

setID(findNextDatabseId());

insertStatement.setString(1, getID().intValue());

insertStatement.setInt(2, name);

insertStatement.setInt(3, numberOfDepentents);

insertStatement.execute();

Registry.addPerson(this);

return getID();

catch(Execption e) {

throw new ApplicationException(e);

} finally { DB.cleanUp(UpdateStatement);

}

}

 

세금 공제 계산과 같은 비즈니스 로직 사번 클래스 안에서 위치한다.

 

class Person ...

 

public Money getExemption() {

Money baseExeption = Money.dallars(1500);

Money dependentExemption = Money.dallars(750);

return baseExemption.add(dependentExemption.multiply(this.getOfdents());

}

Posted by 꿈꾸는자의 생각의파편들

행 데이터 게이트웨이(Row Data Gateway) 패턴

행 데이터 게이트웨이 패턴의 정의

데이터 소스에 있는 단일 레코드를 접근하기 위한 게이트웨이 같이 행동하는 객체 행별 하나의 인스턴스가 있다.

그림 -14. 데이터 게이트웨이 패턴의 구조

행 데이터 게이트웨이 패턴의 설명

메모리 객체에 있는 내제된 데이터베이스 접근 코드는 몇몇 단점을 피할 수 있다. 작업 시작시 메모리 객체가 자신의 비즈니스 로직을 포함하고 있다면, 데이터베이스 조작 추가는 복잡성을 높이게 된다.

행 데이터 게이트웨이는 레코드 구조에 있는 레코드와 같는 객체를 제공한다. 그러나, 이러한 개체는 프로그래밍 언어의 일반적 메커니즘에 접근을 할 수 있다. 모든 데이터 자원 접근의 세부사항은 이 인터페이스 뒤에 숨게된다.

행 데이터 게이트웨이 패턴은 언제 사용하는가?

행 데이터 게이트웨이의 선택은 자주 두 단계를 가진다. 첫 번째는 전부 게이트웨이를 사용할지를 결정하는 것이다. 두 번째는 행 데이터 게이트웨이 또는 테이블 게이트웨이를 사용 할지를 결정하는 것이다.

트랜잭션 스크립트 패턴을 사용 시 가장 자주 행 데이터 게이트웨이를 사용한다. 도메인 모델 패턴을 사용하는 경우는 행 데이터 게이트웨이를 사용하지 않는다.

행 데이터 게이트웨이 패턴의 예제: 사원 레코드

아래에 행 데이터 게이트웨이을 위한 예가 있다. 이것은 간단한 사원 테이블이다.

 

create table people(

ID int primary key,

name varchar,

number_of_dependnets int

)

 

PersonGaetway은 테이블을 위한 게이트웨이이다. 이는 데이터 필드와 접근자로부터 시작한다.

 

class PersonGetway ...

 

private String name;

private int numberOfDepents;

 

public String getName() {

return name;

}

 

public void setName(String name) {

this.name = name;

}

 

public String getNumberOfDepents() {

return numberOfDepents;

}

 

public void setNumberOfDepents(String name) {

this.numberOfDepents = numberOfDepents;

}

게이트웨이 클래스 자신은 입력과 수정을 다룬다.

 

class PersonGatway ..

 

private static final String updateStatementString =

"UPDATE people " +

" set name = ?, number_of_depents = ? " +

" where id = ? ";

public void update() {

PreparedStatement updateStatement = null;

 

try {

updateStatement = DB.prepare(updateStatementString);

updateStatement.setString(1, name);

updateStatement.setInt(2, numberOfDepentents);

updateStatement.setInt(3, getID().intValue());

updateStatement.execute();

catch(Execption e) {

throw new ApplicationException(e);

} finally { DB.cleanUp(UpdateStatement);

}

}

 

private static final String insertStatementString =

"INSERT INTO people VALUES(?, ?, ?, ?)";

 

public Long insert() {

PreparedStatment insertStatment = null;

try {

updateStatement = DB.prepare(updateStatementString);

setID(findNextDatabseId());

updateStatement.setString(1, getID().intValue());

updateStatement.setInt(2, name);

updateStatement.setInt(3, numberOfDepentents);

updateStatement.execute();

Registry.addPerson(this);

return getID();

catch(Execption e) {

throw new ApplicationException(e);

} finally { DB.cleanUp(UpdateStatement);

}

}

데이터베이스의 밖으로 사원 정보를 얻기위해 PersonFinder 를 분리하였다. 새로운 게이트웨이 개체를 생성하기 위해 게이트웨이와 같이 작업한다.

 

class PersonFinder ...

private final static String findStatmentString =

"SELECT id, name, number_of_depents " +

"FROM people " +

"WHERE id = ? ";

 

public PersonGatway find(Long id) {

PersonGateway result = (PersonGateway) Registry.getPerson(id);

if (result != null) return result;

PreparedStatment findStatement = null;

ResultSet rs = null;

try {

findStatement = DB.prepare(findStatementString);

findStatement.setLong(1, id.longValue());

rs = findStatement.executeQuery();

rs.next();    

result = PersonGateway.load(rs);

return result;

} catch(SQLException e) {

throw new ApplicationException(e);

} finally { DB.cleanUp(findStatement, rs);

}

}

 

public PersonGateway find(long id) {

return find(new Long(id);

}

 

class PersonGateway ...

 

public static PersonGateway load(ResultSet rs) throws SQLException {

Long id = new Long(rs.getLong(1));

PersonGateway result = (PersonGateway) Registry.getPerson(id);

if ( result != null ) return result;

String nameArg = rs.getString(2);

int numDependensArg = rs.getInt(3);

result = new PersonGetway(id, nameArg, numDependensArg);

Registry.addPerson(result);

return result;

}

몇가지 중요점에 따라 한 사원 이상 검색을 위해 적절한 검색자 메소드를 제공한다.

 

class PersonFinder ...

 

private final static String findResponsibleStatment =

"SELECT id, name, number_of_depents " +

"FROM people " +

"WHERE number_of_dependents > 0 ";

 

public list findResponsibles() {

list result = new ArrayList();

PreparedStatement stmt = null;

ResultSet rs = null;

try {

stmt = DB.prepare(findResponsibleStatement);

rs = stmt.executeQuery();

while (rs.next()) {

result.add(PersonGateway.load(rs));

return result;

} catch(SQLException e) {

throw new ApplicationException(e);

} finally { DB.cleanUp(findStatement, rs);

}

}

검색자는 구분 맵(195)을 포함하는 레지스트리(480)을 사용한다. 트랜잭션 스트립트(110)에서 게이트웨이를 사용한다.

 

PersonFinder finder = new PersonFinder();

Iterator people = finder.findResponsibles().iterator();

StringBuffer result = new StringBuffer();

while(people.hasNext()) {

PersonGeteway each = (PersonGateWay) people.next();

result.append(each.getName());

result.append("");

result.append(String.valueOf(each.getNumberOfDependents());

}

return result.toString

 

예제: 도메인 객체를 위한 데이터 홀더

 

class Person ...

 

private PersonGateway data;

public Person(PersonGateway data) {

this.data = data;

}

 

class Person ...

 

public int getNumberOfDependents() {

return data.getNumberOfDependents();

}

 

class Person ...

 

public Money getExemption() {

Money baseExeption = Money.dallars(1500);

Money dependentExemption = Money.dallars(750);

return baseExemption.add(dependentExemption.multiply(this.getOfdents());

}

Posted by 꿈꾸는자의 생각의파편들

테이블 데이터 게이트웨이(Table Data Gateway) 패턴

테이블 데이터 게이트웨이 패턴의 정의

데이터베이스에 대한 게이트웨이와 같은 행동을 하는 개체 이다. 하나의 인스턴스가 테이블에 있는 모든 행을 조작한다.

그림 -13. 테이블 데이터 게이트웨이 패턴의 구조

 

테이블 데이터 게이트웨이 패턴의 설명

테이블 데이터 케이트웨이는 단일 테이블이나 뷰(select,insert,update,delete)를 접근하기 위한 모든 SQL을 포함한다. 또한, 다른 코드 데이터베이스와 상호작용을 하기 위한 모든 메서드를 호출한다.

테이블 데이터 게이트웨이 패턴은 언제 사용하는가?

테이블 데이터 게이트는 아주 간단한 데이터베이스 인터페이스 패턴으로 사용 할 수 있다. 이 패턴은 테이터베이스 테이블 또는 레코드형으로 매핑이 가능하다.

테이블 데이터 게이트웨이는 특히 테이블 모듈과 잘 작업이 된다.

행 데이터 게이트웨이 패턴과 같이 테이블 데이터 게이트웨이는 트랜잭션 스크립트 패턴에 매우 적절하게 사용 할 수 있다.

데이터베이스 접근을 캡슐화한 테이블 데이터 게이트웨이 사용의 이점 중 하나는 같은 인터페이스를 이용하여 데이터베이스 조작을 위하여 SQL 또는 저장 프로시저를 사용하여 작업을 할 수 있다. 더구나, 저장 프로시저 자신은 자주 테이블 데이터 게이트웨이를 구조화 한다. 이러한 방법으로 입력과 저장 프로시저는 실제 테이블 구조를 캡슐화 한다.

Posted by 꿈꾸는자의 생각의파편들