hacking/java2009/01/18 01:17
크리에이티브 커먼즈 라이선스
Creative Commons License
JDBC 코딩할 때 골치아픈 것 중의 하나가 generate key(오라클의 sequence, MS SQL의 identity, MySQL의 auto_increment 등)을 다루는 것이다.

아 글쎄~ 요즘세상에 JDBC를 날로 쓰는 경우가 어딨냐고-.-;

대부분의 ORM(JPA, Hibernate, iBATIS 등)에서는 여기에 대한 별도의 처리방법이 존재하지만,
ORM까지 동원하기엔 좀 그렇고-.-, 그렇다고 JDBC를 날로 쓰기도 좀 그렇고...
이럴 땐, commons-dbutilsspring-jdbc를 사용하게 된다.

commons-dbutils를 사용하는 경우는 사실상 JDBC를 날로 쓸 때와 큰 차이가 없으니 넘어가고...

spring-jdbc을 사용하는 경우, 그 중에서도 스프링 2.5부터 추가된 SimpleJdbcInsert의 경우를 알아보면:
SimpleJdbcInsert insert = new SimpleJdbcInsert(dataSource).withTableName("contacts").usingGeneratedKeyColumns("id");
SqlParameterSource paramSource = new BeanPropertySqlParameterSource(contact);
long generatedKey = insert.executeAndReturnKey(paramSource).longValue();
contact.setId(generatedKey);

"와~~ 깔끔하게 끝나네~"라고 기뻐하려는 찰라... 이게 derby에서(만!) 안된다. ㅠ.ㅠ

좀 뒤져봤더니 버그만 올라와 있고 해결책은 안 나와 있다. 그래서... 이렇게 구질구질한 코드를 만들었다:
KeyHolder keyHolder = new GeneratedKeyHolder();
JdbcTemplate template = new JdbcTemplate(dataSource);
template.update(new PreparedStatementCreator() {
  public PreparedStatement createPreparedStatement(Connection connection)
      throws SQLException {
    PreparedStatement pstmt = connection.prepareStatement("INSERT INTO contacts(name,email,address) VALUES(?,?,?)", Statement.RETURN_GENERATED_KEYS);
    pstmt.setString(1, contact.getName());
    pstmt.setString(2, contact.getEmail());
    pstmt.setString(3, contact.getAddress());
    return pstmt;
  }
}, keyHolder);
Long generatedKey = new Long(keyHolder.getKey().longValue());
book.setId(generatedKey); 

혹시, 더 깔끔한 방법을 알고 있는 분 계시면 공유 좀 해주셈~~

Posted by iolo
hacking/java2007/12/04 01:11
크리에이티브 커먼즈 라이선스
Creative Commons License
최근에는 본의 아니게 AJAX로 먹고 살고 있지만, 내 밥줄은 기본적으로 자바다. 그리고, 내가 자바로 코딩할 때 가장 많이 사용하는 라이브러리(프레임웍을 포함해서) 중의 하나가 spring이다. 즐겨쓰던 IoC 컨테이너였던 avalon 프로젝트가 뽀개지면서 대안으로 spring 1.2.x를 처음 쓰기 시작했는데, 벌써(이제서야?) 2.5 버전이 나왔다.

프레임웍 라이브러리의 특성상(하위호환성이 무척 중요하다!) 큰 변화가 있기 힘들지만, 2.5에서는 호환성을 깨지 않으면서 많은 눈에 띄는 변화가 있었다. 이 글에서는 그 중에서 annotation 지원에 대해서 알아보겠다.

스테레오타입 어노테이션

클래스에 붙여서 해당 클래스가 컴포넌트 스캐닝의 대상이고, 스프링에 의해서 관리될 것임을 표시한다. 즉, <beans> XML 설정 파일에서 <bean>으로 선언한 것과 같은 효과를 낸다.

@Component
메타 어노테이션. 다른 모든 스프링 스테레오타입 어노테이션 및 커스텀 Qualifer 어노테이션의 부모 어노테이션.

@Service
상태없는 서비스 컴포넌트.

@Repository

저장소 컴포넌트(DAO 등).

@Aspect
@AspectJ의 어스펙트.

@Controller
Spring MVC 컨트롤러.

스테레오타입은 스테레오타입일 뿐!! 너무 심각한 의미를 부여하지 말자! 대부분의 경우엔 @Service으로 충분하다. @Controller에 대해서는 다음 기회에 좀 더 자세히 알아보기로 하고...

위의 어노테이션이 붙은 클래스를 찾기 위해서 모든 클래스 패스를 뒤지는 것은 곤란하므로.. 아래와 같은 방식으로 스캔할 범위를 지정한다:

예) 가장 단순한 사용 예: 특정 패키지 이하의 모든 클래스에 대해서 스테레오타입 어노테이션이 붙은 클래스를 찾아서 스프링 관리 대상에 포함 시킴.(빈 선언)

<beans>
  <context:component-scan base-package="com.mycompany.myblog" />
   ...
</beans>


의존성 주입(DI; Dependency Injection) 어노테이션

@Autowired
생성자, 메소드, 필드에 붙여서 스프링을 통해 인젝션될 것임을 표시한다.

@Qualifier
@Autowired이 붙은 필드 또는 메소드의 인자에 붙여서 인젝션할 빈을 검색하기 위한 더 정확한 조건을 지정한다.


예) 필드에 지정
@Autowired
@Qualifier("myDataSource“)
private DataSource orderDataSource


예) 메소드에 지정
@Autowired
public void createTemplates(DataSource ds, ConnectionFactory cf) {
   this.jdbcTemplate = new JdbcTemplate(ds);
   this.jmsTemplate = new JmsTemplate(cf);
}


예) @Qualifer와 함께 지정
public class JdbcOrderRepositoryImpl implements OrderRepository {
   @Autowired
   public void init(
      @Qualifier("myDataSource“) DataSource orderDataSource,
      @Qualifier("otherDataSource") DataSource inventoryDataSource,
      MyHelper autowiredByType) {
         // ...
  }
  ...
}


JSR-250 어노테이션

JSR-250은 BEA WebLogic에서 제공되던 것을 JSR을 통해 표준화한 것인데, @Resource어노테이션은 JNDI 리소스 인젝션을 위해 꽤 많이 쓰이고 있는 것 같다.

@Resource
JSR-250에서는 JNDI 리소스만 인젝션할 수 있었지만, 스프링 2.5에서는 스프링을 통해 관리되는 모든 빈을 인젝션할 수 있다.

@PostConstruct
2.0에서는 InitializingBean 인터페이스를 구현하고 afterPropertiesSet()에서 수행하거나, bean설정에서 init-method 속성으로 지정했다.

@PreDestory
2.0에서는 DisposableBean 인터페이스를 구현하고 destroy()를 정의하거나, bean설정에서 destroy-method 속성으로 지정했다.

개인적으로 스프링 전용인 @Autowired보다는 @Resource를 선호하는 편인데, @Qualifer를 쓸 수 없다는 단점이 있지만, @Resource(name="dataSource")처럼 빈 이름을 지정할 수 있으므로 대부분의 경우엔 충분하다.

백문불여일견, 백견불여일타, 백타불여일런!

예) 스프링 2.0.x (스프링에 대한 컴파일 타임 의존성이 없음)
public class JdbcBlogdao implement BlogDao {
   private DataSource dataSource;
   public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; }
   public void init() throws Exception { ... }
   public void destroy() throws Exception { ... }
   ...
}

<beans>
   <bean id="blogDao" class="mypackage.JdbcBlogDao" init-method="init" destroy-method="destroy">
      <property name="dataSource" ref="dataSource />
      ...
   </bean>
   ...
</beans>

예) 스프링 2.0.x(스프링에 대한 컴파일 타임 의존성을 가짐)
public class JdbcBlogdao implement BlogDao, InitializingBean, DisposableBean {
   private DataSource dataSource;
   public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; }
   public void afterPropertiesSet() throws Exception { ... }
   public void destroy() throws Exception { ... }
   ...
}

<beans>
   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">...</bean>
   <bean id="blogDao" class="mypackage.JdbcBlogDao">
      <property name="dataSource" ref="dataSource />
      ...
   </bean>
   ...
</beans>

예) 스프링 2.5(어노테이션과 컴포넌트 스캔을 사용)
@Repository("blogDao")
public class JdbcBlogDao implements BlogDao {
   @Resource
   private DataSource dataSource;
   @PreConstruct
   public void init() throws Exception { ... }
   @PostDestroy
   public void destroy() throws Exception { ... }
   ...
}

<beans>
   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">...</bean>
   <context:component-scan base-package="com.mycompany.myblog" />
   ...
</beans>


다음에는 "Spring MVC 어노테이션"에 대해서 알아볼 생각인데...
틈나는대로 적겠지만, 그 전에 2.6이 나올지도... -.-;

Posted by iolo