2013년 3월 15일 금요일

Spring 을 이용한 hibernate 환경설정

진부한 내용일 수 있지만, Spring 을 이용해 hibernate 에 대한 환경 설정에 대한 정보가 대부분 단편적이거나, 하나의 DB에 대해서만 설명한 글이 대부분이라...
hibernate 가 여러 DB를 동시에 만족 시킬 수 있음을 강조하고, 솔루션을 만들 때 여러 DB에 대해 만족할 수 있도록 테스트를 쉽게 하기 위해 간단하게나마 hibernate 설정을 spring 의 @Configuration을 이용하여 제작해 보았습니다.

우선 모든 DB에 대해 공통적으로 적용되는 부분에 대해 다음과 같이 정의했습니다.


public SessionFactory sessionFactory() {...} 함수가 가장 중요하고, 나머지는 뭐 별로...
당연히 hibernate 설정 클래스이니까... ㅎㅎ
sessionFactory를 만드는데, 여러가지 필요한 설정들을 지정하게 하는데, 미리 정의된 부분도 있고, 사용자가 더 필요한 부분은 "setupSessionFactory(factoryBean)" 위임 메소드를 이용하여, 추가로 정의 할 수 있도록 했습니다.

그럼 대표적으로 사용하는 DB인 HSQL, MySQL, PostgreSQL 에 대한 기본 환경설정 클래스를 보겠습니다.

1. HSql 메모리 DB 사용 (단순 테스트시 유용)

2. MySQL

3. PostgreSql

4. PostgreSql with pgpool-II

과 같다.

아주 사소한 port 번호 같은 것은 기본 값을 사용했다.
뭐 각각 다른 것은 DB명, ConnectionString, Dialect 등이다. 별 차이 없지만, 이렇게 구조적으로  상속체계를 해 놓으면, 실전에서 아주 편리하다.

실제 사용하는 예는 HSql 과 PostgreSql 의 예를 들어보자.

1. HSql 메모리 DB를 이용한 환경 설정 (2nd 캐시도 적용됨)


2. PostgreSql 의 "HAccess" DB 를 이용한 환경 설정 (2nd Cache도 적용됨)

이 것으로 끝나는게 아니고, Application 용 환경설정에서는 다음과 같이 위의 두 가지 환경설정 Class 를 입맛에 따라 Import 해서 테스트를 수행하면 된다.

정말 쉽죠?

이제 실제 단위 테스트 클래스에서 AppConfig를 지정해서 테스트를 수행하시면 됩니다^^

ORM의 장점 중에 여러 DB에 대해 일관된 작업이 가능하므로, 솔루션을 만들때는 최선의 선택이라 생각하는지라, 위와 같은 설정으로 개발을 수행합니다. (비록 한가지 DB만을 대상으로 하는 시스템이라도... 향후 어떻게 될지 모르고, 코드 재활용성도 생각하고^^)

2013년 3월 12일 화요일

hibernate 가 identity column을 clustered index 로 만들지 않는 이유

오랜만에 글을 쓰네요...
요즘 프로젝트 문서 작성을 주로 하는 바람에 글을 쓸 밑천이 없었네요 ㅎㅎ
이런 얕은 내공이라구...

오늘은 그동안 궁금했던 "왜 SQL Server 는 primary key가 clustered index 인데, PostgreSQL, MySQL 등은 clustered index 가 아닌가?" 에 대해 그냥 RDBMS 마다 속 사정이 있겠지... 하고 넘어갔었는데, hibernate 로 매핑을 수행하던 도중에 혹시나 하는 마음에 구글링을 해 봤습니다...

이런 논의가 hibernate 에서도 진행되었었네요...

원문( https://hibernate.onjira.com/browse/HHH-3305 ) 을 보면, Clustered Index 자체가 성능이 좋긴 하고, SQL Server 는 Primary Key가 기본으로 Clustered Index 지만

1. 다른 DB들은  Primary Key가 Clustered Index 가 아니다.
2. 유지 관리에 애로점이 있다.
3. SQL Server 는 DBA 기술이 없는 작은 조직에 초점이 맞춰져있다.
4. Clustered Index 를 삭제하고자 할때에는 부가적인 작업이 필요하다.

흠 이런 이유 때문인지는 모르지만, SQL Server 는 hiberante mapping test 시에 association으 변경 될 때 drop 시 실패하는 경우가 다반사입니다. oracle, postgresql은 괜찮구요. 물론 drop cascade 를 지원하기 때문이기도 하구요...

그동안 습관적으로 Primary Key는 Clustered Index 여야만 해!!! 라는 고정관념을 깨게 되었습니다.

뭐 꼭 필요하다면, DBA 가 튜닝 단계에서 적용해주는게 합당하겠죠^^



2013년 2월 16일 토요일

Spring MVC Controller 선후처리기 만들기

ASP.NET 웹 어플리케이션은 어플리케이션 Lifecycle, Page의 Lifecycle 에 상세한 event 를 정의하고 있어, event handler를 정의하면, 여러가지 선처리나 후처리를 수행할 수 있습니다.

Spring MVC 에서는 어떻게 하나 봤더니 Controller 에 Interceptor 를 등록하면 되더군요.

단계를 요약하자면...

  1. org.springframework.web.servlet.HandlerInterceptor 또는 org.springframework.web.servlet.handler.HandlerInterceptorAdapter 를 상속받아 preHandler, postHandler, afterComletion 등에 원하는 작업을 구현합니다.
  2. servlet.xml 에 위에서 작성한 Interceptor 를 등록합니다.

아주 쉽죠?

그럼 실제 예제와 함께 보시죠. 예제는 Spring Framework 3.2.1.RELEASE 와 Hibernate 4.1.9 Final 로 제작했습니다.

UnitOfWorkInterceptor 는 사용자 요청이 있으면 Start 하고, 요청 작업이 완료되면 Close 하도록 합니다. 이는 Hibernate 를 이용하여 Unit Of Work 패턴을 구현하여, 하나의 요청 중에 모든 작업을 하나의 Transaction으로 묶을 수 있고, 웹 개발자에게는 Unit Of Work 자체를 사용하기만 하면 되고, 실제 Lifecycle 은 Spring MVC 에서 관리하도록 하기 위해서입니다.


보시다 시피, preHandle 에서는 Controller의 메소드가 호출되기 전에 (즉 Unit Of Work 를 사용하기 전에) UnitOfWorks.start() 를 호출하여, hibernate의 session을 열어, Hibernate session 작업을 할 수 있도록 미리 준비해줍니다.

postHandle 에서는 아무 작업도 하지 않는데, 실제 View 에서 ViewModel 를 binding 할 시에, lazy initialized 되는 associated entity를 후에 가져 올 수 있기 때문입니다. 여기서 unit of work 를 끝내버리면, session을 닫아버려, lazy initialize 작업에서 예외가 발생하게 됩니다.

afterCompletetion 에서는  View 작업까지 완료되고, Client 에 Reponse 를 보내기 바로 직전에 호출됩니다. 여기서 unit of work 를 close 합니다.

다음으로는 웹 어플리케이션에 Spring MVC Context 를 설정 시 interceptor를 등록하기만 하면 됩니다.


위와 같이 bean 등록 시에 interceptor를 등록해 주시면 됩니다^^
Spring AOP 를 사용해도 되지만, 요 방법이 가장 간단하고, 쉽게 이해할 수 있을 것 같습니다.

다른 블로거의 글에는 logging 으로 예제를 많이 많들었더군요...
asp.net 으로는 제가 간단하게 성능 측정하는 코드를 예제로 만들어보긴 했지만, 쓸모는 없지요^^
위의 unit of work관련 코드는 asp.net 으로 구현한 것은 상용 제품의 코드로 쓰이고, 아직도 많은 회사에서 구동되고 있습니다...
이를 spring mvc, hibernate 로 포팅한 작업입니다.