간단하게 DAO를 구현할 일이 생겼다.

spring에 JdbcTemplate를 사용하는 것이 편할거 같아서 이용을 하고 있다.

근데, ResultSet이 없으면  org.springframework.dao.EmptyResultDataAccessException이 발생을 한다.

지라에 등록이 되어 있는데, 오픈이 되어있네.
http://jira.springframework.org/browse/SPR-5294

덧~ 위에 지라에 나온 것은 DB2에 대한 얘기이다.


대략 예외 메세지는 다음과 같다.

org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
at org.springframework.dao.support.DataAccessUtils.requiredSingleResult(DataAccessUtils.java:71)
at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:722)
at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:732)
at org.springframework.jdbc.core.simple.SimpleJdbcTemplate.queryForObject(SimpleJdbcTemplate.java:151)
at com.twc.ivr.dao.AccountProfileDaoImpl.findAccount(AccountProfileDaoImpl.java:107)

API를 살펴보니 별 내용이 없다.
http://static.springframework.org/spring/docs/2.0.x/api/org/springframework/dao/EmptyResultDataAccessException.html

그래서 다음과 같이 해결을 했다.

try {
       return (Blog)jdbcTemplate.queryForObject(sql, new Object[] { url }, mapper) ;
} catch (EmptyResultDataAccessException e) {
   return null;
}

구글에서 검색을 해보면. 위와 같은 이유로 고생하는 사람들이 꽤 많다.
http://www.google.co.kr/search?complete=1&hl=ko&newwindow=1&q=JdbcTemplate++org.springframework.dao.EmptyResultDataAccessException&btnG=%EA%B2%80%EC%83%89&lr=&aq=f&oq=

.html 예외는 http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/dao/EmptyResultDataAccessException.html를 서브클래스로 구현되었다.


영회님이 API링크를 줘서 다시 잘 살펴보니 관련 내용이 있다.

queryForObject

public Object queryForObject(String sql,
                             RowMapper rowMapper)
                      throws DataAccessException
Description copied from interface: JdbcOperations
Execute a query given static SQL, mapping a single result row to a Java object via a RowMapper.

Uses a JDBC Statement, not a PreparedStatement. If you want to execute a static query with a PreparedStatement, use the overloaded queryForObject method with null as argument array.

Specified by:
queryForObject in interface JdbcOperations
Parameters:
sql - SQL query to execute
rowMapper - object that will map one object per row
Returns:
the single mapped object
Throws:
IncorrectResultSizeDataAccessException - if the query does not return exactly one row
DataAccessException - if there is any problem executing the query
See Also:
JdbcOperations.queryForObject(String, Object[], RowMapper)
위에 내용에 대해서 덧글을 달아주신 분들이 이야기들을 정리해보면, queryForObject는 하나의 row만 리턴하는 API이고, 쿼리에 만족하는 값이 없으면  IncorrectResultSizeDataAccessException의 sub 클래스인 EmptyResultDataAccessException를 발생한다. 이것은 버그는 아니고 API를 개발한 사람의 의도라는 것이다. 2row이상의 데이터도 아니고 0row를 리턴할때 예외를 발행하는 것은 도데체 알수 없는 의도이다. 어떤 이득을 얻으려고 했을까?

권남님은 그래서 queryForList를 사용하라고 하는데, 내 생각은 하나의 row면 queryForObject를 사용하고 예외처리로 널을 리턴하는 것이 더 좋아보인다. collection이 아닌 데이터를 1개 리턴을 할때 예외가 난다고 리스트로 collection으로 가져온다는 것은 좀 이상하다. 코드에서 의도를 볼때 queryForObject vs queryForList를 사용하는 것은 의미적 차이가 크다.

http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/jdbc/core/JdbcTemplate.html

spring을 이용해서 서블릿 컨텍스트에서 bean설정을 테스트를 해볼수가 있다.

MockServletContext 클래스는 이때 유용한다.

MockServletContext를 이용해서 bean configuraiton을 테스트 하려면, addInitParameter메소드에 파라미터를 셋팅을 해야 한다.

파라미터 이름으로 "contextConfigLocation"를 사용을 할수가 있다.

MockServletContext servletContext = new MockServletContext();
        servletContext.addInitParameter( "contextConfigLocation","classpath:applicationContext*.xml");

spring에서 제공하는 상수로 변경을 하는 것이 좋겠다.

물론 같은 값이지만, "contextConfigLocation"가 갑자기 튀어나오면 선택하기 어렵고 이클립스의 코드 assist를 이용하면, 오타의 가능성도 줄일수가 있다.

MockServletContext servletContext = new MockServletContext();   
          servletContext.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,"XXXX");

이와 같은 코드는 우리가 만들 APIs에서 참고를 해야한다.

지난 포스트에서 configuration문제때문에 애먹은 이야기를 했었다.

다시 한번 곰곰히 생각을 해보니, 같은 bean id 가 문제가 될수는 있지만, StackOverflowException를 발생시키기에는 부족해보인다.

과연 무엇이 잘못된 것일까?

http://www.ologist.co.kr/843

문제는 전혀 예상치 못했던 곳에서 발생을 했다. 같은 이름이 bean id가 다른 applicationContext파일에 존재를 해서 application start time에는 이상이 없었으나, 런타임시에 상호참조에 의해서 예외가 발생을 한것이다.

버그가 발생한 원인을 다시 생각을 해보면, 자신을 참조한 bean을 다시 dependency를 가지게 된 것은 맞는듯 하다.

어떤 상황이 dependency를 가지는 bean들중에 자기자신을 dependency를 가질때 생기는 예외이기떄문에 복잡한 설정 중에서 자기자신을 dependency로 가지는 bean이 configuraiton에 존재해서 일거라는 생각이 들었다.

조금 더 의심가는 부분도 생겼는데, 프로젝트 종료이후 여유가 생기는 시점에 다시 한번 테스트를 해봐야 겠다.

Spring IDE와 AJDT

2007/10/11 14:29
이클립스에서 Spring IDE를 설치하자.

자바코드 refactoring시에 configuration도 refactoring이 되고, 설정파일에 문제가 생겼을 때 바로 노티를 해주기때문에 커밋을 하거나 컨텍스트를 올리전에 문제점을 감지를 할수가 있다.

유용한 기능이니 spring을 사용하시는 분들은 필수적으로 설치해서 사용해볼만하다.

Spring IDE
http://springide.org/updatesite/
업데이트를 하려고 할때 혹시 잘 되지 않으면 아래 업데이트와 같이 받으면 된다.

eclipse ajdt
http://www.eclipse.org/ajdt/downloads/

개발자에게 있어서 교류라는 것은 참 중요하다. 설계에 대한 힌트라던가, 학습욕구를 자극하는 일, 좀더 쉽게 개념을 이해하고, API를 익힐 수 있는 일은 교류를 통해서 좋은 질로, 다양하게 습득을 할수가 있다.

spring을 사용하는 사람들의 교류의 장을 만들어 주시는 분들이 있는데 그 중의 한번이신 영회님의 노력으로 다음과 같은 내용이 공지가 되었다.

국낸 spring사용자에게 조금 더 KSUG에 관심을 갖게 해주는 이벤트라는 생각이 든다.

내가 지금 다니고 있는 회사인 NHN이 sponsor되어있는데, 앞으로도 사내뿐만 아니라 국내 개발자를 위해서 많은 도움을 주는 회사가 되었으면 좋겠다.

난 안타깝지만, 해당날짜에 가족모임이 있어서 참석을 못하지만, 마음만 그곳에서 좋은 성과를 올리기를 기대해본다.

Korea Spring User  Group
http://www.springframework.org/node/519
http://younghoe.info/608






Spring WS1.0이 릴리즈가 되었다.

웹서비스 자체는 플랫폼이나 랭귀지에 상관이 없이 이 기종간에 서비스가 가능해야 하므로, Contract-First스타일의 개발이 올바르다고 할수가 있을거 같다.

Spring-WS only supports the contract-first development style

Spring-WS는 Contract-First스타일만을 지원한다.


Why Contract-First?
When creating Web services, there are two development styles: contract-last and contract-first. When using a contract-last approach, you start with the Java code, and let the Web service contract (WSDL, see sidebar) be generated from that. When using contract-first, you start with the WSDL contract, and use Java to implement said contract.

http://static.springframework.org/spring-ws/site/why-contract-first.html


http://static.springframework.org/spring-ws/site/reference/html/tutorial.html#tutorial.implementing.endpoint


Key Features

  • Makes the Best Practice an Easy Practice: Spring Web Services makes enforcing best practices easier. This includes practices such as the WS-I basic profile, Contract-First development, and having a loose coupling between contract and implementation.
  • Powerful mappings: You can distribute incoming XML request to any object, depending on message payload, SOAP Action header, or an XPath expression.
  • XML API support: Incoming XML messages can be handled in standard JAXP APIs such as DOM, SAX, and StAX, but also JDOM, dom4j, XOM, or even marshalling technologies.
  • Flexible XML Marshalling: The Object/XML Mapping module in the Spring Web Services distribution supports JAXB 1 and 2, Castor, XMLBeans, JiBX, and XStream. And because it is a separate module, you can use it in non-Web services code as well.
  • Reuses your Spring expertise: Spring-WS uses Spring application contexts for all configuration, which should help Spring developers get up-to-speed nice and quickly. Also, the architecture of Spring-WS resembles that of Spring-MVC.
  • Supports WS-Security: WS-Security allows you to sign SOAP messages, encrypt and decrypt them, or authenticate against them.
  • Integrates with Acegi Security: The WS-Security implementation of Spring Web Services provides integration with Acegi Security. This means you can use your existing Acegi configuration for your SOAP service as well.
  • Built by Maven: This assists you in effectively reusing the Spring Web Services artifacts in your own Maven-based projects.
  • Apache license. You can confidently use Spring-WS in your project.


    http://static.springframework.org/spring-ws/site/


상속을 통해서 클래스 갯수를 줄이고 있던 디자인이 있었다. 거의 7개 가량의 갯수를 줄이기 위해서 디자인이 깨지는 것을 보면서도 이게 실용적(?)이라고 생각을 하면서 클래스 갯수를 줄였지만, 영 마음이 불편했다. OOP의 원칙을 깨가면서 만든 디자인이었기때문이다.

불편한 마음을 없애기 위해서 과감하게 클래스 갯수를 늘렸다. 일단, 원칙을 지키면서 점진적으로  refactoring을 하기로 결심을 했다. 대략적인 윤곽은 마음 속에 있었지만, 사실 목적은 분명하지 않은채 점진적으로 변경을 꾀하면서 변경된 코드 안에서 좀더 나은 디자인으로 개선을 해나갔다.

대략의 과정을 설명을 하자면,
구현 상속으로 되어 있던 디자인을 delegate를 통해서 데코레이션 패턴(나..이 패턴 너무 좋아한다..^^;)으로 변경하고 , 인터페이스만을 wiring하는 클래스를 만들고, dependency injection을 spring config에서 설정을 통해서 인스턴스 생성과 Factory에 등록을 했다.

디자인을 변경 후 3가지의 장점을 가질 수가 있었다.

1. 테스트가 쉬워졌다.
구현상속은 많은 dependency를 가지게 된다. dependency를 하나의 인터페이스로 추상화가 가능했고, 하나의 mock을 생성해서 테스트를 할 수가 있었고, 확장하는 클래스에서 필요한 클래스들만 mo필ck으로 만들어서 테스트를 하면 되었기때문에 테스트를 작성하기 쉬웠다.

2. 코드의 이해가 쉬워졌다.
구현에 대해서 집착을 하지 않아도 되고, 클래스 자체만 봐도 이해를 하기 쉬웠다. 다른 클래스들과의 관계는 이 클래스를 이해하는데, 별다른 장벽이 되지 않았다.

3. 객체지향원칙을 지킬 수 있었다.
부모 클래스에서 확장되어지는 자식 클래스때문에 로직이 추가가 되면 안된다. 비즈니스 로직 특성상 피하기 어려웠지만, 최소한의 변경으로 디자인이 깨지는 것을 피할 수 있었다. 즉, 자식과 관련되서 if문이 증가하지 않았다.

아마 spring configuration을 사용하지 않았다면, 클래스 갯수를 7개정도 늘려서 DI를 해주는 상황이었다. 원칙에 따라서 디자인을 개선을 하고 나니 나름 볼만한 코드가 되었다. 클래스 갯수가 총 8개가 늘어나야 할 상황이었지만, 7개에 해당하는 dependency부분을 spring IoC가 해결을 해주었고, 인터페이스를 wiring하는 심플한 하나의 클래스만 늘어나게 되었다. spring config를 통한 IoC가 아니었어도, 클래스 갯수를 늘려서 확장하는 것이 좋은 선택이었을 것이다. 그 좋은 선택 안에서 spring IoC는 좀더 나은 기쁨을 준 것이다.

사실, 서비스되고 있는 코드를 refactoring하는 것은 쉬운 일은 아니지만, 계속된 찜찜한 마음의 짐을 떨치고 싶었다. 좀더 자신감을 가지고 변경을 꾀할수 있었던 것은 테스트가 된 구현 클래스들을 인터페이스 기반으로 조립하는 일이었기때문에 가능했다. 구현을 다시한 부분은 거의 없고, 클래스들 간의 관계를 바꾸는 디자인 개선작업이어서 좀더 편하게 변경을 가할수 있었다.



Spring Java Configuration Project

The Spring Java Configuration project is an experiment in producing a Java-based alternative to configuring Spring Application Contexts. While most people currently use XML to configure bean definitions in their Application Contexts, Spring's versatility and metadata based internal handling of bean definitions means alternatives to XML config are easy to implement. Aside from the Java Config, Properties files and Groovy builder support already exists.

http://www.springframework.org/javaconfig


다음과 같은 코드로 spring wire dependencies를 설정을 할 수가 있다.
@Bean(scope = DefaultScopes.SINGLETON)
public Person rod() {
return new Person("Rod Johnson");
}

@Bean(scope = DefaultScopes.PROTOTYPE)
public Book book() {
Book book = new Book("Expert One-on-One J2EE Design and Development");
book.setAuthor(rod()); // rod() method is actually a bean reference !
return book;
}

나같은 경우는 자바코드로 만드는 dependencyn wiring이 더 가독성이 좋아보인다.
또한 이클립스라는 걸출한 툴에 리팩토링기능을 불안해하지 않고, 얼마든지 사용을 할 수가 있다.

물론 Spring IDE를 사용하면 refactoring은 몰라도 rename을 통해서 클래스 이름이 변경이 되면,
에러표시가 나와서 수동으로 수정은 가능하다.
언제나 java로 된 configuration이 좋은 것은 아니지만, 분명한 장점을 인지하고 사용을 한다면 훌륭한 표현방법이 될 것이다.

개인적으로는 자바로 된 표현이 매력적으로 보인다.

http://static.springframework.org/spring-javaconfig/docs/1.0-m2a/reference/html/


우리는 spring을 사용을하다가 보면, 실수를 할때가 있다.

나는 같은 인터페이스를 구현한 여러 클래스를 하나의 클래스에 등록을 해서 순차적으로 실행을 하는 프로그램을 만들었다.

특정 기능에서 후처리하는 것을 같은 인터페이스를 구현해서 일괄적인 처리를 하는 것과 동시에 어떤 일을 하는지 명확히 알수가 있어서 좋았다. 여러가지 작업들이 있었지만, 모두 런타임시에 에러가 발생을 하면 무시를 하고 넘어가야 할 작업들이었다.

버그의 원인으로 의심을 한것
1. 외부서비스와 연동을 통해서 값을 얻어오는데, 연결이 잘 안되거나, timeout이 발생한다.
2. 데이터를 DTO로 populate를 하는데, null인 값을 호출을 한다.
3. spring configuration이 문제가 있다.

사실 3번을 로그를 확인하다가 가장 나중에 알아냈다. 같은 작업을 2번을 등록했지만, 뒤에 실행되는 것 하나는 런타임에 에러가 나서 이클립스 디버깅 모드로 돌려도 한번 밖에 실행이 되지 않을 것이었다. 물론, 예외처리를 무시하게 했던 글로벌 에러처리를 제거했으면 빠른 시간에 해결이 되었겠지만, 너무 많은 로그들이 나오고 있어서 눈으로 학인하기가 쉽지 않은 상태였다.

같은 인터페스의 구현체를 2번이나 등록을 하는 실수를 범했다. 내가 눈으로 확인했을 당시에는 등록하는 클래스의 ref에는 이름이 중복된게 없었다.

아래와 같이 한 것이다. 똑같은 구현체를 다른 이름으로 2번을 등록했다.
<bean id="movieLister1" class="x.y.SimpleMovieLister" />
<bean id="movieLister2" class="x.y.SimpleMovieLister" />

<bean id="tasksAfter" class="x.y.TasksAfter" >
<property name="afterTasks">
  <list>
   <ref bean="movieLister1" />
   <ref bean="movieLister2" />
  </list>
  </property>
</bean>

<bean id="movieListerMain" class="x.y.SimpleMovieListerMain">
  <property name="tasksAfter" ref="tasksAfter" />
</bean>


물론, 처음부터 잘 등록을 하면 되었으나, 간혹 C&P를 하다가 실수를 할수도 있고, configuration이 길어지게 되면, 눈으로 보는 것도 집중력이 떨어진다. (사람의 눈을 얼만큼 믿어야 할까?)

spring context가 올라갈때 에러가 발생을 하지 않으니 configuraiton에 의심도 하지 않았다.

계속된 NullPointerException이 나와도 과거에 외부 서비스가 연동이 안되면 Null이 떨어진 경험이 나를 더욱더 잘못된 곳에서 디버깅을 하게 만들었다. 한정된 경험은 시야를 무척이나 좁게 만든다.

같이 디버깅을 하던 자바지기가 하나의 메일을 주었다. 이것은 그 동안에 묵혔던 버그를 해결하게 충분한 기능이었다. 나에게는 운이 좋았다...^^

아래 내용으로 해결이 어려울 수도 있지만, spring context에 의도하지않은 객체 생성에 대해서 그리고, 의도한 dependency를 위해서 유용한 방법이다.

내용은 다음과 같다.
http://www.springframework.org/docs/reference/metadata.html
http://www.springframework.org/docs/api/org/springframework/beans/factory/annotation/Required.html



정리를 해보면, 아래와 같이 설정을 하면된다.

1. required사용을 위해서 빈을 등록한다.
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>

2. 사용할 빈을 등록한다.
<bean id="movieLister" class="x.y.SimpleMovieLister">
  <!-- whoops, no MovieFinder is set (and this property is @Required) -->
</bean>

3. 코드상에 setter에 @Required을 넣어준다.
public class SimpleMovieLister {

  // the SimpleMovieLister has a dependency on the MovieFinder
  private MovieFinder movieFinder;

  // a setter method so that the Spring container can 'inject' a MovieFinder
  @Required
  public void setMovieFinder(MovieFinder movieFinder) {
       this.movieFinder = movieFinder;
  }
 
  // business logic that actually 'uses' the injected MovieFinder is omitted...
}


4. 제대로 DI가 되지 않으면 context 로딩시 다음과 같이 예외를 발생한다.
Exception in thread "main" java.lang.IllegalArgumentException:
  Property 'movieFinder' is required for bean 'movieLister'.



좀더 optional한 사용법은 여기를 참조
http://younghoe.info/trackback/504
그날 많은 분들 만날 수 있으면 좋겠네요...^^
2가지 주제인데, 최근 관심이 많은 부분이라, 기대가 되네요.

  • 13:00 - 13:15 이프릴(Epril) 회사 및 공개 세미나 소개
  • 13:15 - 14:45 세션1: 다중 레이어 환경에서 Spring을 활용한 통합 테스트 및 단위 테스트 방안(발표자:안영회)
  • 14:45 - 15:00 휴식
  • 15:00 - 16:30 세션2: XML 스키마 기반 빈 선언을 이용한 자유로운 XML Configuration Customization(발표자:이일민)
  • 16:30 - 17:00 질문 및 자유 토론


이프릴 제1회 스프링 공개세미나 안내
http://www.epril.com/?p=17


BLOG main image
OOP and Java by ologist

공지사항

카테고리

All (646)
private!! (106)
WEB & IT (138)
Developer (399)