Whiteship's Note


[하이버네이트 VS JPA] 객체 다루기

Hibernate/etc : 2010.07.27 17:09


JPA를 언젠간 써야 할텐데 아직도 하이버네이트가 그냥 편해서... @_@;; 암튼 이 둘은 객체를 다루는 API가조금 다른데 그걸 정리해둡니다.

 하이버네이트(Session) JPA(EntityManager) 설명 
save() persist()  저장(정확하게는 Pesistent 상태로 변경) 
 get() find()  DB에서 가져오기 
 load()  getReference() 프록시 가져오기 
 delete() remove()  삭제(정확하게는 Deleted 상태로 변경) 
update()  없음  reattach 다시 부착하기(정확하게는 Detached 상태에서 Persistent 상태로 변경) 
 merge() merge()  merge 병합하기(get() 해온 다음에 Detached 객체의 상태를 복사해간다. 


왠지 CRUD가 다 있어 보이지만 사실 아래 두 줄은 Update 관련 API가 아니라 Detached 상태의 객체를 Persistent 상태로 만들기 용 메서드가 뭐 이것들을 이용해서 Detached 상태 객체를 DB에 반영해서 Update 쿼리를 발생시킬 수도 있지만.. 사실 진정한 Update는 API로 존재하지 않는다. 

즉.. Persistent 상태의 객체를 가지고 어떤 속성을 변경했다 치자.. 이때 굳이 어떤 API를 써서 Update 문을 발생시키지 않아도 된다는 것이다. 

Session session = getSession(); 
Transaction tx = session.beginTransaction(); 
Book book = (Book) session.get(Book.class, 12); 
book.setName("토비의 스프링 3"); 
tx.commit(); 
session.close(); 

저렇게 변경하고 아무것도 실행하지 않는다. 왜일까? 퀴즈닷.
top

  1. Favicon of http://lf.hisfy.com/ BlogIcon 엽우 2010.07.27 20:49 PERM. MOD/DEL REPLY

    setName을 호출하고 트랜잭션이 커밋되면서(아니면 세션이 닫히면서?) 자동으로 업데이트가 되는 거 아닌가요? 하이버네이트는 대충 공부해서 쓰다 보니 정확한 답은 모르겠네요. ^^;

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.28 07:25 PERM MOD/DEL

    맞습니다. 그런걸 바로 Auto Dirty Checking 이라고 합니다.

Write a comment.


토비의 스프링 3

모하니?/Reading : 2010.07.27 10:32



http://www.yes24.com/24/goods/4020006

드디어 나왔네요. (사실 예판 시작한지는 꽤 됐는데 제가 블로깅을 깜빡하고 있었네요.ㅋㅋ 이미 한 줄 알았어요.) 제가 쿨하지 못해서 결국 제 추천사도 들어갔습니다. 온라인 서점에는 제 추천사를 볼 수 없지만 책을 사시면 제 추천사도 들어있을 겁니다. 캬캬캬.

날씨도 덥고 휴가철이라 공부하기 많이 힘드실 겁니다. 그런데 사실 이럴때가 더 공부하기 좋은 때 입니다. 남들이 덥다고 놀고 지쳐있을 때.. 그럴 때 꾸준하게 달리면.. 두배나 빠르게 달리는 거나 마찬가지 거든요. 마침 다음주부터 매주 토요일 4주 과정으로 한빛교육센터에서 이 책으로 강의를 시작합니다.

강의를 들으시는 분들께는 교재삼아 '토비의 스프링 3'이 지급될 겁니다. 교재비는 따로 내는 것 같진 않더군요. 무더운 스프링을 토비의 스프링 3으로 이겨내시죠. 이열치열삼아...
top

  1. Favicon of http://helols.pe.kr BlogIcon is윤군 2010.07.27 11:56 PERM. MOD/DEL REPLY

    study bug.;;

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.27 13:40 PERM MOD/DEL

    fix it.

  2. 내사랑꿀떡 2010.07.28 17:17 PERM. MOD/DEL REPLY

    예판 소식은 알고 있었는데...
    고민하며 미루고 있었는데... 교재로 지급된다니.. 일단.. 겸사 겸사.. 신청했습니다.
    교재로 지급되지 않더라도, ^^;; 질러야죠. ㅋ

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.29 00:11 PERM MOD/DEL

    강의때 뵙겠네요. 꼭 닉넴을 말씀해주세요.ㅋㅋ
    아마 2주차때 지급 될 것 같네요.

Write a comment.


[Spring BlazaDS Integration 레퍼런스] 3장 스프링 빈을 플렉스 리모팅용으로 공개하기

Adobe : 2010.07.26 23:18


참조: http://static.springsource.org/spring-flex/docs/1.5.x/reference/html/index.html#introduction


3.1. 도입


스프링이 관리하는 MessageBroker 는 플렉스 클라이언트가 스프링 빈을 리모팅 호출할 수 있는 형태로 손쉽게 공개할 수 있게 해준다. 이 방법은 스프링 프레임워크가 제공하는 다른 리모팅 기술과 매우 유사한 방법이다. 리모팅은 기존의 스프링 빈을 마치 외부 설정 용으로 만들어 준다. MessageBroker 는 플렉스 AMF 데이터 형식과 자바 사이의 직렬화 및 역직렬화를 투명하게 처리해준다.


3.2. 리모팅 서비스 설정


BlazeDS RemotingService 는 BlazeDS XML 설정에 있는 remoting-config.xml을 추가할 때동으로 설정된다. 스프링이 관리하는 리모팅 목적지 만을 사용할 때는 이 설정 파일 없이 스프링 설정 파일에 message-broker 태그를 추가하면 감각적인 기본값에 의해 RemotingService 가 설정된다. 그 결과 BlazeDS 설정에 remoting-config.xml에 다음과 같이 설정하는 것과 같은 것이다.


<?xml version="1.0" encoding="UTF-8"?>

<service id="remoting-service"

    class="flex.messaging.services.RemotingService">


    <adapters>

        <adapter-definition id="java-object" 

            class="flex.messaging.services.remoting.adapters.JavaAdapter" 

            default="true"/>

    </adapters>


    <default-channels>

        <channel ref="my-amf"/>

    </default-channels>

    

</service>    

    


이것은 애플리케이션 수준의 default-channels 설정이 있다고 가정한다는 것을 알아두자. 애플리케이션 수준 기본 설정에 의존하지 않는다면 원하는 서비스 관련 채널을 설정하는 것을 추천한다(아래 예제 처럼). 만약 애플리케이션 수준 기본값이 없다면, AMFEndpoint 를 사용하는 MessageBroker 에서 사용할 수 첫번째 가용 채널을 RemotingService의 기본값으로 설정한다.


만약 RemotingService에 설정되는 기본값을 더 명시적으로 제어하고 싶다면 message-broker 태그의 remoting-service remoyou하위 엘리먼트를 사용해서 설정할 수 있다. 


<flex:message-broker>

<flex:remoting-service default-adapter-id="my-default-remoting-adapter" 

    default-channels="my-amf, my-secure-amf" />

</flex:message-broker>


만약 레거시 BlazeDS 애플리케이션의 기존 remoting-config.xml이 있다면  RemotingDestinationExporter가 투명하게 모든 스프링이 관리하는 리모팅 목적지로 이전하게 해준다.


3.3. remoting-destination 태그 사용하기

remoting-destination 설정 태그는 기존에 스프링이 관리해주고 있는 서비스를 플렉스 클라이언트에서 직접 리모팅 할 수 있도록 해준다. 다음 스프링 빈 설정은 productService 빈을 설정하고 있다.


    

<bean id="productService" class="flex.samples.product.ProductServiceImpl" />

    


그리고 message-broker 태그를 설정하여 스프링이 관리하는 MessageBroker가 있다고 가정하겠다. 이때 다음의 최상위 remoting-destination 태그가 productService라는 서비스 목적지를 플렉스 클라이언트가 리모팅할 수 있게 공개해준다. 


<!-- Expose the productService bean for BlazeDS remoting -->

<flex:remoting-destination ref="productService" />

    


기본으로 플렉스 클라이언트에 공개되는 리모트 서비스 목적지는 목적지의 서비스 id로 빈 이름을 사용하는데 이 값을 remoting-destination 태그의 destination-id 애트리뷰트로 재정의할 수 있다. 


remoting-destination 태그를 사용하는 방법의 대안으로 bean 정의의 하위 엘리먼트를 사용하는 방법이 있다. 도메인 계층 빈 설정과 플렉스 리모팅 같은 기반 관련 설정을 분리할 필요가 없을 때 사용할 수 있다. (분리하는 것이 테스트를 더 편하게 할 수 있다는 것을 명심하자.) 다음과 같은 설정은 위와 동일한 결과를 가져다준다.


    

<bean id="productService" class="flex.samples.product.ProductServiceImpl" >

<flex:remoting-destination />

</bean>

    


플렉스 클라이언트가 호출할 수 있는 메서드를 remoting-destination 태그의 include-methods와 exclude-methods 애트리뷰트를 사용해서 보다 엄격하게 관리할 수 있다. 목적지를 공개할 BlazeDS 채널은 channels 애트리뷰트를 사용해서 제어할 수 있다. (최상위 엘리먼트로 사용하든 내부 엘리먼트로 사용하든 이 애트리뷰트를 사용할 수 있다.) 좀 더 커스터마이징한 예제는 다음과 같다.


<flex:remoting-destination ref="productService" 

    include-methods="read, update" 

    exclude-methods="create, delete" 

    channels="my-amf, my-secure-amf" />

    


remoting-destination 태그는 공개하는 빈 마다 투명하게 RemotingDestinationExporter 빈 인스턴스를 등록해 준다. 네임스페이스를 사용하지 않고 빈 설정을 하면 다음과 같다.


<!-- Expose the productService bean for BlazeDS remoting -->

<bean id="product" class="org.springframework.flex.remoting.RemotingDestinationExporter">

    <property name="messageBroker" ref="_messageBroker"/>

    <property name="service" ref="productService"/>

    <property name="destinationId" value="productService"/>

    <property name="includeMethods" value="read, update"/>

    <property name="excludeMethods" value="create, delete"/>

    <property name="channels" value="my-amf, my-secure-amf"/>

</bean>


3.4. @RemotingDestination 사용해서 리모팅 빈 공개하기


@RemotingDestination 애노테이션은 XML 리모팅-목적지 태그 대안으로 사용할 수 있다. @RemotingDestination 은 클래스의 타입에 사용하여 해당 빈을 공개할 수 있다. @RemotingInclude@RemotingExclude를 메서드에 사용하여 해당 메서드를 리모팅에 포함할지 제외할지 설정할 수 있다. 


다음 예제는 productService 빈을 애노테이션으로 리모팅 설정한 예에 해당한다.


package flex.samples.product;


import org.springframework.flex.remoting.RemotingDestination;

import org.springframework.flex.remoting.RemotingExclude;

import org.springframework.flex.remoting.RemotingInclude;

import org.springframework.stereotype.Service;


@Service("productService")

@RemotingDestination(channels={"my-amf","my-secure-amf"})

public class ProductServiceImpl implements ProductService {


@RemotingInclude

public Product read(String id) {

...

}

@RemotingExclude

public Product create(Product product){

...

}

@RemotingInclude

public Product update(Product product){

...

}

@RemotingExclude

public void delete(Product product) {

...

}

}

    


top

Write a comment.


[Spring BlazaDS Integration 레퍼런스] 2장 스프링에서 BlazeDS MessageBroker 설정 및 사용

Adobe : 2010.07.26 22:06


참조: http://static.springsource.org/spring-flex/docs/1.5.x/reference/html/index.html#introduction


2.1. 도입

스프링 BlazeDS Integration을 사용할 때 반드시 설정해야 하는 핵심 요소는 MessageBroker다. 플렉스 클라이언트에서 발생한 HTTP 메세지는 스프링 DispatcherServlet 을 통해 스프링이 관리하는 MessageBroker로 전달된다. 스프링이 관리하는  MessageBroker를 사용할 때는 BlazeDS MessageBrokerServlet 을 설정할 필요 없다.


2.2. 스프링 DispatcherServlet 설정

스프링 WebApplicationContext의 시작점(bootstrap)으로 보통 web.xml에 DispatcherServlet 을 다음과 같이 설정한다.

<!-- The front controller of this Spring Web application, responsible for handling all application requests -->

<servlet>

    <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>

    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    <init-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>/WEB-INF/config/web-application-config.xml</param-value>

    </init-param>

    <load-on-startup>1</load-on-startup>

</servlet>


2.3. 스프링에 MessageBroker 설정하기

WebApplicationContext에 MessageBroker를 설정할 때 편리한 스프링 XML 설정 네임스페이스가 제공된다. 그 네임스페이스를 사용하려면 스프링 XML 설정 파일에 스키마 위치를 추가해야 한다. 보통 다음 설정과 같을 것이다.


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

  xmlns:flex="http://www.springframework.org/schema/flex"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="

           http://www.springframework.org/schema/beans

           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

           http://www.springframework.org/schema/flex 

           http://www.springframework.org/schema/flex/spring-flex-1.0.xsd">

...

</beans>

    

    


이렇게 하면 설정 파일에서 flex 네임스페이스로 스프링 BlazeDS Integration 설정 태그를 사용할 수 있다. 다음 예제부터는 위와같이 설정했다고 가정을 하겠다. 이 네임스페이스에서 사용할 수 있는 모든 태그과 애트리뷰트는 spring-flex-1.0.xsd를 참조하기 바란다. 이클립스 같은 XSD를 인식하는 XML 편집기에서는 우리가 타이핑하는 것에 따라 문서를 자동으로 읽어줄 것이다.


스프링 WebApplicationContext에 최소한 MessageBrokerFactoryBean 을 설정하여 MessageBroker를 가동시켜야 하며 MessageBrokerHandlerAdapter 와 적절한  HandlerMapping (보통 SimpleUrlHandlerMapping) 을 사용하여 요청을 스프링이 관리하는  MessageBroker에 전달되게 해야한다. 


빈 설정 파일에 message-broker 태그를 등록하면 그러한 빈들을 자동으로 등록해 준다. 다음 예제가 가장 간단한 형태다.


<flex:message-broker/>    

    


이렇게 하면 MessageBroker 를 설정하고 필요한 기반 요소를 감각적인 기본값(sensible defaults)으로 설정해 준다. 그렇게 사용하는 기본값들은 message-broker 태그의 애트리뷰트 또는 하위 엘리먼트를 사용해서 재정의 할 수 있다. 예를 들어 BlazeDS XML 설정 파일의 기본 위치(/WEB-INF/flex/services-config.xml)를 services-config-path 애트리뷰트로 재정의할 수 있다. MessageBrokerFactoryBean 은 스프링의 ResourceLoader 추상화를 사용하기 때문에 스프링 리소스 패스를 사용할 수 있다. 그 예로, 애플리케이션 클래스패스에서 설정을 읽어오도록 다음과 같이 설정할 수 있다.


<flex:message-broker services-config-path="classpath*:services-config.xml"    

    


이와 동일한 순수 스프링 설정을 사용한 MessageBrokerFactoryBean 정의는 다음과 같다. 



<!-- Bootstraps and exposes the BlazeDS MessageBroker -->

<bean id="_messageBroker" class="org.springframework.flex.core.MessageBrokerFactoryBean" >

<property name="servicesConfigPath" value="classpath*:services-config.xml" />

</bean>    

    


message-broker 태그에서 특히 주목할 점은 MessageBroker에 커스텀 id를 설정할 필요가 없다는 것이다. 나중에 참조할 일도 없기 때문에 사실 그렇게 하는 것을 권장하지 않는다. 커스텀 id를 설정해야 하는 상황은 오직 WebApplicationContext에 MessageBroker를 두개 이상 가동할 경우이다.


2.4. 요청을 MessageBroker쪽으로 매핑하기


들어오는 요청을 스프링이 관리하는 MessageBroker로 전달하려면 세곳에 요청 매핑을 설정해야한다. 

  • web.xml에 DispatcherServlet 매핑
  • 스프링 WebApplicationContext에 HandlerMapping
  • BlazeDS services-config.xml에 채널 정의

가장 간단한 요청 매핑 시나리오는 앞단에 위치한 플렉스가 애플리케이션의 유일한 클라이언트인 경우이다. 이런 경우 /messagebroker를 요청 최상위 패스로 매핑할 수 있다. 그런 경우 web.xml에 다음과 매핑할 것이다.


<!-- Map all /messagbroker requests to the DispatcherServlet for handling -->

<servlet-mapping>

    <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>

    <url-pattern>/messagebroker/*</url-pattern>

</servlet-mapping>    


message-broker 설정 태그를 사용하면  SimpleUrlHandlerMapping 이 설정되어 DispatcherServlet 으로 전달되는 모든 요청을 /* 경로 패턴을 따라 스프링이 관리하는 MessageBroker 에 매핑한다. 자신이 직접 작성한 HandlerMapping 빈을 설정할 때는   message-broker 태그의 disable-default-mapping 애트리뷰트를 사용해서 기본 매핑 사용을 제어할 수 있다. 기본으로 설정되는 SimpleUrlHandlerMapping 의 순서는 mapping-order 애트리뷰트로 설정할 수 있다. (동일한 컨텍스트에 여러 핸들러 매핑 타입이 존재하는 복잡한 경우에 사용할 수 있겠다.)


스프링 WebApplicationContext의 SimpleUrlHandlerMapping 은 모든 요청을 MessageBrokerHandlerAdapter를 통해 스프링이 관리하는 MessageBroker로 전달한다. 기본으로 설정되는 message-broker 태그는 다음과 같은 빈 정의와 동일하다.


<!-- Maps request paths at /* to the BlazeDS MessageBroker -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <value>
            /*=_messageBroker
        </value>
    </property>
</bean>

<!-- Dispatches requests mapped to a MessageBroker -->
<bean class="org.springframework.flex.servlet.MessageBrokerHandlerAdapter"/>    
		


BlazeDS services-config.xml의 채널 정의가 반드시 선택한 매핑에 대응해야 한다. 예를 들어, 위 매핑 전략에 대응하는 AMF 채널을 BlazeDS에 다음과 같이 설정할 수 있다. 

 

<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
    <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" 
    	class="flex.messaging.endpoints.AMFEndpoint"/>
    <properties>
        <polling-enabled>false</polling-enabled>
    </properties>
</channel-definition> 		
		

services-config.xml에 커뮤니테이션 채널을 설정하는 더 자세한 방법은  BlazeDS documentation 에서 참조하기 바란다.


2.5. 스프링 MVC 컨트롤러와 플렉스 클라이언트 같이 사용하기


플렉스 기반 클라이언트 뿐 아니라 더 다양한 클라이언트를 지원하는 애플리케이션이 더 흔할 것이다. 예를 들어 RESTful 아키텍처를 구성하여 여러 종류의 클라이언트를 지원할 수 있다. 잠재적으로 플렉스 HTTPService 컴포넌트를 사용하여 RESTful 종점(endpoint)를 구독할 수도 있다. 스프링 MVC의 컨트롤러 모델은 RESTful 종점 같은 것을 만들기 단순하며, 유연한 방법들을 제공한다. 이러한 하이브리드 웹 애플리케이션 시나리오에서는 다른 방식의 매핑 전략이 필요하다.


가장 간단한 방법은 여러 DispatcherServlet과 계층형 애플리케이션 컨텍스트를 사용하는 것이다. 이 방법에서는 주요 애플리케이션 계층(서비스, 보안, 기반시설 지원, 등)을 ContextLoaderListener가 로딩하는 상위 컨텍스트에 두고, 스프링 MVC 컨트롤러들을 그 하위 DispatcherServlet 컨텍스트에 두고, 플렉스 클라이언트와 관련된 모든 것들을 별도의 DispatcherServlet 컨텍스트에 두는 것이다. 이 방법을 적용한 web.xml은 다음과 같다.


<context-param>

    <param-name>contextConfigLocation</param-name>

    <param-value>

        /WEB-INF/spring/*-context.xml

    </param-value>

</context-param>


<listener>

    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>


<servlet>

    <servlet-name>flex</servlet-name>

    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    <load-on-startup>1</load-on-startup>

</servlet>


<servlet-mapping>

    <servlet-name>flex</servlet-name>

    <url-pattern>/messagebroker/*</url-pattern>

</servlet-mapping>


<servlet>

    <servlet-name>spring-mvc</servlet-name>

    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    <load-on-startup>1</load-on-startup>

</servlet>


<servlet-mapping>

    <servlet-name>spring-mvc</servlet-name>

    <url-pattern>/spring/*</url-pattern>

</servlet-mapping> 

 /WEB-INF/spring/ 디렉토리에  -context.xml 로 끝나는 파일 그룹을 묶어서 부모 애플리케이션 컨텍스트를 만든다. 플렉스 관련 하위 컨텍스트는  /WEB-INF/flex-servlet.xml을 사용하고 스프링 MVC 컨트롤러는 WEB-INF/spring-mvc-servlet.xml안에 설정할 것이다. 이 방법은 깔끔하게 관심사를 분리해주며 스프링 2.5+ 애노테이션 컨트롤러가 기본으로 동작하게 해준다.


이 대안이 될 수 있는 방법으로는 단일 DispatcherServlet 컨텍스트를 사용하는 것이다. 이 방법의 단점은 몇 가지 부가적인 설정을 필요로 한다. /spring/* 요처을 DispatcherSerlvet이 처리하고 mapping XML 네임스페이스 설정 태그를 사용해서 /messagebroker/*를 스프링이 관리하는 MessageBroker로 전달하는 것이다. 그런다음 BlazeDS 채널 정의를 적절하게 수정해야 한다. message-broker 태그로 기본 매핑 전략을 다음과 같이 수정할 수 있다.


<flex:message-broker>

    <flex:mapping pattern="/messagebroker/*" />

</flex:message-broker> 


그런다음 BlazeDS의 채널 정의에서 /spring/*을 고려해서 다음과 같이 수정해야 한다. 


<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">

    <endpoint url="http://{server.name}:{server.port}/{context.root}/spring/messagebroker/amf" 

    class="flex.messaging.endpoints.AMFEndpoint"/>

    <properties>

        <polling-enabled>false</polling-enabled>

    </properties>

</channel-definition> 


단일 매핑 전략에서는 message-broker 태그가 자동으로 등록해주는 HandlerMapping과 HandlerAdapter가 있기 때문에 스프링 MVC 문서에 따라 스프링 MVC 컨트롤러를 위한 HandlerMapping와 HandlerAdapter을 직접 등록해 줘야 한다.




top

  1. 미랭군 2010.08.16 16:16 PERM. MOD/DEL REPLY

    그런데 이렇게 부모 *-config.xml로 묶게 되면 flex에선 환경설정파일들을 잘 읽어들이는데 spring-mvc에선 *-config.xml 파일들을 불러들이지 못하는 문제가 있네요..

    <context:component-scan base-package="base"/>

    flex나 mvc나 둘다 스캔해야해서 이것을 app-config.xml이라는 부모환경설정파일로 뽑아서

    했는데..flex에선 잘 읽어들이는데..mvc디스패쳐서블릿에선 패키지에 있는 정보를 읽어들이지

    못합니다. 혹시 이 원인에 대해서 아시나요?

    Favicon of http://whiteship.me BlogIcon 기선 2010.08.17 20:56 PERM MOD/DEL

    메일로 문의하신 내용이군요.

    *-context.xml을 상위 WAC로 사용하기 때문에 두개의 DispatcherServlet의 WAC에서 모두 *-context.xml에 있는 빈을 참조할 수 있어야 하는게..맞습니다.

    그래도 안된다고 하시니깐.. 흠냐. 한번 확인해 보겠습니다.

Write a comment.


리눅스 시간 맞추기

Linux : 2010.07.23 16:40


맥이나 윈도우는 얼마나 편해 ㅠ.ㅠ 
시간 한번 맞추기가 이렇게 힘들어서야 원...


커맨드만 복사해 놔야지.. 링크가 고장날지도 모르니...

    [root@Zeus ~]# date 
    Fri Aug  3 08:05:03 UTC 2007 
    [root@Zeus ~]# hwclock --show 
    Fri 03 Aug 2007 05:39:07 PM UTC  -0.870183 seconds 
    [root@Zeus ~]# echo $TZ 

    [root@Zeus ~]# export env TZ=KST-09:00:00 
    [root@Zeus ~]# date 
    Fri Aug  3 17:05:45 KST 2007 

    [root@Zeus ~]# date 
    Fri Aug  3 18:05:21 KST 2007 
    [root@Zeus ~]# hwclock --show 
    Fri 03 Aug 2007 06:39:45 PM KST  -0.540050 seconds 
    [root@Zeus ~]# rdate -s time.bora.net 
    [root@Zeus ~]# hwclock --systohc 
    [root@Zeus ~]# date 
    Fri Aug  3 18:06:56 KST 2007 
    [root@Zeus ~]# hwclock --show 
    Fri 03 Aug 2007 06:07:01 PM KST  -0.211481 seconds 

    export env TZ=KST-09:00:00 
    rdate -s time.bora.net 
    hwclock --systohc 


top

TAG 리눅스

Write a comment.


[하이버네이트 배치 추가] flush와 clear

Hibernate/etc : 2010.07.23 14:18


배치 작업이라는 것이 DB에서 데이터를 읽어온 다음 뭔가 수정하고 뭔가를 다시 DB에 넣는 작업인데 이런 작업을 하이버네이트로 할 때 조심해야 할 것이 있습니다.

                InvDailyClosing invDailyClosing = new InvDailyClosing();
                invDailyClosing.setDate(today);
                invDailyClosing.setLocation(location);
                invDailyClosing.setItem(item);
                invDailyClosing.setQtyStart(qtyStart);
                invDailyClosing.setInvInList(invInList);
                invDailyClosing.setInvOutList(invOutList);
                invDailyClosing.closing();
                dao.add(invDailyClosing);

이렇게 작성하면 끝난것 같지만 사실 좀 위험한 코드입니다. 만약 저 코드가 for 루프 안에 있고 굉장히 여러번 반복 된다면 언젠가는 MemoryOutOfException을 보게 될 겁니다. "아니 왜?" 라고 하시는 분들이 계실텐데요. 

흠... 하이버네이트 1차 캐시 때문에 그런 현상이 생깁니다. 하이버네이트는 Persistent 상태의 객체를 하이버네이트 Session에 담아둡니다. 여기사 1차 캐시입니다. 캐시니까 저안에 담아놓고 재사용할 수 있습니다. DB에 다녀오는 횟수를 줄일 수 있겠죠. 그런데 언젠가는 이 캐시를 DB와 동기화 시켜야 합니다. 그래야 Session에 담겨있는 객체(Pesistent 객체)를 지지고 볶은 것이 DB에 반영이 되겠죠. 그렇게 DB와 Session 상태를 동기화 시키는것을 Flush라고 하는데.. 또 Persistent 상태라는 것은... 아.. 이런.. 안되겠군요. @_@ 그냥 하이버네이트 완벽 가이드를 읽어주세요.

아무튼 너무 많이 쌓여서 메모리가 부족해지는 상황이 발생하지 않도록 계속해서 Session을 DB에 동기화 시키고 Session을 비워주는게 좋습니다.

하이버네이트 Session의 flush()와 clear()이 바로 그런 용도로 존재하죠. 그래서 

                InvDailyClosing invDailyClosing = new InvDailyClosing();
                invDailyClosing.setDate(today);
                invDailyClosing.setLocation(location);
                invDailyClosing.setItem(item);
                invDailyClosing.setQtyStart(qtyStart);
                invDailyClosing.setInvInList(invInList);
                invDailyClosing.setInvOutList(invOutList);
                invDailyClosing.closing();
                dao.add(invDailyClosing);
             dao.flushAndClear();

이렇게 하이버네이트용 GenrericDao에 flushAndClear()라는걸 만들어 놓고 써주면 됩니다. 주의하실 것은... 반드시 flush 먼저 하고 나서 clear 해야 합니다. 반대로 하면 음식 담긴 쟁반을 서빙도 안하고 설것이 해버리는거나 마찬가지..

top

  1. Favicon of https://dazzilove2.tistory.com BlogIcon dazzi 2010.07.24 12:22 신고 PERM. MOD/DEL REPLY

    난 개인적으로 배치는 pl/sql 추천! 데이터베이스의 성능을 최대한 이용하는것을 더 선호하는편이얌. 특히 대형사이트에서 자바배치 돌리는건 상당히 무모해보인다는. ^^;;; 새벽에 다 돌아줘야하는 배치들이 아침까지 돌고있거나 순차적으로 돌아야하는 디펜던시 걸려버리면 진짜 답 안나옴 --;; 쿼리도 oltp성이냐 아니냐에따라 플랜을 다르게 가져가야 하듯이 배치도 상황에따라 수행주체를 바꿔줘야할 필요성이 있는것같오. ㅋㅋ. 생각이 그렇다는거~~ 아무래도 내가 디비도 좋아하다보니 이런글에 굳이 덧글달고 있는듯 ^^;;;

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.26 14:25 PERM MOD/DEL

    티스토리가 이상하네요. 댓글 달렸는데 제목 목록에 표시도 안되고 댓글 목록에도 표시가 안되고.. 흠..

    전 딱히 배치를 꼭 자바로 하자 DB로 하자는 주위는 아닌데 아직 배치 로직 검증 단계라.. (일단 제가 후딱 해볼 수 있는)자바로 배치 로직짜서 검증하고 나중에 그걸 SQL로 바꾸던지 해도 될 것 같네요.

    대용량이 아니라 하더라도 20초면 될 일을 20분 동안 하게 되는 사태는 벌어지면 안되겠죠.

Write a comment.


법치와 민주주의...





배경음악이 좋아서 긁어온 것이지 별다른 의도는 없습니다.
top

  1. Favicon of http://www.yangkun.pe.kr BlogIcon 양군 2010.07.21 01:49 PERM. MOD/DEL REPLY

    배경 음악 정말 좋네요.

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.21 21:00 PERM MOD/DEL

    글쵸! 그 중에는 아내랑 같이 친 연탁곡도 있네요.

Write a comment.


주말 강의는 2주 연기

모하니?/Thinking : 2010.07.20 21:05


휴가철이라 그런지 수강생이 10명이 채 안되서 주말 강의를 부득이 하게 2주 연기하게 되었습니다. 스프링 강의 뿐 아니라 아이폰까지도 그런걸 보면 제 잘못은 아닙니다. ㅋㅋ 전 정말 열심히 준비 했고 열심히 강의 했습니다. 물론 부족한 점도 많았지만 앞으로 계속 조금씩 보완해 나가겠습니다. 2주가 연기되서 오히려 좋은 점도 있습니다. 잘하면 토비님 책을 들고 바로 수업을 시작할 수 있을 것 같습니다. 

거럼 다음 강의 때 뵐께요~

top

Write a comment.


[Adobe] BlazeDS는 뭔가?

Adobe : 2010.07.20 20:55


http://opensource.adobe.com/wiki/display/blazeds/BlazeDS/

스프링 BlazeDS라는 프로젝트가 있다. 근데 BlazeDS가 뭔지 모른다. 그래서 살펴봤다.

http://opensource.adobe.com/wiki/display/blazeds/Overview

어도비 플랙스나 에어로 클라이어언트 단을 만들고 자바로 만든 서버 단과 연동하는데 필요한 서비스를 제공해준다. 거기다.. XML이나 SOAP 같은 텍스트 기반 서비스 보다 10배나 빠르단다.. 흠.... Push 기능도 구현할 수 있게 해준다는데.. 조금. 구미가 땡기기 시작한다.

주요 서비스는 다음과 같다.

- 리모팅: 서버단의 자바로 만든 서비스를 바로 호출할 수 있게 해준다.
- 메세지 서비스: 배포/구독 방식 메시징으로 실시간으로 데이터를 주고 받는 애플리케이션을 개발할 수 있다.
- 프록시 서비스: 뭔지 감이 잘 안옴.


top

TAG Abode, BlazeDS
  1. 선영욱 2010.07.21 08:57 PERM. MOD/DEL REPLY

    오오~배포/구독이 된다니~멋진데요. 채팅/게임서버를 구현 가능하겠습니다.

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.21 21:01 PERM MOD/DEL

    한번 써보고 싶어요. 흠.. 액션스크립트에서 자바 코드를 호출한다는 것인지.. 자바 코드로 MXML을 조작한다는 것인지.. 아마도 전자일듯..

Write a comment.


[스프링 3 이해와 선택] 2차 강의 진행합니다.

모하니?/Planning : 2010.07.19 15:52


이번주 토요일부터 시작입니다.

http://www.hanbitedu.co.kr/incumbent/shortContent.do?index=1097

강의 준비하느라 홍보를 게을리 했더니 수강생이 몇 분 안되는 듯 합니다. ㅠ.ㅠ
아니면 휴가철이라 그럴지도...

강의는 토비의 스프링 3을 교재로 진행하고, 대략 일정은 다음과 같습니다.

1주차
- 오브젝트와 의존관계
- 테스트
- 템플릿

2주차
- 예외
- 서비스 추상화
- AOP

3주차
- IoC 컨테이너와 DI
- 데이터 엑세스 기술

4주차
- 스프링 웹 기술과 스프링 MVC
- 스프링 @MVC
- 스프링 기타기술

소스코드, 코딩 동영상, PPT 까지 준비 된 상태이고 앞으로는 게임이나 퀴즈 같은 게임 요소를 추가할 생각입니다. 무더운 날씨에도 즐겁게 공부해 봅시다.
top

  1. Miracle 2010.07.19 18:37 PERM. MOD/DEL REPLY

    악~ 듣고 싶엉~ ㅎㅎ

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.20 06:25 PERM MOD/DEL

    강의 열심히 준비 했는데 ㅠ.ㅠ 한번으로 끝인가...

  2. 2010.07.29 12:02 PERM. MOD/DEL REPLY

    비밀댓글입니다

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2010.07.29 13:20 신고 PERM MOD/DEL

    감사합니다.ㅎㅎ

Write a comment.


[토비의 스프링 3] 2차 완독 후기



결국 추천사를 쓸 수 있게 됐습니다. 캬캬캬.
배려해주신 출판사 부사장님과 토비님께 감사드립니다. 잘 쓸께요. 아.. 부담 100배!!!!



----------------지난 이야기-----------------

사실 완독이라고 할 수는 없다. 이번엔 강의 준비를 하면서 다시 봤는데 강의할 부분만 읽었기 때문에 모든 글을 다시 읽진 않았다. 읽으면서 중간 중간 코드와 API 설명이 나온 부분을 유심히 보면서 혹시 소스 코드에 잘못된 것은 없는지 검증하는 작업을 했다. 내가 빅뱅의 쉘든 같은 사람이었다면 완벽하게 리뷰를 할 수 있었을텐데 난 레너드 수준에서 리뷰를 마감했다. 1차 때 보다 훨씬 적운 수의 리뷰를 드렸지만 품질 만큼은 1차때 보다 더 나은 리뷰를 해드린것 같아 다행으로 생각하고 있다. 그래도 분명히 놓친 부분이 있을텐데... 그건 개정판을 내실 때 수정할 수 있도록 출판사에 제보해주면 좋겠다. 아마 별도의 페이지가 있을지 싶다.

한 가지 아쉬움이 남는다. 난 추천사를 못 썼다. 근래 가장 많은 시간과 애정을 쏟아가며 리뷰한 책이고 그 누구보다도 여러 개발자에 이 책을 추천해주고 싶었는데 그럴 수 없었다. 여태까지 본 책 중에 이렇게까지 자발적으로 추천사를 쓰고 싶은 책은 없었는데 조금 안타깝다. 

내가 이 책을 리뷰 한 것은 이 책에 내가 적은 글을 남기고 싶어서도 아니고 돈을 받고 싶어서도 아니고 강의를 하고 싶어서도 아니었다. 돈은 애초에 생각도 안했고, 강의는 이미 리뷰를 시작한 다음에 계획 됐다. 추천사는 이 책을 읽다보니 너무도 쓰고 싶어졌지만 이젠 상관없다. 난 그냥 이 책 내용이 정말 좋았고 많은 도움을 받았다. 나도 어떻게든 이 책이 조금 더 반짝거리게 해주고 싶었다. 또 기나긴 여정에서 고군분투 하시는 토비님에게 조금이나마 힘이 되고 싶었다. 그것으로 됐다.
top

  1. Favicon of http://koko8829.tistory.com BlogIcon 열이아빠 2010.07.16 17:48 PERM. MOD/DEL REPLY

    추천사를 못쓰신것이 아쉽다는것보다
    더 강렬한 추천의 글은 없을것 같습니다. ^^
    기대가 되네요.

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.16 19:26 PERM MOD/DEL

    ㅎㅎㅎ그랬나요.

  2. Favicon of http://yunsunghan.tistory.com BlogIcon Max 2010.07.16 18:19 PERM. MOD/DEL REPLY

    서운함이 느껴지네....
    걱정이네요, 너무 기대치를 높여 놔서....-_-;;;

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.16 19:26 PERM MOD/DEL

    절대로.. 제 블로그의 모든 글을 걸어도 좋습니다.
    실망하실 일 없습니다.

  3. Favicon of http://toby.epril.com BlogIcon 토비 2010.07.16 19:49 PERM. MOD/DEL REPLY

    기선이 추천사도 싣기로 결정!

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2010.07.16 20:37 신고 PERM MOD/DEL

    ㄱㅅㄱㅅ!!

  4. Favicon of http://moer.egloos.com BlogIcon mOer 2010.07.18 16:28 PERM. MOD/DEL REPLY

    빅뱅 재미있죠 :-)

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2010.07.18 20:10 신고 PERM MOD/DEL

    넌 라즈...

Write a comment.


[스프링 3.0] 상속구조에서 @RequestMapping 퀴즈

Spring/3.0 : 2010.07.13 15:09


아무도 안 풀 것 같지만... 자신이 @RM을 얼마나 이해했는지 측정해보기 위해서는 좋은 방법이니까 시간나면 꼭 해보시기 바랍니다.

@RequestMapping("/hier")
public class SuperController {
@RequestMapping("/list")
public String list(Model model){
model.addAttribute("message", "hier super list");
return "/WEB-INF/views/hello.jsp";
}

}

@Controller
public class SubController extends SuperController {
}

1. 이때 /hier/list 요청을 하면 처리가 될까? 

@Controller
public class SubController extends SuperController {
@Override
public String list(Model model){
model.addAttribute("message", "hier sub! list");
return "/WEB-INF/views/hello.jsp";
}
}

SubController 코드를 이렇게 바꿨다. 

2. 이때 /hier/list를 요청했을 때 화면에 찍히는 ${message}의 값은 무엇인가?

@RequestMapping("/hier2")
public class Super2Controller {
}

@Controller
public class Sub2Controller extends Super2Controller {
@RequestMapping("/list")
public String list(Model model){
model.addAttribute("message", "hier list");
return "/WEB-INF/views/hello.jsp";
}

}

3. 이때 /hier2/list를 요청했을 때 핸들러가 실행될까?

public class Super3Controller {
@RequestMapping("/list")
public String list(Model model){
model.addAttribute("message", "hier2 super list");
return "/WEB-INF/views/hello.jsp";
}
}

@Controller
@RequestMapping("/hier3")
public class Sub3Controller extends Super3Controller {

}

4. 이때 /hier3/list 요청이 처리 될까?

@Controller
@RequestMapping("/hier3")
public class Sub3Controller extends Super3Controller {
@Override
@RequestMapping("/list")
public String list(Model model){
model.addAttribute("message", "hier2 sub~! list");
return "/WEB-INF/views/hello.jsp";
}

}

5. 구현체를 이렇게 바꾸면 에러가 날까? 

6. 그렇치 않다면? ${message}의 값은 어떻게 될까?

@RequestMapping("/hier4super")
public class Super4Controller {
@RequestMapping("/all")
public String list(Model model){
model.addAttribute("message", "hier4 super list");
return "/WEB-INF/views/hello.jsp";
}
}

@Controller
@RequestMapping("/hier4")
public class Sub4Controller extends Super4Controller {
@Override
@RequestMapping("/list")
public String list(Model model){
model.addAttribute("message", "hier4 sub list");
return "/WEB-INF/views/hello.jsp";
}

}

7. /hier4suprer/all 이라는 요청은 처리 될까?

8. /hier4/list 라는 요청은 처리 될까?

정답은 토비님 책 또는 이번주 강의에서..

오늘의 퀴즈 2종 세트를 다 맞추시는 분은 @ReuqestMapping 마스터!

top

Write a comment.


[스프링 3.0] 클래스-메서드 레벨 @RequestMapping 퀴즈

Spring/3.0 : 2010.07.13 12:12


이미 2.5부터 추가된 기능이고 가장 자주 사용하고 있는 애노테이션 @ReqeustMapping.. 과연 얼마나 알고 있을까? 

public class Book2Controller {
@RequestMapping("/book2/add")
public String book2Add(Model model){
model.addAttribute("message", "book2 add");
return "/WEB-INF/views/hello.jsp";
}

@RequestMapping("/book2/get")
public String book2Get(Model model){
model.addAttribute("message", "book2 get");
return "/WEB-INF/views/hello.jsp";
}
}

1. 이 클래스를 <bean />을 사용해서 빈으로 등록하면 /book2/add 이나 /book2/get 요청 핸들러가 동작할까?

2. 만약 1번에서 false를 선택했다면 위 코드의 @RM이 동작하게 만드는 방법 두가지는 무엇일까?

@Controller
@RequestMapping("/book3")
public class Book3Controller {
@RequestMapping
public String add(Model model){
model.addAttribute("message", "book3 add");
return "/WEB-INF/views/hello.jsp";
}

@RequestMapping
public String get(Model model){
model.addAttribute("message", "book3 get");
return "/WEB-INF/views/hello.jsp";
}
}

3. 이렇게 매핑 했을 때 /book3/add 와 /book3/get은 동작할까?

4. 만약 3번에서 false를 선택했다면 Book3Controller의 @RequestMapping 설정을 어떻게 고치면 동작하게 할 수 있을까? (역시 두가지)

@Controller
@RequestMapping("/book3/*")
public class Book3Controller {
@RequestMapping
public String add(Model model){
model.addAttribute("message", "book3 add");
return "/WEB-INF/views/hello.jsp";
}

@RequestMapping
public String get(Model model){
model.addAttribute("message", "book3 get");
return "/WEB-INF/views/hello.jsp";
}
}

5. 위와 같이 설정했을 때 /book3/a/b/c/add 는 동작할까 안할까?

6. 만약 5번에서 동작하지 않는다고 대답했다면.. Book3Controller의 @RM 설정을 어떻게 고치면 동작하게 될지 적어보자.

정답은.. 토비님 책 또는 이번주 강의에서...
top

Write a comment.


네가 있어서 정말 감사하다.

모하니?/Thinking : 2010.07.12 23:32


지난 주말 장모님 댁에 갔을 때 '오늘 하루도 눈을 뜨면 그 자체로 너무 감사하고 고맙다.'라는 말을 들었다. '네'라고 대답은 했지만 사뭇 이해가 되지 않았다. 어떤 뜻인지는 머리로는 이해가 되는데 가슴으로는 와닿질 않았다. 나는 요즘 눈을 뜨면 '오늘은 적어도 이걸 해야되는구나...  아.. 저것까지도 하면 좋겠는데..' 이런 생각을 제일 먼저 한다. '내가 건강한 상태로 지금 오늘을 맞이 할 수 있어서 정말 고맙다.' 이런 생각은 거의 안해본것 같다. 오히려 불만이 더 많았다. '난 왜 이것도 못하지... 난 왜 이것 밖에 안돼지.. 난 왜.. 난 왜.. 난 왜..."  이런 생각만 하고 살아왔다고 해도 과언이 아니다.

그런데, 오늘 문득 성윤이를 만나고 돌아오는 길에 그런 생각이 들었다. '성윤이를 만난건 정말 대단한 행운이구나. 고맙다.'라는 생각이 들었다. 성윤이가 봄싹에서 열심히 활동해 준 것도 고마운 사실이지만 사실 성윤이가 스터디에서 활동한 것 보다는 김성윤이라는 개발자를 알게 된 것 자체가 고맙게 느껴졌다. 정말 치열하게 삶에 도전하고 노력하는 성윤이 같은 개발자가 내 옆에 있었다는 사실이 고맙게 느껴졌다.

이제 몇일이 지나면 온라인으로밖에 볼 수 없는 사이가 되겠지만... 뭐 설마 영영 못보겠어? 다시 볼 그날까지 절대로 너에게 부끄러운 모습이 되지 않도록 유념하며 살겠다. 그러니 너도 잘 살고 있어야 돼. 

고맙다.

나중에 만나서 라이브 코딩으로 한판 겨뤄보자꾸나.. 통키와 타이거처럼.. 말이지..





top

  1. Favicon of https://helols.tistory.com BlogIcon is윤군 2010.07.12 23:55 신고 PERM. MOD/DEL REPLY

    저도 저도 !! 형 만나서 ..
    덕분에 정말 즐거운 2년 이라는 시간을 보냇어요.
    많이 배우고 .. 많이 느끼고 ~

    제가 좀 아직은 냄새만 맡을 줄 아는 허접한 개발자이지만..
    SF 가서 열심히 영어 공부해서..ㅋㅋ 다음에 볼때는 우리 영어로 대화 하자구요 ..ㅋ

    라이브 코딩도 대결 붙고..!!
    그리고 온라인에서 ㅎㅎ 잘 놀면 되죠 머;;ㅋㅋ SF 가서 권한부터 꼭.. ㅠㅠ 자꾸 미루어서 미안하구요..

    정말 함께한 시간이 감사했어요 !!
    앞으로도 좋은!! 개발자가 되자구요!! 세계정복 하는 그날까지..
    (시간좀 되면 같이 유켄한테 가서 맞짱 함뜨자고 합시다..ㅋㅋ )

    귿럭 맨!

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2010.07.13 12:32 신고 PERM MOD/DEL

    내가 통키다.. 외국물 먹은 너가 타이거야.

    Favicon of http://helols.pe.kr BlogIcon is윤군 2010.07.14 02:13 PERM MOD/DEL

    뭘로 보나 ㅋㅋ 제가 통키죠 ..ㅋㅋ 형이 타이거 하삼.

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.14 14:38 PERM MOD/DEL

    무슨소리야.. 당연히 내가 통키지.
    통키 VS 짝퉁 통키 로 한판 해보자는거야?

Write a comment.


버즈 버그 미투 장애




1. 사진 안나옴
2. 난 '좋아요'를 취소 했는데... 아직도 좋아요 상태.

자기들도 알테니.. 열심히 고쳐주겠지. 어이 구글~ 잘못하면 웨이브 꼴 나는거야... 열심히 해줘...
그나저나 첫날은 8시간을 자라는데.. 그럼 난 무조건 지각인데;; 흠... 10시에 자야되나.. 1시간 남았네;; @_@


미투는 아에 장애다... 장애;;; 흠.. 어감이 별로네. 서비스 "점검중"이 훨씬 좋겠다.  

흠... 미투 가입할때 주민번호를 넣었던가.. 안넣은것 같기도 하고. 암튼 한국 사이트들은 제대로 지켜줄꺼 아니면 주민번호좀 그만 받아라. 주민번호 누출시켰으면 제대로 보상이나 뒷처리를 해주던가... 아! 그것도 무슨 법 때문에 그런거라고 들은것 같은데 잘 기억은 안나네.. 흠...


top

TAG 버즈

Write a comment.


[이전글에 이어지는 이야기] 숫자에서 객체로...

모하니?/Coding : 2010.07.07 13:21


... 윗부분 생략 
               double qtyIn = invInDao.getQtyOf(today, item, location);
                // 금일 출고량 조사
                double qtyOut = invOutDao.getQtyOf(today, item, location);
                // 금일 재고량 계산
                double qtyEnd = qtyStart + qtyIn - qtyOut;

                if(isEmpty(qtyStart, qtyIn)){
                    continue;
                }

                InvDailyClosing invDailyClosing = new InvDailyClosing();
                invDailyClosing.setDate(today);
                invDailyClosing.setLocation(location);
                invDailyClosing.setItem(item);
                invDailyClosing.setQtyStart(qtyStart);
                invDailyClosing.setQtyIn(qtyIn);
                invDailyClosing.setQtyOut(qtyOut);
                invDailyClosing.setQtyEnd(qtyEnd);
...아래도 생략

쓰고나서 생각하니 약간 위험한 글을 썼다는 생각에 괜히 불안했는데..  아니나 다를까... 대용량 데이터베이스를 공부하고, 자바 코드로 치우져서 보는거 아니냐, 벽을 세워놓고 한쪽만 보는건 좋치 않다. 라는 글이 달렸었는데 사라졌다.

하아.. 본문에도 적었지만 사실 내가 대용량 DB를 다루는 SQL을 잘 못하는건 사실이다. 근데 어쩌겠는가.. SQL을 하고 싶지 않아서 안하는게 아니라 SQL을 작성할 일이 별로 없는데다, 대용량 데이터는 내가 만지고 싶다고 만질 수 있는 것도 아니다. 난 이럴 때 마다 떠올리며 나를 위로하는 문구가 있는데.. 켄트벡이 쓴 "테스트 주도 개발" 뒷부분 어딘가 보면 그런 말이 있다. 아.. 이런.. 문제의 크기와 해결방법에 관한 거였는데.. 까먹었다. 암튼 0과 1을 떠올리게 하는거였는데 ㅠ.ㅠ

생각해보니, 재고 마감 데이터가 입고와 출고 수량만 들고 있는게 아니라, 해당 마감 데이터에 해당하는 입고 목록과 출고 목록을 들고 있는게 더 유용하겠다는 생각이 들었다. 그렇게 해야 "재고 마감 뷰"에서 해당 마감에 대한 입고 내역과 출고 내역을 보여주기 편할 것 같다. 아니면 마감 데이터를 가지고 해당 마감에 해당하는 입고목록과 출고 목록을 가져오는 기능을 입고DAO와 출고DAO에 추가해줘야 하는데... 글쎄... 흠냐.. 그거 만들기 귀찮아서 기능을 고치고 있는 기분도 들지만, 왠지.. 설계상으론 더 객체 지향적이고 멋진 것 같다.

이런 로직을 반영하려면 숫자보다 객체를 들고 있게 하는게 편하고.. SQL 보다.. 위와 같은 자바 코드를 수정하는게 더 편한데...그건 내가 그렇다는 것이고...  사람에 따라, 상황에 따라, 대용량인지 아닌지에 따라.. 달라질 수 있겠다..

                List<InvIn> invInList = invInDao.listOf(today, item, location);
             List<InvOut> invOutList = invOutDao.listOf(today, item. location);

                if(isEmpty(qtyStart, invInList)){
                    continue;
                }

                InvDailyClosing invDailyClosing = new InvDailyClosing();
                invDailyClosing.setDate(today);
                invDailyClosing.setLocation(location);
                invDailyClosing.setItem(item);
                invDailyClosing.setQtyStart(qtyStart);
                invDailyClosing.setInvInList(invInList);
                invDailyClosing.setInvOutList(invOutList);
                invDailyClosing.closing();

                dao.add(invDailyClosing);

결국은 이렇게 바뀔 것 같다. InvDailyClosing 도메인 객체의 closing() 메소드에서 입고 목록, 출고 목록, 전날 재고를 가지고 금일 재고를 계산하도록...말이다. 물론.. 이정도 쯤이야. SQL로도 간단하게 할 수 있겠다(?). 난 잘 모르겠지만..

top

Write a comment.


복잡한 로직은 복잡한 DAO로 직결되는가?

모하니?/Coding : 2010.07.07 10:14


아니지 않을까... 비즈니스 로직이 복잡하다고 해서 SQL이 복잡해 질 필요는 없다.

재고 마감 로직을 생각해보자. 모든 창고에 들어있는 모든 상품들의 전일 재고량, 금일 입고량, 금일 출고량을 가져와서 금일 재고량을 계산한다고 치자. 이걸 가지도 또 상품 카테고리 별로 묶고 창고 별로 묶어서 집계를 내야 하는데.. 일단은 그 전 단계까지만 해보자.

Location - Inventory
Item - Inventory
Item - InvIn
Item - InvOut
Item - InvDailyClosing

이렇게 관계가 맺어져 있으니 SQL로 join을 하던 sub select를 하던 어떻게든 SQL 한방으로 어떤 창고에 있는 어떤 상품의 '전일 재고량', '금일 입고량', '금일 출고량'을 한방에 가져올 수 있을 것 같다.

근데 난 그렇게 하지 않는다. 사실 그렇게 복잡한걸 만들지도 못한다. 만들라면 공부해가면서 만들겠지만 무지 오래 걸릴 것 같다. 그래서 난 그냥 자바 코드로 SQL을 대신한다.

        dao.delelteAllAt(today);

        for(Location location : locationDao.getAll()) {
            // 모든 상품 마다
            for(Item item : itemDao.getAll()){
                // 전일 재고량 조사
                double qtyStart = inventoryDao.getQtyOf(yesterday(today), item, location);
                // 금일 입고량 조사
                double qtyIn = invInDao.getQtyOf(today, item, location);
                // 금일 출고량 조사
                double qtyOut = invOutDao.getQtyOf(today, item, location);
                // 금일 재고량 계산
                double qtyEnd = qtyStart + qtyIn - qtyOut;

               나머진 생략...
            }
        }

즉 이런식으로 짠다. 굵은 글씨 부분은 DAO test를 해서 해당 기능이 내가 원한대로 동작하는지 일일히 확인해다. DBUnit을 사용해서 테스트용 데이터를 넣고 저 기능을 실행해보는 방식이다. 이렇게 구현하니까 상당히 마음이 놓인다. 저걸 만약 한방에 SQL로 구현했다던지.. 테스트 없이 구현했다면.. 글쎄.... 그렇겐 못했을 것 같다.

암튼 이래서.. 복잡한 로직을 처리하는 DAO 코드라 할지라도 단순해진다. 이런걸 디바이드 앤 퀀쿼 라고 하던가.. 몰겠다. 머라하든.

    public double getQtyOf(Date date, Item item, Location location) {
        Object result = getSession().createQuery("select sum(qty) from InvIn where date = :date and item = :item and location = :location group by item")
                .setDate("date", DateUtils.getDateOnly(date))
                .setEntity("item", item)
                .setEntity("location", location)
                .uniqueResult();
        if(result == null)
            return 0.0;
        return (Double) result;
    }

대충 이정도 코드가 생기는데.. 이정도야 뭐.. 초간단 SQL 수준 아닌가..

이렇게 짜면 걱정되는게.. 성능인데... DB에 자주 다녀올수록 더 많은 부하가 생기는건 사실이지만 어차피 복잡한 쿼리 자체도 join 여러번 하면서 부하가 생길테니.. SQL 도사가 아닌 이상 차라리 성능을 조금 포기하고 유지 보수 가능하고 알아보기 쉬운 코드로 나눠서 작성하는게 더 좋치 않을까 싶다.





top

  1. 비트겐슈타인 2010.07.07 11:05 PERM. MOD/DEL REPLY

    배치 로직이면 모르겠는데. 온라인 로직이라면 흠... (for 문의 압박 -_-)
    기본 생각은 저도 동의합니다. 덩치큰 SQL 보다는 작은 SQL을 잘게 던지는게 DB에 cpu 부하를 덜 주기도 하구, DB 캐싱도 한결 수월해질 것 같고...

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2010.07.07 12:51 신고 PERM MOD/DEL

    이 글이 모든 경우를 SQL 대신 자바 코드로 해결하자는 건 아니었는데... 그렇게 읽히는 분도 계신가 봅니다. 어흑..@_@

  2. Favicon of http://starplatina.tistory.com BlogIcon June 2010.07.14 17:14 PERM. MOD/DEL REPLY

    기선님 댓글 보고 좀 생각해 보니까요.

    좀 젊은 사람들 20대~30대 초초초반 사람들은 SQL 로직보다 자바 코드가 더 익숙하지 않을까 싶은 생각이 드는데요.

    저 같은 경우에도 공부를 하면서 DB 쪽은 DBA들이 하도록 하자는 생각이 좀 강하고, 그런 마인드를 가지고 있으니 비즈니스 로직을 작성을 자바 코드에서 하는 것이 쉽고, SQL에서 하면 버벅 대네요.

    업무 종류도 기존에는 c/s 환경을 web으로 바꾸는 것 이다 보니 쿼리 중심인 경우가 많았는데, 요즘 프로젝트가 또 쿼리 중심 보다는 자바 비즈니스 로직 중심인 경우도 있으니깐 그럴수도 있다는 생각이 드네요 ㅋ

    그나저나 스프링 책 언제 나오나요. 어서 재촉좀 해 보세요 ㅎㅎ

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.14 17:43 PERM MOD/DEL

    흠.. 넹 하지만 group by 같은건 잘쓰면 for 루프를 줄일 수 있어서 좋은 것 같아요. 뭐.. 다루는 데이터가 많지 않으면 그냥 for 뤂 돌려도 상관없을 것 같은데.. 잘 모르겠네요. 때에따라 여러 방법이 있을텐데.. 비즈니스 로직을 무로건 SQL로 구현하는건 암튼 좀 아닌듯.. 이런 저런 상황에 따라 쉽게 바뀌는데 로직인데 테스트나 변경 편의성 등을 볼 땐 자바 코드가 좋을 듯 싶어요.

    토비님 책은 이제 3장까지 마무리 하셨답니다. 이번주에 다 마무리 하시고 출판하면 7월 말에는 정말 나오지 않을까 싶네요.

  3. Favicon of https://slothink.tistory.com BlogIcon 편현장 2010.07.16 01:25 신고 PERM. MOD/DEL REPLY

    ㅎㅎ 부디. itemDao.getAll() 의 결과가 무지막지하지 않게 나오길 빌어야겠네요^^
    저도 왠만하면 위의 예제처럼 짜서 빠르게 만들었다가 성능 문제로 최소한의 쿼리를 사용하게 변경하게 하곤 하는데요. 변경하는건 쉬운데, 문제가 발견되면 꼭. "하이버네이트라서 느린건가보다. 하이버네이트에선 해결이 안되는거니?" 라는 말이 나오는지라.. 가급적 n+1 의 문제를 안드러내려고 하죠.
    번역해주신 책에 가빈 킹이 하이버네이트의 맞는 사용자는 sql 을 잘 알며, 객체지향적으로 설계하고 싶은 고급 사용자라고 하는데 그 말이 맞는거 같습니다. 정말 좋은 프레임웍인데, 대충 쓰다간 욕을 먹죠. 에긍.. 기선님이 일하시는 곳엔 하이버네이트 등에 열린 시각들을 가지고 계신가요?

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.16 11:00 PERM MOD/DEL

    앗 위험한 부분을 잘 짚어주셨군요. 하이버네이트 책 3부에 보시면 n+1 문제 해결책을 여럿 소개하고있긴한데 당장은 성능보다 비즈니스 로직 분석하고 코드로 그걸 잘 반영하고 있는지 검증하는 단계인지라.. 최적화 문제는 일단 로직 검증이 끝난 뒤에 해결해야 될 것 같습니다. (재고 마감이 좀 복잡하네요.ㅠ.ㅠ)

    흠.. 제가 일하는 곳이라.. 여긴 거의.. 제 맘대로에요.

Write a comment.


[IBM DW] HTML5 navigator

View/HTML/CSS : 2010.07.06 20:41


http://www.ibm.com/developerworks/opensource/library/x-html5mobile1/index.html
http://www.ibm.com/developerworks/kr/library/x-html5mobile1/ (번역)

HTML5에 대해 궁금하던 찰나 무심코 IBM DW에 들어가봤더니.. 역시나 좋은 글들이 마구 쏟아지고 있었다. 

이 글에서 소개하는  내용은 흔히 GPS라 부르는 기능에 관한 것이다. 그것만 보면 심심할까봐 트위터 API랑 연동해서 접속한 사람 근방의 트위터글을 검색해 주는 애플리케이션을 소개하고 있다.

현재 위치 알아내기

            var gps = navigator.geolocation;
            if (gps) {
                alert("gps enabled");
                gps.getCurrentPosition(searchTwitter,
                        function(error) {
                            alert("Got an error, code: " + error.code + " message: " +
                                    error.message);
                        });
            } else {
                searchTwitter();
            }

첫번째 인자는 성공시 실행할 콜백, 두번째 인자는 실패시 실행할 콜백, 세번째는 옵션인데 생략했군요.

성공시 실행할 콜백에 전달되는 인자는 position

        function searchTwitter(position) {
            alert("searchTwitter position=" + position);
            var query = "http://search.twitter.com/search.json?callback=showResults&q=";
            query += $("kwBox").value;
            if (position) {
                var lat = position.coords.latitude;
                var long = position.coords.longitude;
                query += "&geocode=" + escape(lat + "," + long + ",50mi");
            }
            alert("query=" + query);
            var script = document.createElement("script");
            script.src = query;
            document.getElementsByTagName("head")[0].appendChild(script);
        }

저기서 꺼내서 사용할 값은 주로 coords의 latitude(위도)와 longitude(경도). 여기서 잠깐... 위도 경도가 뭐더라.. 어떤게 가로인지 세로인지 햇갈린다. 적도를 기준으로 상하 각도를 위도. 북극과 남극은 그래서 위도 90. 경도는 영국 그리니치 천문대를 기준으로 좌우 각도.  서울은 북위 37도, 동경 126도.

위치 추적하기

trackerId = gps.watchPosition(function(pos){
var latLng = new google.maps.LatLng(pos.coords.latitude,pos.coords.longitude);
map.setCenter(latLng);
theUser.setPosition(latLng);
showLocation(pos);
});

watchPosition을 사용하면 계속해서 좌표 받아옴.

function stopTracking(){
if (trackerId){
navigator.geolocation.clearWatch(trackerId);
}
}

trackerId를 clearWatch에 넘겨줘숴 종료한다.

트위터 API랑 구글맵 API 사용법도 나오는데... 생략~




'View > HTML/CSS' 카테고리의 다른 글

[IBM DW] HTML5 navigator  (0) 2010.07.06
[CSS] 탭 만들기  (2) 2009.05.26
[CSS] 링크 적용 범위 넓히기, 롤오버 효과 주기  (0) 2009.05.22
[CSS]그라데이션 배경 사용하기  (2) 2009.05.22
XPath Path Expression 문법  (0) 2009.05.15
Chapter 14. Image Replacement  (0) 2008.03.31
Chpater 13. Styling Text  (0) 2008.03.31
Chapter 10. Applying CSS  (0) 2008.03.26
Chapter 9. Minimizing Markup  (0) 2008.03.23
Chapter 8. More Lists  (0) 2008.03.23
Chapter 7. Anchors  (0) 2008.03.23
top

Write a comment.


스프링 DAO 3파전

모하니?/Coding : 2010.07.02 13:00


JDBC를 사용하는 부류가 있고, iBatis를 사용하는 부류가 있고, 하이버네이트를 사용하는 부류가 있다. 사실 이보다 더 다양한 영속화 기술들이 있지만.. 이 세 부류로 간추려 보려고 한다. 자 이들은 각각 어떻게 코딩을 하고 있을까.

public interface MemberDao {
void add(Member member);
void update(Member member);
Member get(int id);
List<Member> list();
void delete(int id);

}

이런 인터페이스를 각 부류에서 구현한다고 생각해보자.

아참, 전제는 스프링을 사용한다는 것이다.

1. JDBC

@Repository
public class MemberDaoJdbc implements MemberDao{
@Autowired SimpleJdbcTemplate jdbcTemplate;
public void setMemberMapper(RowMapper<Member> memberMapper) {
this.memberMapper = memberMapper;
}

RowMapper<Member> memberMapper = new RowMapper<Member>(){
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
Member member = new Member();
member.setId(rs.getInt("id"));
member.setName(rs.getString("name"));
member.setJoined(rs.getDate("joined"));
return member;
}};
public void add(Member member) {
jdbcTemplate.update("insert into member(id, name, joined) values (?, ?, ?)", 
member.getId(), member.getName(), member.getJoined());
}

public void delete(int id) {
jdbcTemplate.update("delete from member where id = ?", id);
}

public Member get(int id) {
return jdbcTemplate.queryForObject("select * from member where id = ?", memberMapper, id);
}

public List<Member> list() {
return jdbcTemplate.query("select * from member", memberMapper);
}

public void update(Member member) {
jdbcTemplate.update(
"update member set name = :name, joined = :joined where id = :id", 
new BeanPropertySqlParameterSource(member));
}

}

대충 이런 코드가 된다. 스프링이 제공해주는 SimplJdbcTemplate은 멀티 쓰레드 환경에서 공유하면서 사용해도 안전한 객체이다. 따라서 빈으로 등록해놓고 주입받아서 쓴다고 한들 문제될 것이 없다. 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/testContext.xml")
@Transactional
public class MemberDaoJdbcTest {
@Autowired MemberDaoJdbc memberDao;
@Test
public void di(){
assertThat(memberDao, is(notNullValue()));
}
@Test
public void crud(){
Member member = new Member();
member.setId(1);
member.setName("whiteship");
member.setJoined(new Date());
memberDao.add(member);
assertThat(memberDao.list().size(), is(1));
member.setName("기선");
memberDao.update(member);
assertThat(memberDao.get(1).getName(), is("기선"));
memberDao.delete(1);
assertThat(memberDao.list().size(), is(0));
}
}

테스트는 대충 만들었기 때문에 너그럽게 봐주시길...^_^;;;

2. iBatis

스프링에 iBatis의 SqlMapClient를 만들어 주는 팩토리빈을 등록해야 한다.
   
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="SqlMapConfig.xml" />
</bean>

아이바티스 설정 파일도 추가한다. 저 설정에 보이는 SqlMapConfig.xml이 필요하다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig      
    PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"      
    "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>
<sqlMap resource="sample/ibatis/Member.xml" />
</sqlMapConfig>

그 다음 Member와 관련있는 SQL을 모아둔 Member.xml 파일을 만든다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap      
    PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"      
    "http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="Member">
<typeAlias alias="Member" type="sample.domain.Member" />

<delete id="delete" parameterClass="int">
delete from member where id = #id#
</delete>

<insert id="add" parameterClass="Member">
insert into member (id, name, joined) values(#id#, #name#, #joined#)
  </insert>
 
<update id="update" parameterClass="Member">
update member set name = #name#, joined = #joined# where id = #id#
</update>
 
<select id="get" parameterClass="int" resultClass="Member">
select * from member where id = #id#
  </select>

<select id="list" resultClass="Member">
select * from member order by id
  </select>
</sqlMap>

XML 엘리먼트와 어트리뷰트는 별도의 학습이 필요하지 않을만큼 직관적이다.. 이제 DAO 코드를 작성하자.

@Repository
public class MemberDaoIbatis implements MemberDao {
@Autowired SqlMapClientTemplate sqlMapClientTemplate;
public void add(Member member) {
sqlMapClientTemplate.insert("add", member);
}

public void delete(int id) {
sqlMapClientTemplate.delete("delete", id);
}

public Member get(int id) {
return (Member) sqlMapClientTemplate.queryForObject("get", id);
}

@SuppressWarnings("unchecked")
public List<Member> list() {
return sqlMapClientTemplate.queryForList("list");
}

public void update(Member member) {
sqlMapClientTemplate.update("update", member);
}

}

흠 많이 짧아졌다. 그런데.. 전체 코드량은 더 많이 늘은것 같지 않은가? 코드량이 척도의 전부는 아니지만.. 왠지 작업이 줄었다가 보다는 DAO에서 할일을 설정파일로 미뤘다는 느낌이 강하다. 아참 여기서 사용한 SqlMapClientTemplate도 SimleJdbcTemplate과 마찬가지로 빈으로 등록해서 사용해도 별 지장이 없는 쓰레드-안전한 클래스기 때문에 빈으로 등록해놓고 사용했다.

3. Hibernate

이것도 역시 빈을 하나 설정해줘야한다.
 
   <!-- ============================================================= -->
    <!--  Hibernate                                                    -->
    <!-- ============================================================= -->
    <bean id="transactionManager"
          class="org.springframework.orm.hibernate3.HibernateTransactionManager"
          p:sessionFactory-ref="sessionFactory"/>

    <bean id="sessionFactory"
          class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="sample.domain" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

이번건 설정이 제법 길다. 그리고 빈이 하나가 아니라 두갠데.. 그건 나중에 TransactionManager에 대한 글을 올릴 때 설명해야겠다. 아니면 그냥 토비님 책을 보기 바란다. 11장에 자세히 설명해주시고 있다. TransactionManager와 기반 기술의 관계(?)랄까나.. 암튼.. 이제 바로 DAO를 만들 수 있다. 아 아니다 일단 Member 클래스에 애노테이션 부터 추가하자.

@Entity
public class Member {

@Id
int id;

...
}

이렇게 두 군데에 각각 하나씩만 붙여주면 된다. 이제 DAO를 만들자.

@Repository
public class MemberDaoHibernate implements MemberDao{
@Autowired SessionFactory sessionFactory;

public void add(Member member) {
getSession().save(member);
}

public void delete(int id) {
getSession().createQuery("delete from Member where id = ?")
.setInteger(0, id)
.executeUpdate();
}

public Member get(int id) {
return (Member) getCriteria()
.add(Restrictions.eq("id", id))
.uniqueResult();
}

@SuppressWarnings("unchecked")
public List<Member> list() {
return getCriteria().list();
}

public void update(Member member) {
getSession().update(member);
}

private Criteria getCriteria() {
return getSession().createCriteria(Member.class);
}

private Session getSession() {
return sessionFactory.getCurrentSession();
}
}

SQL이 없다. SQL처럼 보이는 HQL(굵은 글씨)만 보인다. 이렇게만 하면 정말 DAO가 구현되는지 의심스럽겠지만 정말 구현이 끝난다. 하지만 하이버네이트 Session과 Criteria API는 약간 학습이 필요하다. 자주 쓰는 API는 몇개 안되기 떄문에 대략 1시간 정도면 익힐 수 있다.

셋 개 다 해본결과.. 난 역시 하이버네이트가 편하다.
top

  1. Favicon of http://lf.hisfy.com/ BlogIcon 엽우 2010.07.02 18:08 PERM. MOD/DEL REPLY

    쭉 하이버네이트만 써봐서 iBatis는 어떨까 궁금했는데 덕분에 궁금증이 해소되었네요.
    저도 하이버네이트가 가장 예뻐보입니다.

    다만 애노테이션을 추가해줘야 하는게 쫌 아쉽네요.
    뭐랄까... 모델은 모델대로 따로 놔두고 설정을 분리하고 싶은 강박증 때문이랄까? ^^;

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.03 13:32 PERM MOD/DEL

    그러게요. 저도 그부분이 아쉽긴한데;
    XML로 설정 빼내기가 귀찮아서 그냥 애노테이션으로..ㅋ;;

  2. 코바 2010.07.03 14:32 PERM. MOD/DEL REPLY

    SqlMapConfig.xml 설정은

    스프링2.5 최신버전부터 p:mappingLocations="classpath*:xxx/**/sqlMap.xml"

    이런 속성으로 설정을 간편화가 됩니다.

    의외로 sql에 비중이 높은 프로젝트에서 ibatis도 쓸모가 많습니다.

    전 하이버네이트를 제대로 안해봐서 한번 해보고 싶네요.

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.03 22:50 PERM MOD/DEL

    오호! 그렇군요~
    아이바티스는 저도 수년만에 첨써보는거라;;

  3. Favicon of http://jjaeko.tistory.com BlogIcon 째코 2010.07.06 18:39 PERM. MOD/DEL REPLY

    지금까지 ibatis만으로 프로젝트를 진행했는데 과연 지금 하고 있는 이 프로젝트가
    ibatis대신 하이버네이트로 가능했을까? 라는 생각이 들곤했어요.
    핑계이긴 하지만 쿼리자체가 곧 업무였고, 길고 복잡했기 때문에요.
    복잡한 쿼리는 hql로는 감당이 안될거 같았고, native sql을 쓰자니 ibatis의 다이나믹 태그가 필요했고...
    기선님은 복잡한 쿼리를 어떻게 해결하시나요?

    하이버네이트 공부할땐 잼있었는데 저도 꼭 한번 프로젝트에 써보고 싶네요 ㅎ

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.06 19:42 PERM MOD/DEL

    흠.. 간단하게 말씀드리자면 저는 복잡한 쿼리를 작성하지 않습니다.

    복잡한 비즈니스 로직이 있긴한데.. 그게 꼭 복잡한 쿼리로 연결되진 않더라구요. 복잡한 서비스 코드가 만들어지긴 하더군요.

    오늘 회사에서 쬐끔 복잡한 부분을 코딩하고 왔는데. 내일 낮에 포스팅 하겠습니다.

Write a comment.