SimpleDAO : 객체를 데이터베이스로 전송함 DBUtil : JDBC ResultSet, Connection 들과 작업할 때 필요한 일반적인 작업을 편리하게 해줌 ObjectRegistry : 객체를 찾을 수 있는 싱글톤 레지스트리. SetupDataSourceContextListener: JDBC 데이터소스 설정. SetupDBContextListener: (내장되어 있는) 데이터베이스 준비함. GetDataServlet: 데이터를 보여주기 위한 페이지 컨트롤러 PutDataServlet: 데이터를 저장하기 위한 페이지 컨트롤러매우 간단한 웹 애플리케이션이지만, 자체적인 컨테이너를 갖고 있으며, N-티어와 같은 더 큰 동작을 나타낼 수 있습니다. 그러므로 이 매우 작은 실험의 결과 일부를 실제 프로젝트로 전환이 가능합니다.
ackage pool_test.util ; public class ObjectRegistry { private static ObjectRegistry _instance = new ObjectRegistry() ; public static ObjectRegistry getInstance(){ return( _instance ) ; } private Map _singletons ; public void put( final String key , final Object obj ){ _singletons.put( key , obj ) ; } public Object get( final String key ){ return( _singletons.get( key ) ) ; } }ObjectRegistry는 실제 String : Object의 한 쌍을 갖는 Map입니다. 특정 위치에서 put() 메소드를 사용해 저장소에 객체를 저장할 수 있고, 또 다른 위치에서 get() 메소드를 사용해서 저장한 것과 같은 객체를 받아 올 수 있습니다. 레지스트리를 사용하는 것은 객체의 일반적인 타입(상위 클래스나 인터페이스)과 룩업 키만 알고 있으면 되기 때문에 객체 의존성을 약하게 합니다. 이 특징-구현(implementation), 생성(instantiation), 구성(configuration)-은 객체를 저장하기 위해서 put() 메소드를 호출 할 때 코드에 남게 됩니다.
package pool_test.util ; import org.springframework....ApplicationContext ; import org.springframework. ...ClasspathXMLApplicationContext ; public class ObjectRegistry { private ApplicationContext _singletons ; private ObjectRegistry(){ _singletons = new ClassPathXmlApplicationContext( new String[] { "spring-objectregistry.xml" } ); } public Object get( final String key ){ return( _singletons.getBean( key ) ) ; } }이전에 사용하던 Map을 Spring의 ApplicationContext로 변경한 것을 주목해야 합니다. Map과 비슷하게 ApplicationContext는 객체를 저장하고, 이름으로 그 객체들을 받아 올 수 있습니다. 비교하자면 ApplicationContext는 설정에 대한 정보를 밖으로 빼낸 XML 파일에서 객체 정보를 모두 불러옵니다. spring-objectregistry.xml에 정보가 변경되면 애플리케이션의 재시작이 필요하긴 하지만, 완전한 재 컴파일은 필요 없습니다.
XML 구성 요소(element)는 Reflection 호출에 대응합니다.<-- 간단히 보여주기 위해서 생략 -->
package pool_test.data.jdbc ; public class SimpleDAO { public void setupTable() ; public void putData() ; public Collection getData() ; public void destroyTable() ; }그렇지만 이 덮개 안에는 다른 동물이 들어있습니다. 이전 버전의 DAO는 클래스 내에 많은 JDBC 코드를 갖고 있었던 반면에, 새로운 버전의 DAO는 Spring을 사용함으로서 많은 짐을 덜게 되었습니다.
package pool_test.data.jdbc ; public class SimpleDAO extends JdbcDaoSupport { private GetDataWorker _getDataWorker ; private PutDataWorker _putDataWorker ; private CreateTableWorker _createTableWorker ; private DestroyTableWorker _destroyTableWorker ; // 생성자는 선언되지 않았습니다. protected void initDao() throws Exception { super.initDao() ; _getDataWorker = new GetDataWorker( getDataSource() ) ; _putDataWorker = new PutDataWorker( getDataSource() ) ; _createTableWorker = new CreateTableWorker( getDataSource() ) ; _destroyTableWorker = new DestroyTableWorker( getDataSource() ) ; return ; } // initDao() public void setupTable() { _createTableWorker.update() ; } public Collection getData() { return( _getDataWorker.execute() ) ; } // ... destroyTable()과 getData() // 비슷한 규약(Convention)을 따릅니다 ... }첫 번째 변화는 상위 클래스에 있습니다. SimpleDAO는 데이터베이스 작업에 필요한 메소드와 내부 클래스를 갖고 있는 Spring의 JdbcDaoSupport를 상속합니다. 도움을 주는 메소드의 첫 번째로 객체에 JDBC DataSource를 할당해주는 setDataSource()가 있습니다. 하위클래스에서는 객체를 받아오기 위해서 getDataSource()를 호출합니다.
package pool_test.data.jdbc ; import org.springframework ... SqlUpdate ; public class SimpleDAO { ... private class PutDataWorker extends SqlUpdate { public PutDataWorker( final DataSource ds ){ super( ds , SQL_PUT_DATA ) ; declareParameter( new SqlParameter( Types.VARCHAR ) ) ; declareParameter( new SqlParameter( Types.INTEGER ) ) ; } // 실제 애플리케이션에서는 외부 소스에서 SQL 문을 불러오게 됩니다. private static final String SQL_PUT_DATA = "INSERT INTO info VALUES( ? , ? )" ; } ... }PutDataWorker는 SQL INSERT와 UPDATE 호출 작업을 관리하는 Spring 템플릿 클래스인 SqlUpdate를 상속합니다. decalreParameter() 메소드는 SQL 문에서 사용할-문자열이나 숫자 각각- 데이터 타입을 Spring에게 말해주기 위해 호출합니다.
public class SimpleDAO { public void putData() { for( ... ){ // ... "nameParam" and "numberParam"는 지역 변수 Object[] params = { nameParam , // 문자열 변수 numberParam // 숫자형 변수 } ; _putDataWorker.update( params ) ; } } }putData() 메소드는 크게 의미없는 데이터 몇 개를 데이터베이스에 전송합니다. 이 메소드는 작업하는 클래스를 대표하고 있습니다. 특별히 작업하는 클래스에는 상속 받은 메소드가 있습니다. SqlUpdate.update()는 데이터를 받아 오는 것을 도와주고, JDBC Connection과 관련된 Statement 객체를 닫아줍니다. 이것은 내가 작성한 커스텀 JDBC 코드의 많은 부분이 외부로 이동하게 되며, 심지어 Connection, Statement, RsultSet을 종료하기 위해 도움이 되는 메소드(Convenient method)를 갖고 있는 옛날 DBUtil과 같은 클래스 전체가 없어지기도 합니다.
package pool_test.data.jdbc ; import org.springframework ... MappingSqlQuery ; // SimpleDAO 안에 있는 클래스 ... private class GetDataWorker extends MappingSqlQuery { // PutDataWorker와 비슷한 생성자를 갖고 있음 ... protected Object mapRow( final ResultSet rs , final int rowNum ) throws SQLException { final SimpleDTO result = new SimpleDTO( rs.getString( "names" ) , rs.getInt( "numbers" ) ) ; return( result ) ; } // mapRow() }이 메소드는 표 형식의 ResultSet 데이터를 한 번에 한 행씩 작업 가능한 SimpleDTO 객체로 변경하는 것을 도와줍니다. Spring은 ResultSet의 매 행마다 이 메소드를 호출합니다. mapRow()은 ResultSet 객체와 상호 작업을 하는 반면에, close() 하는 메소드에 책임을 갖거나, 프로세스에 어떤 행들이 남아 있는지를 체크하지는 않습니다.
이전 글 : 예제로 설명하는 쓰레드 제어하기(2)
최신 콘텐츠