Whiteship's Note


Principle of least astonishment

모하니?/Coding : 2008.09.24 22:06


참조: http://en.wikipedia.org/wiki/Principle_of_least_astonishment

astonishment라는 생소한 단어 때문에 뜻을 가늠하기가 어렵습니다. astonishment는 경악, 놀람이라는 뜻으로, "최대한 놀래키지 말라는 원칙"입니다. 

위키피디아의 정의를 인용하면 다음과 같습니다.

In user interface design, programming language design, and ergonomics, the principle (or rule or lawof least astonishment (or surprise) states that, when two elements of an interface conflict, or are ambiguous, the behaviour should be that which will least surprise the human user or programmer at the time the conflict arises.

즉, 두 개의 애매모호 하거나 상이한 인터페이스 요소는 사용자가 프로그래머에게 혼란을 줄 수 있으니 주의하라는 것입니다.

예를 들어, 보통 Ctrl+S는 저장 단축키로 인식되고 있는데, 이 단축키를 창닫기 용으로 사용하면 어떻게 될까요? 좀 더 코딩에 가까운 예를 들면, 오버로딩한 메소드 두 개가 있을 때 그 둘의 행동은 같아야 합니다. 즉, add(Cat)과 add(Dog)은 둘 다 더해주는 객체만 달라질 뿐 내부에서 하는 일은 동일해야 하는거죠. 둘의 행위가 넘겨져온 객체에 따라 상이해질 경우 개발자나 사용자에게 혼란을 줄 수 있을 겁니다.

API 설계시에 주의해야할 원칙인 듯 합니다. OSAF를 다시 한번 검토해봐야겠습니다. Hibernate에는 사실 UPDATE sql을 직접 날릴 필요가 없습니다. 자동으로 dirty checking을 해서 필요한 순간에 flushing(DB와 SessionContext 동기화)를 할 때 필요한 UPDATE 문을 날려주기 때문입니다. 그래도 가끔은 Session의 reattch() 메소드를 사용해서, 원하는 순간에 명시적으로 UPDATE 문을 날리는 것과 비슷한 결과를 만들 수 있습니다. 

이때, reattach()를 DAO에서는 update()로 감싸야 하는게 좋을지. 그냥 reattch()라고 해야 할지 고민입니다.
신고
top


OSGi에서 SessionFactory(Hibenate) 사용하기

Spring DM/exercise : 2008.08.25 14:59


참조 :
http://www.osgi.org/blog/2007/06/osgi-and-hibernate.html
http://notehive.com/wp/2008/07/23/osgi-hibernate-spring-dm-sample/

번들 세 개만 살펴보겠습니다.

1. hibernate-class
2. hibernate-session
3. model-a

1. hibernate-class

이 녀석은 하이버네이트 라이브러리를 묶어놓은 번들입니다. 얘가 담고 있는 라이브러리는 다음과 같습니다.
사용자 삽입 이미지

이렇게 묶어놓은거 말고 스프링 번들 저장소에서 다운로드해서 일일히 설치해도 되야 할 것 같은데, 저번에도 해봤고, 오늘도 다시 시도 해봤는데, 똑같은 문제가 발생하고 있어서 실패했습니다. 아무래도 스프링 DM 지라에 올리던가 포럼에 올려서 물어봐야 할 것 같네요.

2. hibernate-session

이 녀석이 하는 일은 많습니다. 테스트 DB를 만들고, 하이버네이트 애노테이션을 사용한 도메인 클래스들도 있고, SessionFactory도 만듭니다. 단, SessionFactory를 만들 때 좀 특이하게 com.notehive.osgi.hibernate_samples.session.DynamicConfiguration 클래스를 사용하여 생성합니다. 실질적으로 SessionFactory를 만들어 내는 클래스입니다. 이 클래스의 핵심 메소드인 createNewSessionFactory() 메소드에서 JDK Proxy 클래스를 사용해서 프록시 객체를 생성하고 있습니다.

OSGi 서비스로 com.notehive.osgi.hibernate_samples.session.DynamicConfiguration를 공개하고 있습니다.

3. model-a

이 번들에는 하이버네이트 애노테이션을 사용한 모델과, DAO 구현체가 들어있습니다. SessionFactory와 TransactionManager를 내부에서 빈으로 정의하고 있는데, SessionFactory를 만들 때 위에서 살펴본 hibernate-session 번들이 공개한 com.notehive.osgi.hibernate_samples.session.DynamicConfiguration 서비스를 사용합니다. SessionFactory를 빈 설정만 보면, hibernate-session이나 model-a나 동일합니다.

단, 이 빈은 com.notehive.osgi.hibernate_samples.session.DynamicConfiguration 서비스를 가져옵니다. 그리고 DAO에서는 해당 서비스를 사용해서 만든 SessionFactory를 사용하죠.

그리고 DynamicConfigurationListener 라는 빈을 등록해서 DynamicConfiguration 이 객체에다가 하이버네이트 애노테이션이 붙어있는 클래스를 추가하거나, 제거하는 코드를 넣어뒀습니다.

마지막으로 주목할 것은 osgi.bnd 파일의 설정인데..

Hibernate-Contribution: default; \
    classes="com.notehive.osgi.hibernate_samples.model.a.A1"

이런식으로 속성/값을 추가해뒀고, 이 값을 hibernate-session 번들의 BundleTracker가 읽고 DynamicConfiguration에 애노테이션이 붙은 클래스를 추가/제거 합니다. 그 뒤엔 당근 새로운 SessionFactory를 만들어 가지게 됩니다.



캬...이제야 OSAF를 돌릴 수 있는(정확하겐 하이버네이트 App를 돌릴 수 있는) 플랫폼을 구성하는 실마리를 찾은 것 같습니다.

신고
top


OSGi에서 Hibernate의 SessionFactory 문제

Spring DM/exercise : 2008.08.14 15:34


대체 어떻게 해야 할까? 뭘? @Entity 달려있는 클래스들이 여러 번들들에 분포되어 있고, 애플리케이션이 돌아가는 도중에 번들이 추가되고, 없어지고, 다시 설치되고, 업데이트 되는 와중에 SessionFactory는 그에 따라 계속 바껴야합니다.

Spring이 제공하는 AnnotationSessionFactoryBean 클래스로 만드는 SessionFactory는 정적입니다. 한 번 만들고 다른 빈들이 주입받아서 사용하는데, 도통 어떻게 변경해야 할지 모르겠습니다.

이게 문제는 OSGi, Hibernate, Spring DM, JPA를 사용하려다 보면 자연스럽게 다가오는 문제입니다. 저 말고도 이미 예전부터 이 문제를 당면한 여러 개발자들이 있었습니다. 지금 이 순간 "나는 iBatis를 쓰고 있어서 다행이야!!!" 라고 외치고 계신 분이 혹시 계신가요??? ㅎㅎㅎ 어림없습니다. 이 문제에서 못 벗어납니다. SessionFactory 대신에 SqlMapClient로 놓고 생각해 보시면 똑같습니다.

설정 파일을 번들 하나에 전부 넣어 놓고(애노테이션 붙인 도메인 객체들을 모두 한 번들에 놓고), 그걸로 SessionFactory 만들면 되지 않겠냐구요? 아니.. 미래에 추가될 번들에 들어있는 도메인을 어떻게 지금 추가할 수가 있나요? 백투더퓨쳐가 아닌이상 불가능할 뿐더러, 그럴 바엔 아예 OSGi 번들로 나누지 말고 그냥 기존의 애플리케이션처럼 사용하는게 좋을 거 같습니다. 뭐 잠깐 서버좀 껐다 키죠 뭐.ㅎㅎ

다행히도 이 문제에 대한 해결책이 나왔고, 그걸 구현한 예제까지도 제공하고 있습니다.
http://notehive.com/wp/2008/07/23/osgi-hibernate-spring-dm-sample/
전 이런 개발자가 정말 멋져보입니다. 이력서를 보니까, 88/89년에 인턴쉽 하고, 92년도부터 계속 개발을 해온 사람이네요. 그럼.. 지금.. 16년째.. 캬;;;; 장난 아니셤!!!
신고
top


Table per connrete class with implicit polymorphism

Hibernate/Chapter 5 : 2008.02.01 21:37


특징

  • 모든 구현 클래스 하나 당 하나의 테이블로 표현한다.
  • 상속 받은 속성들도 테이블의 컬럼으로 맵핑한다.

문제점

  • 다형적인 관계를 잘 지원하지 못해. 왜? 상위 클래스와 관계를 맺고 있는 클래스가 하위 클래스를 참조해야 한다면, 이 클래스가 가져야 할 하위 클래스의 외례키는 두 개의 하위 클래스 모두의 주키를 대변해야 할 텐데, 이런게 테이블에서는 불가능하다.
  • 다형적인 쿼리(특정 상위 클래스 타입에 해당하는 모든 객체를 요구하는 쿼리)도 역시 문제가 된다. 왜? 각각의 하위 클래스마다 쿼리를 수행해서 데이터를 가져와야 한다.
  • 약간 다른 여러 테이블이 같은 정보를 공유하고 있다. 이게 왜 문제가 될까? 스키마를 점점 복잡하게 만든다. 예를들어, 상위 타입의 속성 하나를 변경하면 그것을 상속하나 모든 테이블을 수정해야 한다. 모든 하위 클래스에 무결성 제약을 적용하기도 힘들다.
  • 변경될 가능성이 거의 없고, 다형성이 별로 필요 없는, 최상위 레벨의 상속구조에서 사용하기를 권장한다.
  • JPA는 다형적인 쿼리를 지원하지 않는다. 하지만 Hibernate의 쿼리 인터페이스는 지원한다.

해결책

  • SQL의 UNION 함수를 사용해서 위에서 발생한 다형적인 쿼리와 관계에 대한 이슈 대부분을 제거할 수 있다.

맵핑하기

  • 하위 클래스는 @Entity로 맵핑하기
@Entity
@AttributeOverride(name = "owner", column = @Column(name = "CC_OWNER", nullable = false))
public class CreditCard extends BillingDetails {

@Id
@GeneratedValue
@Column(name = "CREDIT_CARD_ID")
private Long id = null;

@Column(name = "NUMBER", nullable = false)
private String number;
  • 상위 클래스는 @MappedSuperclass로 맵핑하기
@MappedSuperclass
public class BillingDetails {

@Column(name = "OWNER", nullable = false)
private String owner;
신고

'Hibernate > Chapter 5' 카테고리의 다른 글

CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
Table per class hierarchy  (0) 2008.02.01
Table per concrete class  (0) 2008.02.01
Table per connrete class with implicit polymorphism  (0) 2008.02.01
top


Transparent and automated persistence

Hibernate/Chapter 3 : 2008.01.09 16:31


Transparent

  • 도메인 모델의 영속성 클래스와 영속성 로직의 완전한 분리. transparent to mean a complete separation of concerns between the persistent classes of the domain model and the persistence logic, where the persistent classes are unaware of—and have no dependency on—the persistence mechanism.
  • Transparent persistence improves code readability and maintenance, as you'll soon see.
  • Testability is a basic requirement for applications with rich domain models
  • Transparent persistence fosters a degree of portability

Automatic

  • 자주 사용하는 SQL과 JDBC API사용으로부터 사용자를 해방시켜준다는 것을 의미한다.

하이버네이트의 투명성

  • 도메인 클래스가 별도의 인터페이스를 구현하거나 상위 클래스를 상속 받지 않아도 된다.
  • 도메인 클래스를 영속성 문맥 밖에서도 사용 할 수 있다.
  • objects aren't aware of the underlying data store.
참조 : Java Persistence With Hibernate
신고
top

TAG hibernate

관심사 분리Seperation Of Concern

Hibernate/Chapter 3 : 2008.01.09 16:30


도메인에서 분리되어야 하는 이유

  • The domain model implementation is usually a central, organizing component; it's reused heavily whenever you implement new application functionality. For this reason, you should be prepared to go to some lengths to ensure that concerns other than business aspects don't leak into the domain model implementation.

관심사 누수leakage of concern

  • You shouldn't put code that addresses these crosscutting concerns in the classes that implement the domain model. When these concerns start to appear in the domain model classes, this is an example of leakage of concerns.
  • EJB 컨테이너가 interseption을 사용해서 몇몇 leakage of concern을 해결했지만, EJB 2.1에서 도메인에 많은 규칙과 제약을 부과하는 바람에 관심사 누수가 생겼다. 이 경우에는 컨테이너 구현 관심사가 누수 됐다. EJB 3.0은 이것을 반영해서 다시 비침략적nonintrusive이고 훨씬 전통적인 JavaBean 프로그래밍 모델에 가까워졌다.

하이버네이트의 관심사

  • Hibernate is a solution for just one of these concerns: persistence.
  • Hibernate persistent classes and the EJB 3.0 entity programming model offer transparent persistence. Hibernate and Java Persistence also provide automatic persistence.
참조 : Java Persistence With Hibernate
신고
top

TAG hibernate

잘못 된 openSession() 사용 예제ㅋ

Hibernate/study : 2007.05.04 20:55


이전 글에서 DAO 구현을 다음과 같이 했었습니다.
public class MemberDaoImpleWIthSpringTransaction implements MemberDao{

    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public void add(Member member) {
        Session session = sessionFactory.getCurrentSession();
        session.save(member);
    }
}

여기서 빨간 색 부분은 찬욱군이 알려줬기 때문이고 원래는 아래처럼 코딩했었습니다.
    public void add(Member member) {
        Session session = sessionFactory.openSession(); //(1)
        session.save(member);
        session.flush(); //(2)
        session.close(); //(3)
    }

위에 표시한 빨간 부분이 모두 잘못된 부분이였습니다.

(1) 오픈 세션은 새로운 세션을 만들게 되고 그럼 새로운 트랜잭션에서 save를 실행하게 됩니다. 그런데 지금 저 코드는 새로운 트랜잭션이 아니라 Service Layer에서 사용하던 트랜잭션을 사용해야 합니다. 그러려면 새로운 세션이 아닌 getCurrentSession을 사용하면 기존에 존재하는 트랜잭션을 사용하게 됩니다.

(2) flush()는 무조건 DB에 저장하게 된다고 합니다. 따라서 삭제해야합니다. 지금 이 코드는 트랜잭션 내에 있고 예외가 발생하면 롤백해야 되니까 무조건 DB에 넣어버리는 flush()를 사용하면 안되겠습니다.

(3) getCurrentSession은 자기가 알아서 세션을 닫기 때문에 명시적으로 session을 닫아버리면 안됩니다.(에러가 나더군요.)

찬욱군 베리베리 땡큐 감사!!
신고
top


12.2.8. Transaction management strategies

Spring/Chapter 12 : 2007.04.27 09:26


TransactionTemplate 나 TransactionInterceptor 를 사용하든 둘 다 PlatformTransactionManager 객체를 사용하고 있습니다.
Hibernate를 위해 사용할 수 있는 TransactionManager로는 두 개가 있습니다.

1. HibernateTransactionManager :: for a single Hibernate SessionFactory, using a ThreadLocal  Session under the hood
2. JtaTransactionManager :: delegating to the JTA subsystem of the container
물론 직접 PlatformTransactionManager 인터페이스를 구현해서 사용해도 됩니다.

따라서 단일 트랜잭션 리소스를 사용하다가 분산된 트랜잭션 처리가 필요할 때는 간단히 JtaTransactionManager를 사용하도록 수정하면 됩니다.

아래의 코드는 여러개의 LocalSessionFactoryBean 를 사용하여 Oracle과 MySQL JDBC를 사용하는 JtaTransactionManager 설정을 보여줍니다.

<beans>

  <bean id="myDataSource1" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName value="java:comp/env/jdbc/myds1"/>
  </bean>

  <bean id="myDataSource2" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/myds2"/>
  </bean>

  <bean id="mySessionFactory1" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="myDataSource1"/>
    <property name="mappingResources">
      <list>
        <value>product.hbm.xml</value>
      </list>
    </property>
    <property name="hibernateProperties">
      <value>
        hibernate.dialect=org.hibernate.dialect.MySQLDialect
        hibernate.show_sql=true
      </value>
    </property>
  </bean>

  <bean id="mySessionFactory2" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="myDataSource2"/>
    <property name="mappingResources">
      <list>
        <value>inventory.hbm.xml</value>
      </list>
    </property>
    <property name="hibernateProperties">
      <value>
        hibernate.dialect=org.hibernate.dialect.OracleDialect
      </value>
    </property>
  </bean>

  <bean id="myTxManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

  <bean id="myProductDao" class="product.ProductDaoImpl">
    <property name="sessionFactory" ref="mySessionFactory1"/>
  </bean>

  <bean id="myInventoryDao" class="product.InventoryDaoImpl">
    <property name="sessionFactory" ref="mySessionFactory2"/>
  </bean>

  <!-- this shows the Spring 1.x style of declarative transaction configuration -->
  <!-- it is totally supported, 100% legal in Spring 2.x, but see also above for the sleeker, Spring 2.0 style -->
  <bean id="myProductService"
      class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager" ref="myTxManager"/>
    <property name="target">
      <bean class="product.ProductServiceImpl">
        <property name="productDao" ref="myProductDao"/>
        <property name="inventoryDao" ref="myInventoryDao"/>
      </bean>
    </property>
    <property name="transactionAttributes">
      <props>
        <prop key="increasePrice*">PROPAGATION_REQUIRED</prop>
        <prop key="someOtherBusinessMethod">PROPAGATION_REQUIRES_NEW</prop>
        <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
      </props>
    </property>
  </bean>

</beans>

JTA가 JNDI에 종속적이기 때문에 JNDI를 사용하여 datasource를 가져오는 모습입니다. 단일 리소스를 사용할 경우에는 HibernateTracsactionManager를 사용하는 것이 좋으며 이 때는 datasource를 가져 올때 걍 평범하게 가져오면 됩니다.


신고
top


12.2.7. Declarative transaction demarcation

Spring/Chapter 12 : 2007.04.25 13:00


이번엔 선언적인 트랜잭션 경계 설정에 관해 살펴보겠습니다. Spring AOP를 사용하는 방법으로 이전에는 Service Layer에 트랜잭션과 관련된 코드가 들어있었다면 Service Layer이 오직 자신의 본연의 임무에만 충실할 수 있도록 코드가 다음과 같이 바뛰게 됩니다.
public class ProductServiceImpl implements ProductService {

private ProductDao productDao;

public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}

// notice the absence of transaction demarcation code in this method
// Spring's declarative transaction infrastructure will be demarcating transactions on your behalf
public void increasePriceOfAllProductsInCategory(final String category) {
List productsToChange = this.productDao.loadProductsByCategory(category);
// ...
}
}
이전 글에서의 빨간색 글자 부분이 모두 사라졌으며 TransactionTemplate 변수 역시 필요없게 되었습니다. 이 말은 Spring 관련 API가 코드에서 사라졌다는 것이죠. 다시 또 말하면  Spring의 non-invasive 특징을 잘 나타내주고 있습니다.

Spring 1.2 방식의 AOP를 사용하여 트랜잭션 경계를 설정하는 방법은 다음과 같습니다.
<beans>

<bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>

<bean id="myProductService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="product.ProductService"/>
<property name="target">
<bean class="product.DefaultProductService">
<property name="productDao" ref="myProductDao"/>
</bean>
</property>
<property name="interceptorNames">
<list>
<value>myTxInterceptor</value> <!-- the transaction interceptor (configured elsewhere) -->
</list>
</property>
</bean>

</beans>
TransactionInterceptor 와 TransactionTemplate의 차이
* 발생 시키는 Exception 유형 :: Interceptor는 throwable 타입, Template은 transactionException 타입(Runtime Exception)
* 롤백 설정 단위 :: Interception 는 메소드마다 정책을 다르게 설정해 줄 수 있다. Template은 어플리케이션에서 사용하는 TransactionStatus 객체에 설정하기 땜시 단위가 다름.

위의 설정 내용을 Spring 2.0 방식으로 다음과 같이 간단하게 바꿀 수 있습니다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

<!-- SessionFactory, DataSource, etc. omitted -->

<bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>

<aop:config>
<aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>
</aop:config>

<tx:advice id="txAdvice" transaction-manager="myTxManager">
<tx:attributes>
<tx:method name="increasePrice*" propagation="REQUIRED"/>
<tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>

<bean id="myProductService" class="product.SimpleProductService">
<property name="productDao" ref="myProductDao"/>
</bean>

</beans>

신고
top


12.2.6. Programmatic transaction demarcation

Spring/Chapter 12 : 2007.04.25 12:41


프로그래밍적인 방법으로 트랜잭션 경계를 지정하는 방법은 다음과 같이 비즈니스 로직 부분에 PlatformTransactionManager 를 인스턴스로 등록하고 Setter Injection을 사용하는 것입니다.
<beans>

<bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>

<bean id="myProductService" class="product.ProductServiceImpl">
<property name="transactionManager" ref="myTxManager"/>
<property name="productDao" ref="myProductDao"/>
</bean>

</beans>
아래의 코드를 통해 위 설정 내용이 어떻게 적용 되는지 확인할 수 있습니다.
public class ProductServiceImpl implements ProductService {

private TransactionTemplate transactionTemplate;
private ProductDao productDao;

public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}

public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}

public void increasePriceOfAllProductsInCategory(final String category) {
this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {

public void doInTransactionWithoutResult(TransactionStatus status) {
List productsToChange = this.productDao.loadProductsByCategory(category);
// do the price increase...
}
}
);
}
}
TransactionTemplate 객체를 사용하여 콜백과 템플릿을 사용하여 원하는 코드를 트랜잭션 처리 하에 처리하게 됩니다.

단점은 트랜잭션 처리할 부분에 빨간색의 코드들이 추가적으로 중복되게 됩니다. 이런 중복은 AOP를 사용하여 따로 빼낼 수 있는데요. 그런 방법을 다음 챕터인 선언적인 트랜잭션 경계 선언에서 알아보겠습니다.
신고
top


12.2.5. Implementing DAOs based on plain Hibernate3 API

Spring/Chapter 12 : 2007.04.25 12:25


Hibernate 3.0.1 에서는 "Contexture Sessions"라고 부르는, 트랜잭션 당 하나의 current 세션을 사용하는 방법이 있습니다.
public class ProductDaoImpl implements ProductDao {

private SessionFactory sessionFactory;

public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}

public Collection loadProductsByCategory(String category) {
return this.sessionFactory.getCurrentSession()
.createQuery("from test.Product product where product.category=?")
.setParameter(0, category)
.list();
}
}
Spring 에서도 위와 같은 방법으로 코딩을 하면 트랜잭션 당 하나의 Hibernate Session객체를 사용하여 작업을 합니다.

위의 소스코드는 다음과 같은 DI가 필요합니다.
<beans>

<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>

</beans>
이런 스타일의 코딩의 장점은 Spring API에 종속 되지 않는 다는 것입니다. 오직 Hibernate API만 사용하고 있습니다.

단점은 HibernateException을 발생시키는데 이 것으로는 optimistic locking 이 실패 했다거나 하는 것을 알려주지 못합니다.

결론은 DAO를 구현할 때 Spring을 사용하여(HibernateTransactionManager) 트랜잭션 관리도 하고 DataAccessException을 사용할 수도 있지만 Hibernate3 API만 사용하여 구현할 수도 있습니다.
신고
top


레퍼런스 12장에 오타

Spring/Chapter 12 : 2007.04.24 13:14


문제가 발생한 코드는 Spring Reference에 있던 소스 코드입니다.
public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {

public Collection loadProductsByCategory(String category)
throws DataAccessException, MyException {

Session session = getSession(getSessionFactory(), false);
try {
List result = session.find(
"from test.Product product where product.category=?",
category, Hibernate.STRING);
if (result == null) {
throw new MyException("invalid search result");
}
return result;
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
}
}
위 코드에 잘못된 부분이 getSession 메소드 뿐이 아니였습니다. 바로 아래에 있는 session.find()도 문제가 있습니다. find() 메소드는 Hibernate의 org.hibernate.classic 패키지의 Session 인터페이스에 있습니다. 그러나 보통 사용하는 Session은 org.hibernate 패키지의 Session 인터페이스이였습니다. 그리고 그곳에는 find()라는 메소드가 없지요.

classic 패키지에 있는 Session이 상위 패키지에 있는 Session 인터페이스를 상속하고 있습니다. 하지만.. 모든 인터페이스가 deprecated 됐습니다. -_-;;
사용자 삽입 이미지

굳이 deprecated된 인터페이스를 사용(org.hibernate.classic.Sessoin 사용)하는 예제코드의 의도를 살리려면 다음과 같이 코드가 바껴야 합니다.

import org.hibernate.classic.Session;

        Session session = (Session) getSession(false);
        try {
            List result = session.find("from test.Product product where product.category=?", category, Hibernate.STRING);
            if (result == null) {
                throw new MyException("invalid search result");
            }
            return result;
        }

org.hibernate.Session을 사용하려면 다음과 같이 코드를 바꿔야 합니다.

        Session session = getSession(false);
        try {
            List result = session.createQuery("from test.Product product where product.category=?").setString(0, category).list();
            if (result == null) {
                throw new MyException("invalid search result");
            }
            return result;
        }

구형 인터페이스를 사용하고 캐스팅을 사용하느냐(처음 것) 아니면 신형 인터페이스를 사용하여 캐스팅도 없애느냐(뒤에 있는 것). 의 문제 인데요. 당연히 후자가 되어야 적당하다고 생각합니다.

전자에서 사용한 인터페이스는 Hibernate2 부터 deprecated 된거라고 신형 Session을 사용하라고 API에도 나와있습니다.
An extension of the Session API, including all deprecated methods from Hibernate2. This interface is provided to allow easier migration of existing applications. New code should use org.hibernate.Session.
용겐 횔러 횽하 신형 API 사용해서 예제 수정 해주세욜~
신고
top


12.2.4. Implementing Spring-based DAOs without callbacks

Spring/Chapter 12 : 2007.04.24 11:36


콜백 메소드를 사용하지 않고 HibernateDaoSupport로 부터 Session을 구해서 다음과 같이 코딩할 수 있습니다.

public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {

    public Collection loadProductsByCategory(String category)
            throws DataAccessException, MyException {

        Session session = getSession(getSessionFactory(), false);
        try {
            List result = session.find(
                "from test.Product product where product.category=?",
                category, Hibernate.STRING);
            if (result == null) {
                throw new MyException("invalid search result");
            }
            return result;
        }
        catch (HibernateException ex) {
            throw convertHibernateAccessException(ex);
        }
    }
}

이 때 유용하게 사용할 수 있는 메소드로는 역시 Session을 가져다 주는 getSession메소드 입니다.

희한하네요. getSession() 메소드의 인자가 두 개인 녀석이 안보이는데 레퍼런스에 있는 예제 코드가 잘못된 것 같습니다. 이 것도 이슈트랙에 올려야 겠군요. "용겐 휄러 횽하 예제 코드가 또 틀렸어효"

getSession(); getSession(boolean allowCreate); => HibernateDaoSupport 클래스에 있고
getSession(SessionFactory sessionFactory, boolean allowCreate); => SessionFactoryUtils 클래스에 있습니다.

어쨋든 allowCreate 속성은 Session을 만들 때 가져올 때 현재 트랜잭션에서 가져오느냐 아니면 새 트랜잭션에서 가져오느냐를 설정해 줍니다. true면 새거 false면 현재 트랜잭션을 사용하게 됩니다.

convertHibernateAccessException(ex); 메소드는 이름에서도 알 수 있듯이 Hibernate에서 발생하는 예외를 바꿔줍니다. unchecked Exception으로 바꿔주는 일을 하겠죠.

즉 하드코딩을 하더라도 Spring이 제공하는 예외 랩핑을 사용할 수 있지만 try-catch 보기도 싫고 굳이 예외 변경하는 코드를 매번 넣어야 하기 때문에 대부분의 경우 HibernateTemplate을 사용하는게 편할 것 같습니다.


신고
top


12.2.3. The HibernateTemplate

Spring/Chapter 12 : 2007.04.23 19:04


이 전 글에서 Spring Container에 등록한 SessionFactoryBean을 사용하여 HibernateTemplate을 생성할 수 있습니다.

따라서 HibernateTemplate을 사용할 DAO 클래스에 setter injection을 사용하기 위해 보통 다음과 같이 설정합니다.
<beans>

  <bean id="myProductDao" class="product.ProductDaoImpl">
    <property name="sessionFactory" ref="mySessionFactory"/>
  </bean>

</beans>

어플리케이션에서 사용할 때는 다음과 같이 콜백을 사용하여 session에 접근합니다.
public class ProductDaoImpl implements ProductDao {

    private HibernateTemplate hibernateTemplate;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }

    public Collection loadProductsByCategory(final String category) throws DataAccessException {
        return this.hibernateTemplate.execute(new HibernateCallback() {
            public Object doInHibernate(Session session) {
                Criteria criteria = session.createCriteria(Product.class);
                criteria.add(Expression.eq("category", category));
                criteria.setMaxResults(6);
                return criteria.list();
            }
        };
    }
}

HibernateTemplate이 Session의 생성이나 소멸, 트랜잭션을 책임집니다. 따라서 콜백을 사용하여 단순하게 할 일(find, select, insert 등) 만 코딩해 주면됩니다.

HibernateTemplate의 execute() 메소드 소스 코드를 보시면 어떤 일을 해주는지 짐작할 수 있습니다.

more..


한 단계 더 나가서 HibernateTemplate을 DAO 클래스 내부에 직접 멤버 변수로 선언하지 않고 DAO 클래스가 HibernateDaoSupport 클래스를 상속 받도록 하면 getHibernateTemplate() 메소드르 사용하여 원할 때 마다 HibernateTemplate을 사용할 수 있습니다.

public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {

    public Collection loadProductsByCategory(String category) throws DataAccessException {
        return this.getHibernateTemplate().find(
            "from test.Product product where product.category=?", category);
    }
}

이런식으로 사용하면 DAO마다 setter injection 해야하는 수고를 덜 수 있겠습니다.
신고
top


12.2.2. SessionFactory setup in a Spring container

Spring/Chapter 12 : 2007.04.23 10:56


Spring 의 Application Context에 SessionFactory를 bean으로 등록해 둡니다.

다음은 Reference에 있는 XML 기반의 Hibernate 설정 파일을 사용할 때 등록하는 방법입니다.
<beans>

  <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
    <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
    <property name="username" value="sa"/>
    <property name="password" value=""/>
  </bean>

  <bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="mappingResources">
      <list>
        <value>product.hbm.xml</value>
      </list>
    </property>
    <property name="hibernateProperties">
      <value>
        hibernate.dialect=org.hibernate.dialect.HSQLDialect
      </value>
    </property>
  </bean>

</beans>

다음은 찬욱이가 만들어 둔 어노테이션을 사용하는 Hibernate 설정을 사용했을 때의 SessionFactoryBean을 등록하는 예입니다. dataSource는 어딘가 다른 곳에 있습니다.
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="annotatedClasses">
            <list>
                <value>buyingBook.model.Member</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <!-- 지금 사용하고 있는 데이터베에스에 최적화 된 SQL을 생성하기 위해서
                      현재 데이터베이스에 해당하는 Hibernate dialect 클래스 명 -->
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>

                <!--  콘솔 창에 사용하는 모든 SQL문을 출력할지를 선택  -->
                <prop key="hibernate.show_sql">true</prop>

                <!-- DB Schema에 변경사항이 발생한 경우 자동으로 수정  -->
                <prop key="hibernate.hbm2ddl.auto">update</prop>

                <!-- 성능 튜닝에 도움이 되는 유용한 통계를 제공  -->
                <prop key=" hibernate.generate_statistics">true</prop>

                <!-- 자동 커밋이 되는 것을 방지 -->
                <prop key="hibernate.connection.autocommit">false</prop>

                <!-- "Current" Session의 Scope 설정.<jta/thread/managed/custom> -->
                <prop key="hibernate.current_session_context_class">thread</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

어플리케이션에서 "sessionFactory"로 빈을 룩업하면 SessionFactory type의 객체를 반환합니다. 빨간색 클래스들의 상위 클래스인 AbstractSessionFactoryBean 클래스의 코드 일부를 참조하면 알 수 있습니다.
사용자 삽입 이미지

만약에 사용하는 ORM이 바뀌면 여기서 설정파일만 바꿔주면 됩니다. 예를 들어 JNDI에 있는 DataSource를 사용할 거라면 다음과 같이 바꿔줍니다.
<beans>

  <bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/myds"/>
  </bean>

</beans>

신고
top


12.2.1. Resource management

Spring/Chapter 12 : 2007.04.23 00:46


자원을 관리하는 코드가 보통 사방에 흩어져 있게 됩니다. 하지만 스프링에서는 이런 자원 관리간단하면서도 강력한 방법인 tamplate을 사용한 IoC를 이용해서 관리합니다. 자원을 관리하는 기능과 SQLException을 보다 구체적이고 un-checked Exception인 DataAccessException으로 래핑해줍니다.

HibernateTemplate, HibernateInterceptor, HibernateTransactionManager 이런 클래스들을 제공하는 가장 주요한 목적
1. 어플리케이션에서 Data Access와 Transaction 기술을 깔끔하게 레이어링
2. 어플리케이션 객체들 간의 Loose Coupling

따라서...
1. no more business service dependencies on the data access or transaction strategy
2. no more hard-coded resource lookups
3. no more hard-to-replace singletons
4. no more custom service registries.


신고
top


Spring + Hibernate 프로젝트 베이스

모하니?/Coding : 2007.03.16 21:26


사용자 삽입 이미지
Spring 과 Hibernate 로 모델 부분을 구현하여 jar로 묶어서 나중에 war에 넣을 부분을 코딩할 프로젝트 베이스 입니다.

어노테이션 기반으로 퍼시스턴스를 표현할 것입니다.
public class MemberTest {
    @Test public void persistence(){
        ApplicationContext ac = new ClassPathXmlApplicationContext(
                new String[] { "applicationContext-dao.xml",
                        "applicationContext-jdbc-datasource.xml" });
        SessionFactory sf = (SessionFactory) ac.getBean("sessionFactory");

        Session s = sf.openSession();
        Transaction tx = s.beginTransaction();

        Member keesun = new Member();
        keesun.setName("기선");
        keesun.setAccount("whiteship");
        keesun.setPassword("pass");

        s.save(keesun);
        tx.commit();
        s.close();

        assertNotNull(keesun.getId());
    }
}

위와 같은 예제 코드가 하나 들어있으며 DB 설정은 database.properties 에서 적절하게 바꿔주시면 됩니다. hibernate.dialect 에 넣어야 할 값은 여기를 참조해서 사용하시는 DB에 맞게 바꿔주시면 됩니다.


나중에 war 프로젝트 베이스도 만들면 올려둬야쥐~
신고

'모하니? > Coding' 카테고리의 다른 글

까다로운 녀석..ㅠ.ㅠ  (0) 2007.03.27
Report Validator v0.9  (2) 2007.03.26
Report Validator v0.5  (0) 2007.03.26
아 답답해;;  (2) 2007.03.26
테스트를 꼭 실행 해야 하는 시점  (0) 2007.03.23
Spring + Hibernate 프로젝트 베이스  (0) 2007.03.16
JDBC Connection Properties  (0) 2007.03.16
만들고 싶은게 너무 많아요.  (6) 2007.03.09
레포트 검사기  (2) 2007.03.09
랜덤 리스트 축출기 v1.0  (0) 2007.03.08
Tdd Helper 사용 중  (0) 2007.03.07
top


JUnit 으로 Hibernate 테스트

Hibernate/study : 2007.01.01 19:30


지난번에는 PizzaApp 클래스에서 새로운 피자 객체를 만들고 save()를 사용해서 DB에 insert를 했었습니다.

이번에는 PizzaTest 클래스를 만들고 JUnit을 사용해 봅니다. save()를 사용해서 DB에 피자 한판을 넣어보고 들어갔는지 빼내어 보고 빼낸 것이 null이 아닌지 확인해 봅니다.

연습용이기 때문에 test가 별로 맘에 안드실 수도 있습니다. 저도 별로 테스트가 맘에 들지 않지만 DB에서 데이타를 꺼내오고 JUnit 형태로 만들어 보는것에 목적을 뒀습니다.

public class PizzaTest {

 

       private Session s;

 

       private Transaction tx;

 

       @Before

       public void createSession() {

             ApplicationContext ac = new ClassPathXmlApplicationContext(

                           new String[] { "applicationContext-dao.xml",

                                        "applicationContext-jdbc-datasource.xml" });

             SessionFactory sf = (SessionFactory) ac.getBean("sessionFactory");

 

             s = sf.openSession();

             tx = s.beginTransaction();

       }

 

       @Test

       public void createPizza() {

             Pizza p = new Pizza();

             p.setName("keesun's 2007 pizza");

             p.setPrice(2007);

             p.setToping("happiness");

             p.setSize("2007cm");

 

             s.save(p);

 

             List pizzaList =  s.createQuery("from O_Pizza").list();

             assertNotNull(pizzaList);

       }

 

       @After

       public void close() {

             tx.commit();

             s.close();

       }

}

오.. 잘 돌아갑니다. O_Pizza는 실제 table에는 o_pizza 이렇게 소문자로 들어가 있습니다. postgres 특징인것 같습니다. 하지만 프로그램에서도 o_pizza 이렇게 쓰면 다음과 같은 에러를 볼 수 있습니다.

more..

DB의 table이름을 쓰지 말고 @Entity의 name속성에 지정한 이름으로 사용해야 합니다.


 

신고

'Hibernate > study' 카테고리의 다른 글

DbUnit  (2) 2007.01.23
Hibernate에서 쿼리 날리는 방법  (0) 2007.01.19
객체들의 상태 변화(in Hibernate)  (2) 2007.01.19
숙제  (0) 2007.01.19
Transaction  (2) 2007.01.02
JUnit 으로 Hibernate 테스트  (0) 2007.01.01
Detached Objects  (0) 2007.01.01
Object identity  (2) 2007.01.01
Persistent Object  (0) 2007.01.01
Study To Do List  (2) 2006.12.29
Transient Object  (0) 2006.12.28
top


1-6. 모델 클래스 수정하기



Pizza 클래스에 새로운 필드를 추가해 봅니다.

toping이라는 멤버를 추가하고 역시 어노테이션을 사용하여 @Column으로 지정해 줍니다. 그리고 getter, setter를 만들어 준뒤 PizzaApp 클래스에 적당한 값들로 세팅해 주고 save를 해봅시다.

more..


그리고 PizzaApp를 실행 시키면 다음과 같이 새로운 필드가 생기고 기존에 있던 data들은 새로운 필드에 null값이 들어간 상태로 바뀝니다.

Hibernate: select nextval ('Pizza_PizzaId_Seq')
Hibernate: insert into O_Pizza (toping, price, name, size, pizzaId) values (?, ?, ?, ?, ?)

사용자 삽입 이미지

여기서 궁금한건..만약에 새로 만든 필드의 Column 속성에서 nullable을 false로 하면 어떻게 될까요? 해보면 되겠죠?

pgAdmin에서 table을 전부 날려버리고 Pizza 클래스에 추가했던 toping 멤버를 삭제하고 게터와 세터로 삭제합니다. 그리고 name, size, price만 넣어 줍니다.
사용자 삽입 이미지

이 상태에서 이제 다시 Pizza 클래스에 Toping을 추가하고 이번에는 @Column(nullable=false)로 설정을 해줍니다. 그리고 PizzaApp에서는 새로운 레코드를 추가합니다.

more..

그리고 실행을 하면 다음과 같은 메시지가 콘솔창에 출력됩니다.

Hibernate: select nextval ('Pizza_PizzaId_Seq')
Hibernate: insert into O_Pizza (toping, price, name, size, pizzaId) values (?, ?, ?, ?, ?)

엇... DB로 확인해 봤더니 toping이라는 컬럼이 추가 되었고 새로운 레코드도 입력 되었습니다. 하지만 기존에 있던 레코드는 새로 생긴 컬럼이 null로 되어있습니다.

사용자 삽입 이미지

오호~~~table을 생성하는 쿼리를 살펴봤더니 toping 컬럼을 만들 때 not null을 넣지 않았군요. Hibernate 가 마치 '이정도 쯤이야' 하며 으쓱해 하는 기분이군요. 새로 추가되는 필드는 nullable=false로 하더라도 무시 됩니다. 그리고 그렇게 처리하는 것이 논리적인 것 같습니다.

사용자 삽입 이미지

신고
top


1-4. 모델 만들기



src에 model패키지를 추가하고 간단한 POJO 클래스를 만들어 봅니다.

현재 살짝 졸린데 배까지 고픈 관계로 Pizza 클래스를 만들기로 하겠습니다. 속성으로는 고유한 값에 해당하는 pizzaId, size, name, price 이렇게 네가지가 있습니다.

more..


매우 단순한 클래스가 만들어졌습니다. 이제 이 클래스에 어노테이션을 적용합니다. 어노테이션을 적용하게 되면 테이블과 매핑이 이뤄집니다.

사용자 삽입 이미지
이때 주의 할 점은 방언에 해당하는 org.hibernate.annotations에 있는 어노테이션 말고 차후 확장성을 고려하여 javax.persistence에 있는 어노테이션을 사용하기 입니다.

more..


각 어노테이션에 지정할 수 있는 속성이 보고 싶을 땐 이클립스에서 원하는 어노테이션에서 f4를 누르면 왼쪽 hierarchy 창에서 보입니다.

여기서 사용한 어노테이션으로는 Entity, Id, Column, SequenceGenerator, GeneratedValue입니다. 각각에서 사용가능한 속성들에 대한 공부도 필요할 것 같습니다.
신고
top


1-2. 필요한 라이브러리들 추가



먼저 Eclise에서 프로젝트를 생성합니다. 저는 study로 생성하고 소스폴더는 src와 test를 만들겠습니다. 그리고 필요한 라이브러리 파일들을 모아두기 위해 lib라는 폴더를 만들겠습니다.

물개 선생님께서 앞으로 사용하게 될 라이브러리 목록들을 정리해 주셨습니다.
- hibernate-3.2 / hibernate3.jar
- hibernate-3.2 / lib / ant-antlr-1.6.5.jar
- hibernate-3.2 / lib / antlr-2.7.6.jar
- hibernate-3.2 / lib / c3p0-0.9.0.jar
- hibernate-annotations-3.2.1.GA / hibernate-annotations.jar
- spring-framework-2.0.1 / dist / spring.jar
- spring-framework-2.0.1 / dist / modules / spring-hibernate3.jar
- spring-framework-2.0.1 / lib / cglib / cglib-nodep-2.1_3.jar
- spring-framework-2.0.1 / lib / jakarta-commons / commons-collections.jar
- spring-framework-2.0.1 / lib / jakarta-commons / commons-logging.jar
- spring-framework-2.0.1 / lib / dom4j / dom4j-1.6.1.jar
- spring-framework-2.0.1 / lib / jpa / persistenec.jar (JPA)
- spring-framework-2.0.1 / lib / ehcache / ehcache-1.2.3.jar
- spring-framework-2.0.1 / lib / j2ee / jta.jar
- spring-framework-2.0.1 / lib /log4j / log4j-1.2.14.jar
- junit4.1 / junit-4.1.jar
C:/Program Files/PostgreSQL/8.1/jdbc/postgresql-8.1-405.jdbc3.jar
각각의 jar파일들이 어떤 곳에 필요한지는 공부가 필요한 상태입니다. 위 jar파일들을 찾아서 lib폴더로 복사줍니다.

프로젝트에서 lib폴더 안에 있는 jar파일을 모두 선택해 주고 오른쪽 마우스를 클릭하여 Add to BuildPath를 클릭하여 빌드 경로에 추가해 줍니다.

사용자 삽입 이미지

신고
top


1-1. 프로젝트 관련 라이브러리 다운로드



OpenSeed Forum 인덱스OpenSeed Forum 인덱스OpenSeed Forum 인덱스OpenSeed Forum 인덱스OpenSeed Forum 인덱스
http://www.openseed.net/forum/ 이곳에서 만난 teacher.seal께 배운 강좌에 대한 복습이 시작 됩니다.

Hibernate와 Spring을 사용하여 간단한 프로젝트를 구성할 때 필요한 파일들을 다운로드 합니다.

먼저 Hibernate관련 소스는 http://www.hibernate.org/6.html 이곳에서 Hibernate Core와 Hibernate Annotations를 다운로드합니다.
사용자 삽입 이미지

Spring 소스는 http://www.springframework.org/download 이곳에서 2.0.1버젼의 download를 클릭한 뒤 with-dependencies를 다운로드 합니다.
사용자 삽입 이미지
거의 60MB 정도 합니다.[각주:1] 셋 중에서 덩치가 가장 크군요.

마지막으로 JUnit은 http://www.junit.org/index.htm 이곳의 상단 왼쪽에 download 메뉴를 클릭하면 자동으로 최신 버젼 다운로드를 할 수 있습니다.
사용자 삽입 이미지

아! 그리고 한가지 더! 사용할 DB에 따른 JDBC jar파일이 필요합니다. 물개 선생님과 예제 실습을 Postgres로 진행하기로 했습니다. 따라서 postgres로 다운 받아서 설치하셔야합니다.
http://www.postgresql.org/ftp/pgadmin3/release/v1.6.1/win32/ 이곳에서 다운로드를 하시고 설치를 하시면 됩니다.

Postgres를 제외한 다른 파일들은 D:\study\openSources 폴더 안에 모아두고 전부 압축을 풀었습니다.

사용자 삽입 이미지

일단 이것으로 준비물은 다 챙긴것 같습니다. :) 집에간 뒤에 계속하겠습니다.

신고
top







티스토리 툴바