하이버네이트, 스프링 MVC에서 enum 사용하기 2

어제에 이어 오늘도 물고 늘어진다. 이번에 해결한 문제는, DB에 저장할 값을 int 타입 뿐만이 아니라 String 타입을 사용해도 무방하도록 코드를 수정했다.

public enum UserCate implements PersistentEnum {
   
    ADMIN(“admina”, “관리자”), STAFF(“staff”, “직원”), SUPP(“supp”, “협력업체”);
   
    private final String value;
    private final String descr;
   
    private UserCate(String value, String descr) {
        this.value = value;
        this.descr = descr;
    }

    public String getValue() {
        return value;
    }
    public String getDescr() {
        return descr;
    }
   

}

이건 DB에 저장할 값으로 String 값을 사용할 enum 이고..

public enum CodeCate implements PersistentEnum {

    COLOR(10, “색상”), SIZE(20, “사이즈”), PAYTERM(30, “지불조건”), SHIPVIA(40, “운송방식”);
   
    private final Integer value;
    private final String descr;
   
    private CodeCate(Integer value, String descr) {
        this.value = value;
        this.descr = descr;
    }
   
    public int getValue() {
        return value;
    }
    public String getDescr() {
        return descr;
    }

}

이건 DB에 저장할 값으로 int 값을 사용할 enum이다.

하이버네이트가 필요로 하는 UserType을 만들어 보자.

public class CodeCateType extends GenericEnumUserType<CodeCate>{

    public CodeCateType() {
        super(CodeCate.class);
    }
   
}

public class UserCateType extends GenericEnumUserType<UserCate>{

    public UserCateType() {
        super(UserCate.class);
    }
   
}

끝이다. 도메인 객체 타입에 설정해보자. 구현에 필요한 코드만 파란색으로 강조를 했다. 생성자 만드는 부분까지 없앨 수 있을 것 같다. 3차 구현에서 해보자.

Code.java

    @Column
    @Type(type=”koma.domain.usertype.CodeCateType”)
    CodeCate codeCate;

User.java

    @Column
    @Type(type=”koma.domain.usertype.UserCateType”)
    UserCate userCate;

자.. 이러면 하이버네이트가 CodeCate는 Integet SQL 타입 컬럼을 만들어 주고 UserCate는 String SQL 타입(varchar)를 만들어 줄 것이다.

스프링 MVC에서 바인딩 할 때 사용할 PropertyEditor는 어떨까? 한 줄 씩이다.

        binder.registerCustomEditor(CodeCate.class, new GenericEnumPropertyEditor<CodeCate>(CodeCate.class));
        binder.registerCustomEditor(UserCate.class, new GenericEnumPropertyEditor<UserCate>(UserCate.class));

끝인가?? 글쎄.. 모르곘다. Charater를 추가하는 것도 별 문제 없을 듯 하다. 해보자. 이러다가 모든 타입을 지원해야 하는건 아니겠지?? Char까지만 해보자. 여기서도 생성자에 클래스를 주고 있는데 3차에서는 저 코드를 없앨 수 있게 구현해보자.

자.. 하이버, 스프링에서 자바 enum 사용 간편화 3차 구현 ㄱㄱㅆ

하이버네이트, 스프링 MVC에서 enum 사용하기

하이버네이트와 스프링 풀셋으로 구성되어 있는 웹 애플리케이션에서 자바 enum을 사용할 때 생기는 이슈가 뭘까?

1. DB에 어떤 값을 넣을 것이고,
2. 화면에는 어떤 값을 보여주고 어떻게 바인딩 할 것인가?

이 두 가지라고 한다. 그 밖에 이슈 될만한 것은.. 흠.. 뭐.. 없지 않을까 싶다. 왜 이슈일까?

1번 문제를 보자. DB에 잘 안 들어갈까? 하이버네이트로 맵핑을 해보자.

enum UserType {
 MEMBER, MANAGER
}

@Entity
class Member {

  @Column
  UserType userType;
}

이 상태로도 SessionFactory를 생성하는데 별 문제도 없을 뿐더러, 읽고 저장하기가 잘 된다. 문제는 DB에 들어가는 값이다. DB에 들어가는 값을 보면, UserType.MEMBER는 0, UserType.MANAGER는 1이 integer 컬럼에 저장된다. enum의 ordinal() 메서드가 반환해주는 값을 그대로 저장한 것이다. 문제는 ordinal() 값이 고정이 아니라, enum 순서에 따라 바뀐다는 것이다. 이런… 그럼 안 되겠다. ordinal 말고, String을 저장하고 싶다면, JPA의 @Enumerated 애노테이션을 추가해주면 된다. @Enumerated(EnumType.STRING) 이렇게 말이다. 이 것을 사용하면 방금 말한 ordinal 문제는 사라질 것이다. DB에는 ordinal이 반환하는 Integer대신 enum의 name이 저장될 것이다.

그런데.. 어떤 이유에선가 굳이 integer 값을 DB에 저장하고 싶다면 어찌해야 될까? 이제부터 복잡해진다. 일단 enum에 필드를 하나 추가하고, EJ2가 추천하는 방법으로 enum을 구현한다. 다음에는 하이버네이트의 UserType 인터페이스를 구현한 클래스를 하나 만들고,

    @Type(type=”koma.domain.usertype.CodeCateUserType”)
    @Column
    CodeCate codeCate;

이런식으로 하이버네이트의 @Type 애노테이션을 이용하여 맵핑 방법(db에 어떻게 저장하고, db에서 어떻게 꺼내 올 것인가)을 담고 있는 UserType 구현체를 지정해주어야 한다. 이 구현체를 만들 때는 UserType 인터페이스가 제공하는 메서드 10개 정도를 구현해야 된다. 귀찮은 일이다. 그래서 GenericEnumUserType 이라는 클래스를 만들었다. 간단하게 상속만 하고, 생성자만 만들면 되도록 귀찮을 일을 줄여놨다. 자 그럼 일단 첫 번째 문제는 해결이다.

두 번째 문제는 첫 번째에 비하면 비교적 쉽다. 지난 프로젝트에서 PropertyEditor와 씨름을 했던탓에 면역이 생긴 것 같다. 화면에 Enum을 보여줄 떄 enum의 name을 보여주고 싶진 않을것이다. 역시 새로운 필드를 추가해야겠다. 그리고 화면에 보여줄 때는 그 값을 출력하고, 화면에서 어떤 것을 선택했을 때에는 아까 DB에 입력한 값을 선택해서 가져오도록 화면 코드를 작성했다.

다음은 그렇게 해서 가져온 integer 값을 Enum 객체로 샥 바꿔주는 일을 할 PropertyEditor를 만드는 것이다. 간단하다. getAsText()에서는 getValue()로 가져온 객체를 내가 사용하는 enum으로 타입을 변환 한 다음 아까 추가한 필드의 getter를 사용하여 String 값을 넘겨주었다. 이제 화면에서 사용자 친화적인 문구를 볼 수 있을 것이다. 다음은 setAsTest(String text)를 재정의 하여 text는 화면에서 선택한 enum이 DB에 입력하는 값인 integer 값일 것이다, 일단 Integer.parseInt()를 해야 겠다. 아.. 이런.. Enum 클래스에서 valueOf(Class, String) 메서드를 제공해준다. 하지만 난 int 값을 사용하기로 마음 먹었으니 저 클래스는 사용하지 못하겠다. 유틸을 하나 만들었다. Enum 클래스와 int 값을 받아서 해당 int 값을 가지고 있는 Enum을 돌려받는.. 그런 클래스다. 자 그럼 이제 이 유틸을 이용해서 setAsText(String text) 구현도 마칠 수 있다. 이러한 PropertyEditor 역시 매번 만들어 쓰기 귀찮으니깐, 아예 클래스를 만들지 않고 객체만 만들어 사용할 수 있는 GenericEnumPropertyEditor를 만들었다. 두 번째 문제도 해결됐다.

오늘 내가 할 일은 이게 끝인 듯 하다. 자 그럼 잠깐 회고를 해보자.

DB에 int 값이 아닌 enum의 name 문자열을 저장한다면 어떻게 될까?

일단, UserType을 만들 필요가 없어진다. 아까도 이야기 했듯이 @Column과 @Enumerated(EnumType.STRING)를 사용하면 UserType 없이고, 문자열로 enum을 DB에 저장할 수 있다. GenericEnumUserType도 필요가 없고, 매번 UserType 클래스를 만들어야 하는 수고도 줄어든다.

다음, 화면에서 enum 목록(Arrays.asList(enum.values());를 사용하면 간단)을 보여줄 때, enum에 추가한 사용자 친화적인 설명을 담고 있는 descr 속성에 담겨있는 값을 보여주고, 실제로 선택하는 값이 DB에 저장하는 int값이 아닌 enum의 name이라면 어떻게 바뀔까? getAsText() 구현은 동일하고, setAsText()에서 받아오는 값이 Enum의 name이니깐, Enum.valueOf(Class, String)을 사용할 수 있다. 굳이 Util 클래스를 만들 필요도 없고, setAsText() 구현도 간단해진다. 다만, Enum 마다 PropertyEditor 객체를 지정해 줘야 하는 건 어쩔 수 없다. 하지만 이건 정말 일도 아니다. 새로운 클래스를 추가하는 것도 아닌데 이 일이 뭐 크게 대수겠는가.

결국.. DB에 어떤 이유로 인해 enum의 interger 값을 저장하는 것이 enum의 name 문자열을 저장하는 것보다 훨씬 복잡하고, 귀찮은 것 같다.

DB에 int를 저장하는게 좋을까 string을 저장하는게 좋을까? integer 값을 저장해야 하는 별다른 이유가 없다면 나는 enum의 name을 저장하고 싶다.

수정은 내일.. 오늘은 이만 퇴근..

===========================

할려고 했으나.. 이게 끝이 아니란다. DB에 저장할 enum 필드를 선택할 수 있게 해야 되고(결국 위에서 실컷 고민한게.. 물거품처럼 하얘지는 느낌이다.),

enum 목록을 가져올 때 정렬을 할 수 있어야 한단다.(그럼 이것도 Arrays.asList(enum.values()); 만으로는 어림 없을 듯 하다.)

또한 i18n까지도..

@_@

스프링 2.5 환경에서 하이버네이트 사용하기

참조 요약: Spring One 2008 Wokring With Hibernate in a Spring 2.5 Environment

스프링의 HibernateTemplate
(Hibernate 3.1 이전)

– 스프링이 관리하는 트랜잭션을 사용한다.
– 예외 번역 제공

public class HibernateClinic extends HibernateDaoSupport {

}

Native Hibernate DAO
(Hibernate 3.1+_

– 트랜잭션 훅(hook)을 제공한다.
– transactional session을 찾는 로직을 제공할 수 있게 한다. sessionFactory.getCurrentSession()
– 예외 번역 제공 @Repository PersistenceExceptionTranslation post processor
– 순수 하이버네이트 API만 사용할 수 있다.

단위 테스트 종류
– “Logical” 단위 테스트
– 통합 테스트

스프링으로 통합 테스트하기
– DI
– 애플리케이션 컨텍스트 로딩 줄이기
– 자동 롤백
– 하이버네이트 + JDBC 조합

AbstractTransactionalDataSourceSpringContextTests
– Ctrl + Shift + T ATDSSCT
– 애플리케이션 컨텍스트 로딩, 캐슁
– DI
– JdbcTemplate 제공

WAR에서 하이버네이트 VS OSGi 개발
– 일반 WAR 배포
  – SessionFactory가 클래스패스에 도메인 타입들을 찾는다.
  – 저장소(DAO, repositoru)들이 SessionFactory를 사용한다.
– OSGi 개발
  – 여러 도메인 번들로 쪼갠다.
  – SessionFactory는 도메인 타입에 접근해야 한다.
  – 저장소는 SessionFactory에 접근해야 한다.

OSGi 환경에서 하이버네이트
– “infrastructure” 번들
  – SessionFactory 서비스를 제공하고
  – 모든 도메인 타입을 가져온다.
– 여러 도메인 모델
  – 도메인 타입을 공개한다.
  – SessionFactory 서비스를 사용한다.
– Petclinic 예제를 제공한다.

=====

발표에서 다루는 내용이 너무 많아서 다소 산만했습니다. 하이버네이트만 집중적으로 다뤘으면 어땠을까 싶더군요. @Repository 얘기가 나오니까 컴포넌트 스캔 얘기로 새고.. 도메인 객체에 repository 객체 주입하는 얘기가 나오니까 @Configurable이랑 aspectj 얘기로 새고.. 테스트 얘기 나오니까 Test Context 쪽으로 또 얘기가 새버렸다가 나중엔 다시 하이버네이트랑 OSGi 얘기 조금 꺼내고 끝~~

멋진건 54분이라는 짧은 발표 시간 중에도 저렇게 많은 내용과 중간 중간 전부 데모까지 보여줬다는 겁니다.

스프링과 디자인 패턴

참조, 요약: 프로 스프링 2.5

인터페이스 기반 프로그래밍
생성 패턴
– Singleton: BeanFactory
– Factory
– Builder: BeanDefinitionBuilder
– Prototype: 스프링 없이 만들려면 별도의 추상 클래스 만들고 makeCopy() 같은 매서드 필요해
구조 패턴
– Proxy: 스프링 AOP
– Adapter: MessageListenerAdapter, MessageListenerAdapter102(어댑터), MessageListener
– Wrapper와 Decorator: DisplayTag
– Facade
행동 패턴
– Observer: ApplicationListener
– Strategy
템플릿 매서드
– JdbcTemplate, HibernateTemplate, HibernateCallback, …

흠~ 여기서 좀 걸리는건 템플릿 매서드 패턴의 예로 든 JdbcTemplate 및 기타 Template 들인데요. 빨간책에서는 템플릿 매서드 패턴으로 XXSqlQuery 클래스들을 꼽고 있고 XXTemplate들은 Stategy 패턴의 일종인 콜백이라고 하고 있는데 좀 혼란을 주네요. @_@

스프링을 사용하는 애플리케이션의 성능 최적화 방안

참조: Spring In Production White Paper
번역, 요약, 편역: 백기선

어떻게 하면 스프링을 사용한 애플리케이션의 성능을 향상 시킬 수 있을까?

처음으로 할 일은 성능을 측정하여 핫스팟을 발견하고 변경으로 인해 얻을 수 있는 이점을 정량화 한다. 최적화는 두 분류 효율적인 청사진 만들기(설정 튜닝하기)와 효율적인 런타임 기능 사용하기(애플리케이션 설계 최적화)로 나뉘어진다.

측정하기

튜닝은 측정부터.. 아파치 JMeter, Selenium 그리고 프로파일러를 사용한다. 스프링소스 컨설턴트들은 JAMon을 Spring AOP나 ApsectJ와 함께 사용해서 컴포넌트의 동작이나 요청 처리 경로를 프로파일링 할 때 좋은 성과를 봤다.

효율적인 청사진 만들기

효율적인 청사진 만들기의 비밀은 배포 플랫폼의 장점을 충분히 활용하는것에 있다. 스프링이 환경 종속적인 정보를 애플리케이션 코드 밖으로 빼내어 관리하기 때문에 이렇게 하는 것이 훨씬 쉽다.

데이터베이스 커넥션 풀의 경우, 애플리케이션 서버 위에서 실행하고 있다면 풀 설정을 관리자 콘솔에서 하고 스프링에는 JNDI를 통해 참조하도록 할 수 있다.

<jee:jndi-lookup id=”dataSource”
    jndi-name=”jdbc/MyDataSource”/>

이렇게 하면 두 가지 장점이 생긴다. 하나는 애플리케이션을 애플리케이션 서버 콘솔을 통해 운영팀이 관리하기 쉬워진다. 두 번째는 애플리케이션 서버 밴더가 커넥션 풀을 최적화 할 것이다.

같은 이유로 JMS ConnectionFactory 와 Destinationeh 애플리케이션 서버에 설정하고 JNDI로 얻어올 수 있다.

트랜잭션 관리의 경우도, 스프링은 커스텀 플랫폼 트랜잭션 매니저를 제공하여 여러분 배포 환경에 맞는 걸 사용할 수 있도록 해준다. ex) WebLogicJtaTransactionManager, Oc4jJtaTransactionManager,
스프링 2.5에 추가된 <tx:jta-transaction-manager/> 태그가 자동으로 기반으로 하고 있는 플랫폼을 찾아서 적절한 구현체를 선택할 것이다.

스프링 JMX를 사용하여 애플리케이션 자원을 노출할 때, 제품 플랫폼이 제공하는 MBeanServer와 연동하고 싶을 것이다. ex)  웹로직-JNDI (“java:comp/env/jmx/runtime”), WebSphereMBeanServerFactoryBean,
스프링 2.5에 추가된  <context:mbean-server … /> 태그를 사용하면 자동으로 적절한 MBeanServer를 찾아준다.

이런 장점이 있지만, 통합 테스트를 애플리케이션 서버 밖에서 하고 싶을 것이다. 예를 들어, 기본적인 메시징 테스트는 ActiveMQ를 사요해서 하지만 실제 제품을 배포할 때는 IBM의 MQSeries를 사용할 수 있다. 스프링이 여러 설정 파일을 사용하는 기능이 있기 때문에 쉽게 처리할 수 있다. 우리는 모든 환경-독립적인 설정을 핵심 애플리케이션 설정에서 분리하는 것을 추천한다. 최선책은 애플리케이션 모듈 마다 하나의 설정 파일을 유지하는 것이다. 여기에 추가로 integration-test.xml 설정과 proeduction.xml 같은 설정을 정의할 수 있을 것이다. 통합 테스트를 할 때는 적절한 모듈 설정 파일과 integration-test.xml을 사용하여 application context를 만들면 된다. 배포할 때는 production.xml 파일을 사용하면 될 것이다.

이와 관련있는 설정으로 PropertyPlaceholderConfigurer는 설정 값을 외부화 하여 운영 팀에서 변경할 수 있도록 할 때 매우 유용하다. 스프링소스 컨설턴트가 성공적으로 사용하고 있는 방법은 다음과 같이 properties 파일을 연쇄적으로 사용하는 것이다.

1. classpath*:*.properties.local: 이에 해당하는 프로퍼티 파일들은 소스 코드 관리 시스템에 포함시키지 않느다. 개발자 마다 재정의해서 쓰도록 한다.

2. classpath*:META-INF/*.properties.default: 이 속성 파일들은 빌드에 의해 생성되는 애플리케이션 요소에 포함되며 기본 설정 값들을 가지고 있다. 프로젝트의 요구사항에 따라 이 수준은 생략할 수도 있다.

3. classpath*:*.properties: 이 파일들은 애플리케이션 요소들 밖에 존재두고, 운영팀에서 쉽게 수정할 수 있게 한다.

<context:property-placeholder
  location=”classpath*:META-INF/*.properties.default,
classpath*:*.properties,
            classpath*:*.properties.local”/>

효율적인 청사진 만들기에 추가적으로 생각해 봐야 할 것들

  • 최적의 JDBC 커넥션 풀 갯수 찾아보기. 테스트 할 때 실제 배포 시나리오대로 해볼 것
  • 쓰레드 풀 구현체를 사용하는 스프링의 TaskExecutor를 사용할 때 최적의 쓰레드 풀 크기를 찾아볼 것. 처음은 CPU 갯수와 동일한 쓰레드 갯수로 설정해두고, 로컬 파일 I/O를 쓰면 쓰레드 하나를 추가하고. 네트워크 기반 I/O를 할 떈 또 몇 개를 추가하는 식으로..
  • read-only 트랜잭션일 경우에는 read-only 속성을 선언할 것. 이렇게 하면 하이버네이트를 사용하여 많은 객체를 데이터베이스에서 읽은 다음 아무 일도 하지 않았을 때 정말로 성능이 향상된다. 이렇게 하면 FlushMode.NEVER로 설정되기 때문에 세션에서 불필요한 dirth checking을 하지 않는다.
  • 만약 2단 커밋이 필요 없다면, JTA 대신에 로컬 트랜잭션 매니저 사용을 고려해 보라. 스프링은 HibernateTransactionManager를 통해서 정말 쉽게 JDBC와 Hibernate 데이터 접근을 동일한 트랜잭션에서 처리하게 해준다. 둘은 다른 방법으로 DB에 접근하지만 동일한 트랜잭션을 사용한다.
  • acknowledge=”transacted” 설정을 통해 메시지 리스너 컨테이너에 네이티브 JMS 트랜잭션 사용을 고려하라.

런타임 최적화하기

대부분의 엔터프라이즈 애플리케이션 성능 문제는 영속 계층으로부터 기인한다. Good performance here is often a function of sound design choices. 몇 가지 팁을 살펴보자.

  • ORM 툴을 사용할 때, eager와 lazy 로딩 전략의 균형을 잘 맞춰야 한다. 기본으로 로딩 지연을 사용하고, 특정 경우에만 fetch-join으로 튜닝하여 이른 로딩 장점을 활용한다. 쿼리를 튜닝할 때는 데이터 셋을 제품이 지금부터 향후 1년 간을 기준으로 하라.
  • ORM 툴이나 데이터베이스를 사용하여 로그에 SQL문이 보이도록 하라. 너무 많은 쿼리가 발생하는 이규가 생길 때 이를 쉽게 찾을 수 있다.
  • 하이버네이트를 사용할 때, 하이버네이트 Statistics 객체를 사용하여 런타임에 무슨 일이 벌어지는지 알 수 있게 하라. 프로그래밍을 통해 statics에 접근하거나, 스프링을 사용하여 하이버네이트 Statics MBean을 여러분의 MBean 서버에 노출시킬 수 있다. 프로그래밍을 통한 statics 객체 사용을 JUnit 테스트에 활용하여 여러분이 예측 가능한 쿼리가 얼마나 많이 발생하는지 확인하거나, 허용하는 쿼리 수를 기술 할 수 있다. 그 수를 벗어나면 테스트가 실패하도록.
  • 배치 스타일의 기능, 벌크 업데이트 또는 추가, 스토어드 프로시저는 보통 ORM 보다는 JDBC를 사용하는 것이 최선책이다. 스프링은 이들을 혼용하기 쉽게 해준다. 예를 들어 하이버네이트와 JDBC 데이터 접근을 동일한 트랜잭션으로 할 수 있다. 이 때 동일한 테이블을 사용하는 JDBC가 제대로 동작하려면 하이버네이트 세션을 적절한 시기에 flush 해주어야 한다.
  • 데이터베이스가 제공하는 기능을 활용하라.
    • 엑셀 스프레드시트를 일겅야 하는 애플리케이션에서 간단하게 변환하고 각 행을 SQL 서버 테이블에 넣어야 한다면 3시간이 걸리는 일도 SQL 서버 lined 쿼리를 사용하면 17초 만에 뚝딱.
    • 하이버네이트로 데이터 트리를 특정 뎁쓰로 변환하는 작업을 아무리 튜닝해도 시간도 오래 걸리고 메모리로 쫑나는데, 이걸 오라클의 스토어드 프로시저로 오라클의 계층 쿼리 기능으로 하니까 5초 미만으로 해결 됨.
    • flat 파일을 오라클 데이터베이스로 읽을 필요가 있을 때, 오라클 SQL 로더를 사용하여 데이터를 staging 테이블로 읽어들인다음, 스토어드 프로시저로 변경하고 데이터를 복사하여 원하는 테이블에 넣을 수 있다.
  • 만약 (비즈니스 로직은 전혀 없고)완전한 영속 로직만 있는 메소드가 있다면 데이터베이스의 스토어드 프로시저로 옮기고 스프링 JDBC를 사용하여 그것을 호출하라.
  • 읽기 전용 참조 데이터는 메모리 내부의 캐시에 둘 수 있다.

배치 애플리케이션은 추가적으로 고려할 것이 있다. 메소드 사용이 중요하기 때문이다. 스트림-기반 알고리즘이 최선의 선택이다. 예를 들어 컬렉션 보다는 이터레이터를 사용하라. 파일을 가지고 작업할 때, 만약 줄을 나눠야 한다면 스프링-기반이 아니라 캐릭터-기반을 사용하라. 우리는 이런 접근 방법을 사용하여 2백 50만 줄을 읽어 들인 적이 있다. 파싱하고 처리하는데 4초 미만이 걸렸고 메모리는 102K만 사용했다.

XML을 사용하는 배치 애플리케이션도 스트리밍을 사용하라. 우리는 280mb 파일에 들어있는 100,000개의 복잡한 XML 이벤트를 처리해야 할 필요가 있었다. DOM 기반의 접근 방법으로 2.5 시간이 걸렸고, 가비지 컬렉팅으로 9분이 필요했다. XML pull-parshing 기반 접근 방법으로 바꾸었더니 3초 만에 처리가 끝났고 200k 메모리를 사용했다.

또 다른 팁으로 단위 테스트와 통합 테스트를 할 때 java.lang.management 패키지에 있는 JVM 통계정보를 사용하는 것이다. 그것을 사용하면 CPU와 가비지 컬렉션 시간들을 확인할 수 있다.

데이터 계층을 위한 마지막으로 조언으로  every team benefits from access to a good DBA.

이밖에 스프링소스 컨설턴트로부터 얻은 다른 최적화 방안은 다음과 같다.

  • 스프링 배치 프로젝트가 제공하는 retry 기능을 사용하여 실패시 재시도가 필요할 때 사용할 수 있다.(예를 들어, 오라클 RAC의 개별 노드에서 실패한 기능의 경우) 사용자가 에러를 만나는 부담을 덜어줄 수 있다.
  • 웹 요소 랜더링 비용을 과소평가 하지 말아라. 트랜잭션 밖에서 되길 원할 것이다.(이부분 때문에 OSIV 패턴 이야기가 나왔군..)
  • application context를 요청 마다 새로 생성하지 말아라.
  • 스프링의 비동기 task executer를 사용하여 백그라운드에서 실행해도 될 작업을 사용자가 기다리게 하지 말아라.
  • 적절한 리모팅 프로토콜을 선택하라. 만약 SOAP 호환이 필요없다면, 스프링의 HttpInvoker 같이 간단한 스키마를 사용하는 것이 더 간단하고 빠를 것이다.
  • 스프링 AOP를 애플리케이션의 굉장히 여러 부분에 적용하고 있다면 ApsectJ 사용을 고려하라

스프링소스 컨설턴트들이 최적화에 도움을 얻은 참고자료는 다음과 같다.

  • Thomas Kyte’s “Runstats.sql” test harness
  • “Effective Oracle by Design” (Thomas Kyte)
  • “Java Performance Tuning” (Jack Shirazi)
  • Sun’s Java Performance Guides