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


토비의 스프링 3

모하니?/Reading : 2010.07.27 10:32



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

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

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

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


[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

Adobe : 2010.07.26 23:18 Trackback. : 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


리눅스 시간 맞추기

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 리눅스
Linux : 2010.07.23 16:40 Trackback. : 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


법치와 민주주의...





배경음악이 좋아서 긁어온 것이지 별다른 의도는 없습니다.
저작자 표시
신고
top


주말 강의는 2주 연기

모하니?/Thinking : 2010.07.20 21:05


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

거럼 다음 강의 때 뵐께요~

저작자 표시
신고
top


[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

[스프링 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


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



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



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

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

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

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


[스프링 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


[스프링 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


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

모하니?/Thinking : 2010.07.12 23:32


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

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

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

고맙다.

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





저작자 표시
신고
top


버즈 버그 미투 장애




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

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


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

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


저작자 표시
신고
top

TAG 버즈

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

모하니?/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


복잡한 로직은 복잡한 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


[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


스프링 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







티스토리 툴바