PK 두개 이상시 매핑

Posted at 2009. 12. 24. 14:04 | Posted in Framework/Hibernate
반응형


위와 같은 PK(Primary Key)가 두개인 테이블이 있습니다.

이 테이블을 매핑시켜봅시다.

PK가 하나일때는 <id> 태그를 써서 쓰면 되지만, 두개일때는 <composite-id> 태그를 사용합니다.

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="model">
	<class name="Code" table="common_code">

		<composite-id>
			<!-- PK1 -->
			<key-property name="code" type="string">
				<column name="code" length="10">
					<comment>코드</comment>
				</column>
			</key-property>
			<!-- PK2 -->
			<key-property name="lang" type="string">
				<column name="language" length="3">
					<comment>언어</comment>
				</column>
			</key-property>
		</composite-id>
			
		<property name="codeDesc" type="text">
			<column name="code_desc" not-null="false">
				<comment>코드 설명</comment>
			</column>
		</property>

	</class>
</hibernate-mapping>

그리고 클래스 파일은 Serializable 인터페이스를 구현해야 한답니다....

package model;

import java.io.Serializable;

public class Code implements Serializable {

    private static final long serialVersionUID = -14882784587978705L;
 
    private String code; // PK1
    private String lang; // PK2
    private String codeDesc;

    // getter, setter
}

반응형

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

Reverse Engineering  (0) 2009.12.24
Hibernate 시작하기  (0) 2009.12.23
Criteria 사용하여 질의 하기 #2  (1) 2009.10.03
Criteria 사용하여 질의 하기 #1  (4) 2009.09.10
다대다(n:m) 관계 설정하기  (0) 2009.09.02
일대다(1:n) 관계 설정하기  (1) 2009.08.31
일대일(1:1) 관계 설정하기  (0) 2009.08.27
하이버네이트(Hibernate) 사용하기  (6) 2009.08.24
//

Reverse Engineering

Posted at 2009. 12. 24. 09:57 | Posted in Framework/Hibernate
반응형


이미 만들어져 있는 데이터베이스의 테이블을 이용해 java 파일과 매핑 xml 파일을 생성시켜봅시다~



일단 http://antop.tistory.com/53 에서 아래 3단계까지는 하셔야 합니다 ㅎㅎ

- 하이버네이트 플러그인 설치
- 하이버네이트 라이브러리 세팅
- 하이버네이트 기본 설정파일 작성

설정파일(hibernate.cfg.xml)까지 만들었으면

File - New - Other... - Hibernate - Hibernate Console Configuration - Next




하이버네이트 콘솔 정보는 hibernate.cfg.xml 설정 파일을 사용합니다.




하이버네이트 콘솔이 뭐냐... 하면 하이버네이트 플러그인을 설치하면 볼 수 있는 Hibernate Perspective 에서 사용하는 데이터베이스 접속 정보 정도로 보면 되겠습니다.

난중에 여기서 HQL 쿼리도 날려보고 여러가지를 할 수 있습니다.


이제 이 콘솔 정보를 기반으로 Reverse Engineering(이하 리버스) 파일을 생성 합니다.

File - New - Other... - Hibernate - Hibernate Reverse Engineering File (reveng.xml) - Next




리버스 파일을 저장 할 폴더 지정 - Next




인제 여기서 console configuration 에서 셀렉트박스를 눌러보면 방금전에 만든 콘솔 정보가 있을 겁니다.

콘솔정보 선택 후 Refresh 클릭하면 DB에 있는 테이블 목록을 받아옵니다.

리버스하고싶은 테이블을 선택 후 Finish




그럼 hibernate.reveng.xml 파일이 열리는데 걍 닫기.


이제 Hibernate Perspective 로 이동합니다.

아래 그림 처럼 [그.. 아이콘] 클릭 후 -_-... "Hibernate Code Generation Configurations..." 클릭




아무것도 없습니다.... "새로 만들기" 버튼 클릭




적당히 이름 정하고, 콘솔정보 선택하시고, 만들어지는 파일들을 저장할 폴더 위치를 지정합니다.

※ 아래 그림의 Name 처럼 하면 이미 있는 이름이라고 안됩니다. 딴거 쓰세요 ㅠ_ㅠ




src 폴더(최상위)를 선택합니다.




package 경로를 지정합니다.

※ 패키지를 지정하면 패키지 폴더도 생성 됩니다.

이제 리버스 엔진 파일 아까 만들었던걸 지정해줍시다.




새로 만들기를 원하냐고 물어보는데 전 이미 만들었으니 "Use existing..." 클릭




파일을 선택합니다.




이제 Exporters 탭으로 이동해서 어떤 파일을 생성할지 선택해줍니다.




Run 클릭...



신나게 파일을 생성하고 있습니다.




파일이 생성되었습니다.

뭐 에러나는 것들도 있네요.. 컴퓨터가 만능은 아니자나여!


반응형
//

Hibernate 시작하기

Posted at 2009. 12. 23. 15:17 | Posted in Framework/Hibernate
반응형
하이버네이트를 시작하기 위한 기본설정을 해봅시다.

Eclipse Galileo
Java 1.6.0_16
Tomcat 6.0.14



http://www.hibernate.org 에서 라이브러리와 플러그인을 다운로드 받습니다.

왼쪽 메뉴에서 'Download' 를 클릭 후 zip 파일을 다운로드 합니다.






하이버네이트 플러그인(Hibernate Tools)은 이클립스 플러그인 설치를 이용해서 합니다.

업데이트 주소는 하이버네이트 홈페이지에서 HIBERNATE Tools 부분에 링크 되어있습니다.



업데이트 주소 : http://download.jboss.org/jbosstools/updates/stable



플러그인이 잘 설치 되었습니다... ㄷㄷ;





이제 하이버네이트를 하기위한 최소한의 라이브러리를 세팅합시다.

다운로드 받은 hibernate-distribution-3.3.2.GA-dist.zip 파일을 압축 해제합니다.

hibernate3.jar 와 lib\required 폴더 안의 *.jar 만 있으면 하이버네이트를 사용할 수 있습니다.

log4j 를 사용하기 위해선 slf4j-log4j12-1.5.10.jarlog4j-1.2.15.jar 가 필요합니다. (설정파일 log4j.properties !!!)

마지막으로 데이터베이스에 맞는 jdbc 드라이버가 필요합니다.





하이버네이트 기본 설정 파일인 hibernate.cfg.xml 을 작성해봅시다. 이클립스 플러그인으로 생성하겠습니다.

File - New - Other... - Hibernate - Hibernate Configuration File (cfg.xml) 선택 - Next




설정파일을 저장할 경로 지정




접속 정보를 입력합니다. - Finish

접속 정보는 http://antop.tistory.com/54 에 있는 샘플 DB 입니다.

※ Database dialect 를 DB 에 알맞게 잘 골라주세요.




src 경로 아래 hibernate.cfg.xml 파일이 생성 되었습니다.

※ 플러그인을 설치하면 하이버네이트 관련 xml 파일의 아이콘이 아래 그림과 같이 표시됩니다.



파일을 열어보면 기본적으로 데이터베이스 접속 정보가 설정되어있습니다.

코드 어시스트(Ctrl + Space)를 이용해(플러그인 있어야함) 다른 설정들도 할 수 있습니다.



하이버네이트 세션(JDBC의 Connection이라고 보면 됨)을 관리해주는 클래스를 사용하도록 합시다.

HibernateUtil 클래스입니다. 하이버네이트 공식 메뉴얼에서 구한 국민 세션 관리 유틸이죠 ㅎㅎ

package util;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
   public static final SessionFactory sessionFactory;

   static {
      try {
         // Create the SessionFactory from hibernate.cfg.xml
         sessionFactory = new Configuration().configure().buildSessionFactory();
      } catch (Throwable ex) {
         // Make sure you log the exception, as it might be swallowed
         System.err.println("Initial SessionFactory creation failed." + ex);
         throw new ExceptionInInitializerError(ex);
      }
   }

   public static final ThreadLocal<Session> session = new ThreadLocal<Session>();

   public static Session getCurrentSession() throws HibernateException {
      Session s = session.get();
      // Open a new Session, if this thread has none yet
      if (s == null) {
         s = sessionFactory.openSession();
         // Store it in the ThreadLocal variable
         session.set(s);
      }
      return s;
   }

   public static void closeSession() throws HibernateException {
      Session s = (Session) session.get();
      if (s != null)
         s.close();
      session.set(null);
   }
}


※ 여기까지 하셨으면 하이버네이트를 사용할 최소한의 준비는 끝났습니다.



간단한 테스트 파일을 하나 만들어서 접속 테스트 해봅시다.

다른 이무 작동 없고 접속만 했다가 끊는 소스 입니다.

HibernateUtil 을 이용해서 접속합니다.

package app;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;

import util.HibernateUtil;

public class Test {
   
   public static void main(String[] args) {

      Session sess = null;
      Transaction tx = null;
  
      try {
         // 세션 열기
         sess = HibernateUtil.getCurrentSession();
         // 트랜잭션 시작
         tx = sess.beginTransaction();
         
         // processing..
   
         tx.commit(); // 커밋
      } catch (HibernateException e) {
         tx.rollback(); // 롤백
         e.printStackTrace();
      } finally {
         // 세션 닫기
         HibernateUtil.closeSession();
      }
    
   }
}

에러 안뿜어내고 로그 나오면 잘 된겁니다. ㄷㄷ;;


반응형
//

Criteria 사용하여 질의 하기 #2

Posted at 2009. 10. 3. 10:03 | Posted in Framework/Hibernate
반응형
JOIN

http://docs.jboss.org/hibernate/stable/core/api/org/hibernate/Criteria.html





부서와 사원의 1:n 관계에 JOIN 쿼리를 날려봅시다.

원래 하이버네이트에서는 매핑을 잘 맺어주면, 단순 getter 로도 관계를 가지는 테이블의 내용을 가져올 수 있습니다.

Criteria crit = sess.createCriteria(Emp.class);

List<Emp> emps = crit.list();
   
int size = emps.size();
for(int i=0 ; i < size ; i++) {
   Emp emp = emps.get(i);
   Dept dept = emp.getDept();

   String empName = emp.getName();
   String deptName = dept != null ? dept.getDeptName() : "";
    
   System.out.println("row: " + (i+1));
   System.out.println(emp);
   System.out.println(dept);
}

이렇게하면 사원에 해당하는 부서의 정보를 가져올 수 있습니다.

로그를 보면 select문이 여러번 출력되는 것을 볼 수 있습니다.

List<Emp> emps = crit.list(); 에서 ' select empno, ename, job, hire_date from tbl_emp; ' 쿼리가 실행되고,

dept.getDeptName(); 에서 사원의 부서번호에 따라 ' select deptno, dname, loc from tbl_dept; ' 쿼리가 실행 됩니다.

부서의 종류가 엄청나게 많다면 두번째 select 쿼리가 많이 실행 되게 될 것입니다.

쿼리를 할때 최소한의 I/O로 실행되게 하고, 뭐.. 서브쿼리보다는 조인이 빠르고 흘러가면서 들은 것들이 있기 땜시... JOIN ㄱㄱ



INNER JOIN

키를 기준으로 일치하는 데이터만 가져옵니다. (다 아시졍? +_+)

// select * from tbl_emp e
Criteria crit = sess.createCriteria(Emp.class, "e");
// join tbl_dept d on this.deptno = d.deptno
crit.createCriteria("dept", "d");
   
// Result 를 Map 형태로 변환
crit.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);

List<Map<String, Object>> joinList = crit.list();

int size = joinList.size();
for(int i=0 ; i < size ; i++) {
   Map<String, Object> map = joinList.get(i);
   Emp emp = (Emp) map.get("e");
   Dept dept = (Dept) map.get("d");

   System.out.println("row: " + (i+1));
   System.out.println(emp);
   System.out.println(dept);
}


Criteria crit = sess.createCriteria(Emp.class);

Session.createCriteria(클리스명) 를 통해서 생성한 Critereria 객체에 다시 createCriteria(String associationPath, String alias) 를 사용해서 JOIN 을 하면 됩니다.

이렇게 JOIN 한 결과는 Emp 객체도 Dept 객체도 아니기 때문에, Criteria의 setResultTransformer 메소드를 사용하여 Criteria를 이용한 조회 결과를 별도 정의한 객체 형태나 Map 형태로로 전달받아야 하겠습니다.

근데 전 객체로 변형하는 법은 잘 안되서... 걍 Map 으로 ㄱㄱㄱ!

여기서 두번째 인자로 넣는 엘리어스명은 쿼리에서 쓰는 엘리어스명도 되지만 나중에 Map 에서 꺼 낼때의 키도 되기 때문에 꼭 지정해줘야 할 것입니다....

실행 되는 쿼리는 아래와 같습니다.

    select
        this_.empno as empno0_1_,
        this_.ename as ename0_1_,
        this_.job as job0_1_,
        this_.hire_date as hire4_0_1_,
        this_.deptno as deptno0_1_,
        d1_.deptno as deptno1_0_,
        d1_.dname as dname1_0_,
        d1_.loc as loc1_0_
    from
        tbl_emp this_
    inner join
        tbl_dept d1_
            on this_.deptno=d1_.deptno


이러면 당연히 부서가 없는 'TOM' 은 목록에 나오지가 않겠죠? 만약 사원 목록을 본다면 부서가 없는 사원이 나오면 안되겠죠?



LEFT JOIN

두개의 테이블을 조인할 때 왼쪽 테이블을 기분으로 오른쪽 테이블을 조인 시키는 LEFT JOIN 을 해봅시다...

createCriteria(String associationPath, String alias, int joinType) 를 사용하면 됩니다.

// select * from tbl_emp e
Criteria crit = sess.createCriteria(Emp.class, "e");
// join tbl_dept d on this.deptno = d.deptno
crit.createCriteria("dept", "d", Criteria.LEFT_JOIN);
   
// Result 를 Map 형태로 변환
crit.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);

List<Map<String, Object>> joinList = crit.list();

int size = joinList.size();
for(int i=0 ; i < size ; i++) {
   Map<String, Object> map = joinList.get(i);
   Emp emp = (Emp) map.get("e");
   Dept dept = (Dept) map.get("d");

   System.out.println("row: " + (i+1));
   System.out.println(emp);
   System.out.println(dept);
}

실행되는 쿼리는 아래와 같습니다. 엘리어스 사용에 주의 하세요~

    select
        this_.empno as empno0_1_,
        this_.ename as ename0_1_,
        this_.job as job0_1_,
        this_.hire_date as hire4_0_1_,
        this_.deptno as deptno0_1_,
        d1_.deptno as deptno1_0_,
        d1_.dname as dname1_0_,
        d1_.loc as loc1_0_
    from
        tbl_emp this_
    left outer join
        tbl_dept d1_
            on this_.deptno=d1_.deptno


※ RIGHT JOIN 은 없네요 테이블의 좌 우 를 바꾸셔요~ 히힛

간단하게 사원명과 부서명으로 조회하는 페이지 만들어 봤어요~








Pagination

많은 자료를 조회할 때 일정한 양의 자료를 한 페이지만 보여주도록 하는 기능입니다.

꼭 필요한 기능이고, 그다지 복잡할 것도 없지만, 막상 쿼리 날리려면 DBMS 마다 쿼리도 틀리고... 쉽지 안쵸 ㅠ_ㅠ

하지만 하이버네이트에서는

setFirstResult(int firstResult) 과 setMaxResults(int maxResults) 메소드 두개면 해결 됩니다! ㅋㅋㅋ

전부터 해오던 우편번호 테이블 가지고 해봅시다...


Criteria crit = sess.createCriteria(Zipcode.class);
crit.setFirstResult(40);   // 시작 로우 라인 번호(?)
crit.setMaxResults(10);    // 시작 로우 라인 번호로 부터 몇개?!

List<Zipcode> list = crit.list();
   
int size = list.size();
for(int i=0 ; i < size ; i++) {
   Zipcode z = list.get(i);
   System.out.println(z.toString());
}

한 페이지에 10개씩 보여준다고 하고 4페이지의 내용을 보여주게 됩니다. 


페이징 처리가 되있는 우편번호 검색 페이지를 만들어봤습니다.




몇개의 DMBS 에서 돌려보니까 아래와 같은 쿼리가 나오네요


MySQL 5.1.38
    select
        this_.seq as seq0_0_,
        this_.zipcode as zipcode0_0_,
        this_.sido as sido0_0_,
        this_.gugun as gugun0_0_,
        this_.dong as dong0_0_,
        this_.ri as ri0_0_,
        this_.bldg as bldg0_0_,
        this_.st_bunji as st8_0_0_,
        this_.ed_bunji as ed9_0_0_
    from
        tbl_zipcode this_ limit ?,
        ?


Oracle 10g Express Edition
    select
        *
    from
        ( select
            row_.*,
            rownum rownum_
        from
            ( select
                this_.seq as seq0_0_,
                this_.zipcode as zipcode0_0_,
                this_.sido as sido0_0_,
                this_.gugun as gugun0_0_,
                this_.dong as dong0_0_,
                this_.ri as ri0_0_,
                this_.bldg as bldg0_0_,
                this_.st_bunji as st8_0_0_,
                this_.ed_bunji as ed9_0_0_
            from
                tbl_zipcode this_ ) row_
        where
            rownum <= ?
        )
    where
        rownum_ > ?


Microsoft SQL Server 2005
    select
        top 20 this_.seq as seq0_0_,
        this_.zipcode as zipcode0_0_,
        this_.sido as sido0_0_,
        this_.gugun as gugun0_0_,
        this_.dong as dong0_0_,
        this_.ri as ri0_0_,
        this_.bldg as bldg0_0_,
        this_.st_bunji as st8_0_0_,
        this_.ed_bunji as ed9_0_0_
    from
        tbl_zipcode this_


MSSQL 은 일단 페이지번호 * 갯수 만큼 가져와서 내부적으로 처리를 하나봐요.....




http://blog.naver.com/oyukihana?Redirect=Log&logNo=60002506896

반응형

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

PK 두개 이상시 매핑  (2) 2009.12.24
Reverse Engineering  (0) 2009.12.24
Hibernate 시작하기  (0) 2009.12.23
Criteria 사용하여 질의 하기 #1  (4) 2009.09.10
다대다(n:m) 관계 설정하기  (0) 2009.09.02
일대다(1:n) 관계 설정하기  (1) 2009.08.31
일대일(1:1) 관계 설정하기  (0) 2009.08.27
하이버네이트(Hibernate) 사용하기  (6) 2009.08.24
//

Criteria 사용하여 질의 하기 #1

Posted at 2009. 9. 10. 15:52 | Posted in Framework/Hibernate
반응형
Hibernate에서는 HQL에 익숙하지 못하거나 HQL 작성시 발생할 수 있는 오타로 인한 오류를 최소화 하기 위해 org.hibernate.Criteria API를 사용할 수 있도록 합니다.
Creteria API 호출을 통해 특정 객체에 대한 조회가 가능하고 org.hibernate.criterion.Restrictions API 호출을 통해 WHERE문에 해당하는 기본 조회 조건을 정의할 수 있습니다..

쉽게 말해서 select 쿼리입니다.

우편번호 데이터(50353건)로 여러가지 조회를 해봅시다! (Type 4 사용)



package com.tistory.antop;

public class Zipcode {
    // 데이터 순서(5)
    private int seq;
    // 우편번호(7)
    private String zipcode;
    // 특별시,광역시,도(4)
    private String sido;
    // 시,군,구(15)
    private String gugun;
    // 읍,면,동(26)
    private String dong;
    // 리(18)
    private String ri;
    // 건물명(40)
    private String bldg;
    // 시작번지(11)
    private String stBunji;
    // 끝번지(9)
    private String edBunji;

    // constructor, getter and setter (private setter seq)

    @Override
    public String toString() {
        // 길어서 생략...
    }
}



<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.tistory.antop">
    <class name="Zipcode" table="tbl_zipcode">
        <!-- 데이터 순서(5) -->
        <id name="seq" length="5">
            <generator class="native" />
        </id>
        <!-- 우편번호(7) -->
        <property name="zipcode" length="7" not-null="true" />
        <!-- 특별시,광역시,도(4) -->
        <property name="sido" length="4" not-null="true" />
        <!-- 시,군,구(15) -->
        <property name="gugun" length="15" not-null="true" />
        <!-- 읍,면,동(26) -->
        <property name="dong" length="26" not-null="true" />
        <!-- 리(18)  -->
        <property name="ri" length="18" />
        <!-- 건물명(40) -->
        <property name="bldg" length="40" />
        <!-- 시작번지(11) -->
        <property name="stBunji" column="st_bunji" length="11" />
        <!-- 끝번지(9) -->
        <property name="edBunji" column="ed_bunji" length="9" />
    </class>
</hibernate-mapping>


들어가기 전에 테스트 소스는 아래와 같습니다.
public class App {

    public static void main(String[] args) {
        Session sess = HibernateUtil.getCurrentSession();
        Transaction tx = sess.beginTransaction();

        // processing
  
        tx.commit();
        HibernateUtil.closeSession();
    }
 
    // Zipcode 객체가 들어있는 List를 출력
    private static void display(List<Zipcode> list) {
  
        try {
            int size = list.size();
   
            if(size == 0) {
                throw new Exception("리스트가 없습니다.(0건)");
            }
      
            for(int i=0 ; i < size ; i++) {
                System.out.println(list.get(i).toString());
            }
   
        } catch (NullPointerException e) {
            System.out.println("리스트가 존재하지 않습니다.");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}




SELECT

select * from tbl_zipcode
Criteria crit = sess.createCriteria(Zipcode.class);
List<Zipcode> zipcodeList = crit.list();

select seq, zipcode, st_bunji, ed_bunji from tbl_zipcode
Criteria crit = sess.createCriteria(Zipcode.class);
  
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.id().as("seq"));
projectionList.add(Projections.property("zipcode").as("zipcode"));
projectionList.add(Projections.property("stBunji").as("stBunji"));
projectionList.add(Projections.property("edBunji").as("edBunji"));
  
crit.setProjection(projectionList);
crit.setResultTransformer(new AliasToBeanResultTransformer(Zipcode.class));
  
List<Zipcode> zipcodeList = crit.list();
 
원하는 칼럼을 뽑아낼 때 쓰는 ProjectionListProjections을 사용합니다.(테이블 하나 조회하면서는 안쓸듯...)

해보니까 Projections 에서 꼭 as() 를 사용해서 Zipcode 클래스의 필드명과 이름을 맞춰줘야 하더군요... -0-/

Projections.id() 와 Projections.property() 는 뭐가 다른지 아시겠죠? ㅎㅎ;; id() 가 PK가 되는 필드 입니다.


select count(*) from tbl_zipcode
Criteria crit = sess.createCriteria(Zipcode.class);
crit.setProjection( Projections.rowCount() );
  
List<Integer> zipcodeList = crit.list();
System.out.println("count : " + zipcodeList.get(0));

select count(seq) as count from tbl_zipcode
Criteria crit = sess.createCriteria(Zipcode.class);
crit.setProjection(Projections.count("seq").as("count"));
   
List<Integer> zipcodeList = crit.list();
System.out.println("count : " + zipcodeList.get(0));




ORDER BY

select * from tbl_zipcode order by seq desc
Criteria crit = sess.createCriteria(Zipcode.class);
crit.addOrder(Order.desc("seq"));
   
List<Zipcode> zipcodeList = crit.list();

Order.asc(String propertyName) 도 있습니다~ +_+




Criteria crit = sess.createCriteria(Zipcode.class);
List<Zipcode> zipcodeList = crit.list();

생략하겠습니다~


WHERE

where문은 Restrictions을 사용합니다. 내용이 많으니 자세한것은 API를 봐주세요~

select * from tbl_zipcode where seq = 1555
crit.add(Restrictions.eq("seq", 1555));

select * from tbl_zipcode where seq = 45
crit.add(Restrictions.idEq(45));    // idEq() 를 사용하면 PK 칼럼으로 검색합니다.

select * from tbl_zipcode where dong like '방화%'
crit.add(Restrictions.like("dong", "방화", MatchMode.START));    // '방화%'

MatchMode 종류는 ANYWHERE, END, EXACT, START 4가지 종류가 있습니다.

딱 보면 아시겠지만 '%방화%', '방화%', '방화', '%방화' 이렇게 됩니다.


select * from tbl_zipcode where sido = '서울' and zipcode like '157%'
crit.add(Restrictions.eq("sido", "서울"));
crit.add(Restrictions.like("zipcode", "157", MatchMode.START)); 

여러개의 조건을 사용할 때에는 계속 .add() 해주면 됩니다.


select * from tbl_zipcode where where (st_bunji = '514' and sido = '서울' and dong = '방화2동')
Map<String, String> m = new Hashtable<String, String>();
m.put("sido", "서울");
m.put("dong", "방화2동");
m.put("stBunji", "514");
  
crit.add(Restrictions.allEq(m));

여러개의 eq를 사용할 경우 Map 을 사용할 수 있습니다.


select * from tbl_zipcode where seq >= 5300
crit.add(Restrictions.ge("seq", new Integer(50300)));

주의 해야 할 것은 2번째 인자는 Object 형태라는 것입니다. +_+;

ge(>=), gt(>), le(<=), lt(<) 가 있습니다.

    select * from tbl_zipcode where sido in ('서울', '부산')
    String[] in = {"서울", "부산"};
    crit.add(Restrictions.in("sido", in));    // Collection 또는 Object[] 사용 가능

    select * from tbl_zipcode where ( sido = '서울' and zipcode like '157%' )
    crit.add(
        Restrictions.conjunction()    // and 연산으로 그룹
            .add( Restrictions.eq("sido", "서울") )
            .add( Restrictions.like("zipcode", "157", MatchMode.START) )
    );

    and 조건을 여러개 사용할 경우 그냥 계속 .add() 해도 되지만 Restrictions.conjunction() 로 그룹할 수 있습니다.

    Restrictions.conjunction() 은 and 로 그룹하고, Restrictions.disjunction() 은 or 로 그룹합니다.


    select * from tbl_zipcode where ( (dong like '방화%' or dong like '%1동') and (zipcode like '157%' or zipcode like '431') )
    crit.add(
        Restrictions.conjunction()
            .add(
                Restrictions.disjunction()
                    .add(Restrictions.like("dong", "방화", MatchMode.START))
                    .add(Restrictions.like("dong", "1동", MatchMode.END))
            )
            .add(
                Restrictions.disjunction()
                    .add(Restrictions.like("zipcode", "157", MatchMode.START))
                    .add(Restrictions.like("zipcode", "431", MatchMode.START))
            )
    );

    - 동이 '방화'로 시작하거나 '1동'으로 끝나고 우편번호가 157로 시작하거나 431로 시작하는 데이터를 조회 합니다.
    - (동이 '방화'로 시작 or 동이 '1동'으로 끝) and (우편번호가 '157'로 시작 or 우편번호가 '431'로 시작)

    정도가 되겠네요... (헷갈리.. ㅠㅠ)


    select * from tbl_zipcode where st_bunji between '157' and '158'
    crit.add(Restrictions.between("stBunji", "157", "158"));

    select * from tbl_zipcode where ( dong like '방%' and dong like '%1동' )
    crit.add( Restrictions.and( Restrictions.eq("sido", "서울"), Restrictions.like("dong", "1동", MatchMode.END) ) );

    Restrictions.and(조건, 조건) 은 두개의 조건을 and 로 그룹합니다.
    Restrictions.or(조건, 조건) 은 두개의 조건을 or 로 그룹합니다.


    select * from tbl_zipcode where not ( sido in ('서울', '부산') )
    crit.add( Restrictions.not( Restrictions.in("sido", new String[] { "서울", "부산" }) ) );





    GROUP BY

    ProjectionListProjections을 사용합니다

    select sido, count(sido) as sidoCount from tbl_zipcode group by sido
    // 각 시도별 우편번호 갯수를 카운트
    ProjectionList projectionList = Projections.projectionList();
    projectionList.add( Projections.alias( Projections.count("sido"), "sido_count") );   // count(sido) as sido_count
    projectionList.add( Projections.groupProperty("sido").as("sido") );    // group by sido
      
    crit.setProjection(projectionList);
    crit.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
      
    List<Map<String, Object>> list = crit.list();
      
    for(int i=0 ; i < list.size() ; i++) {
        Map<String, Object> m = (Map<String, Object>) list.get(i);
        System.out.println(m);
    }

    Projections.groupProperty(String propertyName) 을 이용해서 GROUP BY 를 하고

    crit.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); 는 조회해서 나온 결과를 Map 형태로 변환해 줍니다.

    이렇게 나온 결과가 Zipcode 클래스 자체 형태가 아니라면 Map 으로 변환해서 처리하면 됩니다.


    ※ groupPropery(String propertyName) 과 groupProperty(String propertyName).as(String alias) 와는 차이가 있습니다.

    select count(sido) as sido_count from tbl_zipcode group by sido
    projectionList.add( Projections.groupProperty("sido") );

    // 결과
    {sido_count=2800}
    {sido_count=8170}
    {sido_count=3614}
    {sido_count=4921}


    select count(sido) as sido_count, sido from tbl_zipcode group by sido
    projectionList.add( Projections.groupProperty("sido").as("sido") );    // sido 가 select 칼럼에 추가
    // 결과
    {sido=강원, sido_count=2800}
    {sido=경기, sido_count=8170}
    {sido=경남, sido_count=3614}



    여러 칼럼으로 GROUP BY 하려면 똑같이 Projections.groupProperty() 를 추가 하면 됩니다.

    select sido, gugun, count(sido) as sidoCount from tbl_zipcode group by sido, gugun
    // 각 시도별 우편번호 갯수를 카운트
    ProjectionList projectionList = Projections.projectionList();
    projectionList.add( Projections.alias( Projections.count("sido"), "sido_count") );   // count(sido) as sido_count
    projectionList.add( Projections.groupProperty("sido").as("sido") );    // group by sido
    projectionList.add( Projections.groupProperty("gugun").as("gugun") );    // group by gugun


    ※ 다른 WHERE(또는 다른 것들..)과 같이 쓸때 Projections에서 alias를 다른 이름으로 사용해야 합니다.

    crit.add(Restrictions.eq("sido", "서울"));
      
    // 각 시도별 우편번호 갯수를 카운트
    ProjectionList projectionList = Projections.projectionList();
    projectionList.add( Projections.alias( Projections.count("sido"), "sido_count") );   // count(sido) as sido_count
    projectionList.add( Projections.groupProperty("sido").as("sido") );    // group by sido

    select count(sido) as sido_count, sido from tbl_zipcode where sido = '서울' group by sido

    정도의 쿼리를 예상 했지만 실제로 해보면 잘못된 쿼리라는 에러가 납니다.

    이유는 하이버네이트에서 쿼리를 만들때 내부적으로 alias 를 사용하는데,

    select count(this_.sido) as y0_, this_.sido as y1_ from tbl_zipcode this_ where y1_=? group by this_.sido

    위와 같은 쿼리가 나오게 됩니다. 이상하죠? ㅎㄷ;;

    crit.add(Restrictions.eq("sido", "서울"));
      
    // 각 시도별 우편번호 갯수를 카운트
    ProjectionList projectionList = Projections.projectionList();
    projectionList.add( Projections.alias( Projections.count("sido"), "sido_count") );   // count(sido) as sido_count
    projectionList.add( Projections.groupProperty("sido").as("g_sido"));    // group by sido

    // 결과
    {g_sido=서울, sido_count=7807}

    위와 같이 Projections 에서 다른 alias 를 사용하면 where 문의 칼럼이 제대로 나옵니다.

    select count(this_.sido) as y0_, this_.sido as y1_ from tbl_zipcode this_ where this_.sido=? group by this_.sido





    HAVING

    having 은 Criteria 에서 지원이 안되는 것 같습니다. HQL 을 사용하면 되겠지만 쿼리문을 쓰기 싫으므로...

    열시미 찾아서 having 이 지원되게 수정(3.1 rc1 버젼) 된 라이브러리를 찾았습니다! (찾았는데 오류나서 개인적으로 수정 -_-)

     - 수정된 java 파일 입니다.

     - 하이버네이트 jar 에 통합시킨 파일입니다. ( 3.2.4.GA + 수정된 java(3.1 rc1) )


    수정 된 부분은 Criteria 클래스에서 public Criteria addHaving(Criterion criterion); 메소드가 추가되었습니다.


    select count(sido) as sido_count, sido from tbl_zipcode group by sido having sido in ('서울', '경기', '부산')
    ProjectionList projectionList = Projections.projectionList();
    projectionList.add( Projections.alias( Projections.count("sido"), "sido_count") );
    projectionList.add( Projections.groupProperty("sido").as("g_sido"));
      
    crit.addHaving( Restrictions.in("sido", new String[] {"서울", "경기", "부산"}) );    // having sido

    // select count(this_.sido) as y0_, this_.sido as y1_ from tbl_zipcode this_ group by this_.sido having this_.sido in (?, ?, ?)
    {g_sido=경기, sido_count=8170}
    {g_sido=부산, sido_count=3287}
    {g_sido=서울, sido_count=7807}



    이 group by 와 having 부분은 이것저것 해보면 아시겠지만 제약(?)이 있군요.. ㅠ_ㅠ

    select count(sido) as sido_count, sido, gugun from tbl_zipcode group by sido, gugun having count(sido) > 500

    이거 할 줄 아시는분... 젭알!!!!!!! ㅠ_ㅠ





    간단히 우편번호 검색하는 걸 만들어 봤습니다. ㄷㄷ;


    반응형

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

    PK 두개 이상시 매핑  (2) 2009.12.24
    Reverse Engineering  (0) 2009.12.24
    Hibernate 시작하기  (0) 2009.12.23
    Criteria 사용하여 질의 하기 #2  (1) 2009.10.03
    다대다(n:m) 관계 설정하기  (0) 2009.09.02
    일대다(1:n) 관계 설정하기  (1) 2009.08.31
    일대일(1:1) 관계 설정하기  (0) 2009.08.27
    하이버네이트(Hibernate) 사용하기  (6) 2009.08.24
    //

    다대다(n:m) 관계 설정하기

    Posted at 2009. 9. 2. 11:05 | Posted in Framework/Hibernate
    반응형
    이번에는 사람과 세미나 사이의 다대다(n:m) 관계를 설정해보겠습니다. ^^;;

    사람은 여러개의 세미나를 등록할 수 있고, 세미나는 여러 사람이 올 수 있죠~

    Java 1.6.0_15
    Eclipse 3.5
    Hibernate 3.3.2.GA
    Apache Tomcat 6.0.18
    MySQL 5.1 (HSQLDB 1.9.0 rc4)



    사람(Person)과 세미나(Person) 을 매핑 합시다~

    Person.java
    package com.tistory.antop;

    public class Person {

        private int seq;
        private String name;
        private Gender gender;
     
        private enum Gender {
            남, 여
        }
     
        // constructor, getter and setter (private setter seq)
    }

    Person.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Person" table="tbl_person">
            <id name="seq">
                <generator class="native" />
            </id>
      
            <property name="name" />
            <property name="gender" />
      
        </class>
    </hibernate-mapping>


    Seminar.java
    package com.tistory.antop;

    import java.util.Date;

    public class Seminar {

        private int sid;
        private String title;
        private Date sDate = new Date();
        private String area;

        // constructor, getter and setter (private setter sid)
     }

    Seminar.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Seminar" table="tbl_seminar">
            <id name="sid">
                <generator class="native" />
            </id>
      
            <property name="title" />
            <property name="sDate" column="s_date" />
            <property name="area" />
      
        </class>
    </hibernate-mapping>


    hibernate.cfg.xml
    <hibernate-configuration>
        <session-factory>
            ...

            <!-- Mapping -->
            <mapping resource="com/tistory/antop/Person.hbm.xml" />
            <mapping resource="com/tistory/antop/Seminar.hbm.xml" />

        </session-factory>
    </hibernate-configuration>


    관계가 맺어지지 않은 테이블이 매핑 되었습니다~



    이제 n:m 관계를 맺어봅시다~

    논리적(생각)으로는 두 테이블이 n:m 관계가 나오지만, 물리적(실제)으로는 n:m 관계를 만들려면 두 테이블을 이어줄 테이블이 하나 더 껴들어가야 합니다... ㅠ_ㅠ




    클래스와 매핑 XML 에 n:m 관계(양방향)를 추가 합시다.

    서로를 n 개만큼 참조해야하므로 Set 으로 하겠습니다.


    Person.java
    package com.tistory.antop;

    import java.util.HashSet;
    import java.util.Set;

    public class Person {
       
        ...

        // person n:m seminar
        private Set<Seminar> seminars = new HashSet<Seminar>();

        public Set<Seminar> getSeminars() {
            return seminars;
        }

        public void setSeminars(Set<Seminar> seminars) {
            this.seminars = seminars;
        }

        // 세미나 등록
        public void addSeminar(Seminar s) {
            this.seminars.add(s);
        }
        
        // 세미나 취소
        public void cancelSeminar(Seminar s) {
            this.seminars.remove(s);
        }
    }

    Person.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Person" table="tbl_persons">
            ...

            <!-- person n:m seminar -->
            <set name="seminars" table="tbl_persons_seminars">
                <key column="seq" />
                <many-to-many class="com.tistory.antop.Seminar" column="sid" />
            </set>
     
        </class>
    </hibernate-mapping> 

    <set name="seminars" table="tbl_persons_seminars">
       java.util.Set 을 사용 했고 담고있을 필드는 seminars, n:m 관계를 맺어주는 테이블명은 tbl_persons_seminars

    <key column="seq" />
       자기 테이블(Person)의 FK(Foreign Key) 되는 칼럼명입니다.

    <many-to-many class="com.tistory.antop.Seminar" column="sid"  />
      m 관계를 맺게되는 테이블(tbl_seminar)의 클래스명과 그쪽의 FK가 되는 칼럼명입니다.


    Seminar 쪽도 마친가지로 매핑해 줍니다.

    Seminar.java
    package com.tistory.antop;

    import java.util.HashSet;
    import java.util.Set;

    public class Seminar {

        // person n:m seminar
        private Set<Person> persons = new HashSet<Person>();

        // 누가 이 세미나에 신청 했는지 알기위해 getter 만 public
        public Set<Person> getPersons() {
            return persons;
        }

        @SuppressWarnings("unused")
        private void setPersons(Set<Person> persons) {
            this.persons = persons;
        }
     
        // 사람(person)이 세미나 등록, 취소를 할 수 있다고 보고 세미나를 기준으로 등록 삭제는 없슴!
    }

    Seminar.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Seminar" table="tbl_seminars">

            ...  

            <!-- person n:m seminar -->
            <set name="persons" table="tbl_persons_seminars">
                <key column="sid" />
                <many-to-many class="com.tistory.antop.Person" column="seq" />
            </set>
      
     </class>
    </hibernate-mapping>




    테스트 해봅시다~

    request.getParameter() 안쓰려고 스트러츠 썻는데 더 꼬여뿌렀네용 -_-;;

    하이버네이트 부분은 dao 에 있습니다.




    반응형

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

    PK 두개 이상시 매핑  (2) 2009.12.24
    Reverse Engineering  (0) 2009.12.24
    Hibernate 시작하기  (0) 2009.12.23
    Criteria 사용하여 질의 하기 #2  (1) 2009.10.03
    Criteria 사용하여 질의 하기 #1  (4) 2009.09.10
    일대다(1:n) 관계 설정하기  (1) 2009.08.31
    일대일(1:1) 관계 설정하기  (0) 2009.08.27
    하이버네이트(Hibernate) 사용하기  (6) 2009.08.24
    //

    일대다(1:n) 관계 설정하기

    Posted at 2009. 8. 31. 16:22 | Posted in Framework/Hibernate
    반응형
    전 포스핑에서 1:1 관계를 설정해봤습니다.

    테스트 업무는 고객(cutomer)이 문의(support)를 올리고, 관리자가 그 문의에 답변(reply)을 다는 업무입니다.

    고객은 문의를 여러번 할 수 있고, 관리자는 그 문의에 여러번 답변할  수 있습니다.


    Java 1.6.0_15
    Eclipse 3.5
    Hibernate 3.3.2.GA
    Apache Tomcat 6.0.18
    MySQL 5.1 (HSQLDB 1.9.0 rc4)



    먼저 아무 관계가 없는 매핑을 만들어 봅시다.

    Customer.java
    package com.tistory.antop;

    public class Customer {

        private String id;
        private String name;
        private String pwd;
        private String tel;

        // construct, getter, setter
    }

    Customer.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Customer" table="tbl_customers">
            <id name="id" length="20" />
            <property name="pwd" column="password" length="20" not-null="true" />
            <property name="name" length="30" not-null="true" />
            <property name="tel" length="13" not-null="false" />
        </class>
    </hibernate-mapping>


    Support.java
    package com.tistory.antop;

    public class Support {

        private int id;
        private String title;
        private String contents;

        // construct, getter, setter (private setter id)
    }

    Support.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Support" table="tbl_supprots">
            <id name="id" column="support_id">
                <generator class="native" />
            </id>
            <property name="title" length="255" not-null="true" />
            <property name="contents" type="text" />
        </class>
    </hibernate-mapping>


    Reply.java
    package com.tistory.antop;

    public class Reply {

        private int seq;
        private String reply;
     
        // construct, getter, setter (private setter seq)
    }

    Reply.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Reply" table="tbl_replays">
            <id name="seq">
                <generator class="native" />
            </id>
            <property name="reply" type="text" not-null="true" />
        </class>
    </hibernate-mapping>


    hibernate.cfg.xml
    <hibernate-configuration>
        <session-factory>
            ...

            <!-- Mapping -->
            <mapping resource="com/tistory/antop/Customer.hbm.xml" />
            <mapping resource="com/tistory/antop/Support.hbm.xml" />
            <mapping resource="com/tistory/antop/Reply.hbm.xml" />

        </session-factory>
    </hibernate-configuration>



    아무런 관계(릴레이션)가 없는 테이블 3개가 매핑 되었습니다.



    이제 1:n 관계를 설정 합시다.

    1:1에서와 마찬가지로 서로 참조를 시켜야 합니다.
     
    db 에서는 1:n 관계를 1이 되는쪽의 PK 와 n 이 되는쪽의 FK를 지정하여 관계를 설정해주고,
     
    클래스로 구현하면 1이 되는 클래스 필드가 n이 되는 클래스를 n개 만큼 참조하고 있어야 합니다.

    반대로 n쪽의 클래스는 1이 되는 클래스를 참조하고 있어야겠죠?


    먼저 고객(customer)과 문의(support)의 1:n 관계를 설정 해봅시다.


    관계를 맺을 수 있는 필드와 사용할 적절한 메소드를 추가합니다.

    Customer.java
    public class Customer {
        ...

        // customer 1:n support 관계를 가지는 필드
        private Set<Support> supports = new HashSet<Support>();

        public Set<Reply> getReplys() {
            return replys;
        }

        private void setReplys(Set<Reply> replys) {
            this.replys = replys;
        }

        // 문의 추가
        public void addSupport(Support support) {
            if(getSupports() == null) {
                setSupports(new HashSet<Support>());
            }
            // customer -> support 참조
            getSupports().add(support);
            // support -> customer 참조
            support.setCustomer(this);
        }
     
        // 문의 삭제
        public void delSupport(Support support) {
            getSupports().remove(support);
        }
     
        // 문의 비우기
        public void clearSupports() {
            getSupports().clear();
        }
    }

    Customer.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Customer" table="tbl_customers">
            ...

            <!-- customer 1:n support -->
            <set name="supports" inverse="true" cascade="all-delete-orphan">
                <key column="customer_id" />
                <one-to-many class="com.tistory.antop.Support" />
            </set>
        </class>
    }

    Customer 와 Support 의 1:n 관계에서 Support는 콜랙션(Collection) 형태로 참조됩니다.

    Set, List, Bag, Map 등등 있는데 자세한건 여기에서 확인을... -0-

     -= <set></set> : java.util.Set 타입으로 정의합니다.
       - name : java.util.Set 타입의 필드명입니다. (Customer.java의 "private Set<Support> supports")
       - inverse : 객체간 관계의 책임을 어디에 둘지에 대한 옵션을 정의하기 위한 속성입니다.
                       즉, 한 쪽은 owner의 역할을 맡기고, 다른 한 쪽에는 sub의 역할을 맡기기 위함입니다.
       - cascade : 부모 객체에 대한 CUD를 자식 객체에도 전이할지에 대한 옵션을 정의하기 위한 속성입니다.
     -= <key column="..." /> : FK(Foreign Key)를 명시합니다. (Support 쪽의 FK)
     -= <on-to-many class="..." /> : 관계를 맺는 클래스명(패키지명포함)을 명시합니다.


    Support.java
    public class Support {
        ...
        // customer 1:n support 관계를 가지는 필드
        private Customer customer;
       
        public Customer getCustomer() {
            return customer;
        }

        private void setCustomer(Customer customer) {
            this.customer = customer;
        }

        // 답변 추가
        public void addReply(Reply reply) {
            if(getReplys() == null) {
                setReplys(new HashSet<Reply>());
            }
            // support -> reply 참조
            getReplys().add(reply);
            // reply -> support 참조
            reply.setSupport(this);
        }
     
        // 답변 삭제
        public void delReply(Reply reply) {
            getReplys().remove(reply);
        }
     
        // 답변 비우기
        public void clear() {
            getReplys().clear();
        }
    }

    Support.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Support" table="tbl_supprots">
             ...

            <!-- customer 1:n support -->
            <many-to-one name="customer" column="customer_id" class="com.tistory.antop.Customer" />
        </class>
    </hibernate-mapping>

    Support 쪽에서는 그냥 customer 하나만 참조하면 됩니다.


    이제 문의(support)와 답변(reply)도 1:n 관계를 설정합시다! +_+/

    Support.java
    public class Support {
        ...

        // support 1:n reply 관계를 가지는 필드
        private Set<Reply> replys = new HashSet<Reply>();

        public Set<Reply> getReplys() {
            return replys;
        }

        private void setReplys(Set<Reply> replys) {
            this.replys = replys;
        }
    }

    Support.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Support" table="tbl_supprots">
            ...

            <!-- support 1:n reply -->
            <set name="replys" inverse="true" cascade="all-delete-orphan">
                <key column="support_id" />
                <one-to-many class="com.tistory.antop.Reply" />
            </set>
        </class>
    </hibernate-mapping>


    Reply.java
    public class Reply {
        ...

        // support 1:n reply 관계를 가지는 필드
        private Support support;

        public Support getSupport() {
            return support;
        }

        public void setSupport(Support support) {
            this.support = support;
        }
    }

    Reply.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Reply" table="tbl_replays">
            ...

            <!-- support 1:n reply -->
            <many-to-one name="support" column="support_id" class="com.tistory.antop.Support" />
        </class>
    </hibernate-mapping>


    이제 문의(support)와 답변(reply)의 1:n 관계도 설정 되었습니다.




    이제 CRUD 해야졍.... 배보다 배꼽이 더 크네여 아오~

    이번에는 조금 제대로 만들어 보았습니다... (아주 조금입니다... 조ㄱㅡ..)

    진짜 배보다 배꼽이 더 켜졌네요;;;





    참고 사이트
    http://docs.jboss.org/hibernate/stable/core/reference/en/html/associations.html
    http://dev.anyframejava.org/anyframe/doc/core/3.1.0/corefw/guide/hibernate-persistence-mapping-association.html
    http://javacan.tistory.com/entry/106


    반응형

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

    PK 두개 이상시 매핑  (2) 2009.12.24
    Reverse Engineering  (0) 2009.12.24
    Hibernate 시작하기  (0) 2009.12.23
    Criteria 사용하여 질의 하기 #2  (1) 2009.10.03
    Criteria 사용하여 질의 하기 #1  (4) 2009.09.10
    다대다(n:m) 관계 설정하기  (0) 2009.09.02
    일대일(1:1) 관계 설정하기  (0) 2009.08.27
    하이버네이트(Hibernate) 사용하기  (6) 2009.08.24
    //

    일대일(1:1) 관계 설정하기

    Posted at 2009. 8. 27. 16:58 | Posted in Framework/Hibernate
    반응형

    바로전 포스트에서 아주 기본적인 하이버네이트 매핑하는 걸 했었는데...

    이번에는 이 매핑된 클래스끼리 관계 설정하는 것을 해보겠습니다!!

    두개의 테이블이 PK(Primary Key)와 FK(Foreign Key)로 관계(Relation)가 형성되어 있는데 ORM(Object-Relational Mapping) 이랍시고 클래스와 매핑 시켰는데 테이블간의 관계를 클래스에서 제대로 사용할 수 없다면 쓰나 마나겠죠... -_-/

    먼저 일대일(1:1) 관계를 해봅시다.


    Java 1.6.0_15
    Eclipse 3.5
    Hibernate 3.3.2.GA
    Apache Tomcat 6.0.18
    MySQL 5.1 (HSQLDB 1.9.0 rc4)



    먼저 일단 기본적인 클래스와 매핑 XML 을 만듭니다.(1:1관계가 설정 안된 상태)

    Board.java
    package com.tistory.antop;

    import java.text.SimpleDateFormat;
    import java.util.Date;

    public class Board {

        private int boardId;
        private String title;
        private String userName;
        private Date writeDate = new Date();

        public Board() { }

        // getter and setter (private setter boardId)
    }

    Board.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Board" table="tbl_board">
            <id name="boardId" column="board_id" length="11">
                <generator class="native" />
            </id>
            <property name="title" length="255" not-null="true" />
            <property name="userName" column="user_name" length="30" not-null="true" />
            <property name="writeDate" column="write_date" type="timestamp" not-null="true" />
        </class>
    </hibernate-mapping>

    ※ 매핑 XML 에 대한 설명은 여기(또는 한글번역본)를 봐주세요~ ㅠ_ㅠ 결고 설명하기 귀찮아서 그러는게 아닝미...


    BoardDetail.java
    package com.tistory.antop;

    public class BoardDetail {

        private int boardId;
        private String email;
        private String contents;
        private Board board;
     
        public BoardDetail() { }

        // getter and setter (private setter boardId)
    }

    BoardDetail.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="BoardDetail" table="tbl_board_detail">
            <id name="boardId" column="board_id" type="integer" length="11"></id>
            <property name="email" type="string" length="50" not-null="false" />
            <property name="contents" type="text" not-null="false" />
        </class>
    </hibernate-mapping>


    hibernate.cfg.xml
    <hibernate-configuration>
        <session-factory>
            ...
            ...

            <!-- Mapping -->
            <mapping resource="com/tistory/antop/Board.hbm.xml" />
            <mapping resource="com/tistory/antop/BoardDetail.hbm.xml" />
        </session-factory>
    </hibernate-configuration>


    따로따로 쓰면 아주 잘 되는 매핑입니다. 현재 상태에서의 테이블은 아래와 같습니다.



    1:1 관계를 설정해봅시다.

    Board.java 와 BoardDetail.java 에 서로를 참조하는 필드를 만듭니다.

    Board.java
    public class Board {
        ...
       
        private BoardDetail boardDetail;

        // getter and setter
    }

    BoardDetail.java
    public class BoardDetail {
        ....

        private Board board;

        // getter and setter
    }


    매핑 XML 에서 Board와 BoardDetail 의 1:1 관계를 설정합니다.

    Board.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="Board" table="tbl_board">
            ...
            
            <!-- name: 관계를 맺을 클래스를 참조하는 필드 이름 -->
            <!-- class: 관계를 맺을 클래스 이름(패키지 포함) -->
            <!-- cascade: 삭제나 업데이트 시 관계가 맺어진(?) 테이블도 적용할지 하는 설정 -->
            <one-to-one name="boardDetail" class="com.tistory.antop.BoardDetail" cascade="all" />
         </class>
    </hibernate-mapping>

    BoardDetail.hbm.xml
    <hibernate-mapping package="com.tistory.antop">
        <class name="BoardDetail" table="tbl_board_detail">
            <!-- Board 의 pk 번호를 따라가게 설정 -->
            <id name="boardId" column="board_id" type="integer" length="11">
                <generator class="foreign">
                    <param name="property">board</param>
                </generator>
            </id>

            ...
      
            <!-- name: 관계를 맺을 클래스를 참조하는 필드 이름 -->
            <!-- class : 관계를 맺을 클래스 이름(패키지 포함) -->
            <!-- constrained: 매핑된 테이블의 PK에 대한 FK constraint가 연관된 클래스의 테이블을 참조하는지 여부를 지정 -->
            <one-to-one name="board" class="com.tistory.antop.Board" constrained="true" />
        </class>
    </hibernate-mapping>


    보면 알겠지만... 속성들이 DDL에 나오는 것들이랑 거의 비슷 합니다. 촉이 좋으면 금방 알 것입니다. +_+;

    hibernate.cfg.xml 에서 hbm2ddl.auto을 설정해서 만들어진 DDL 을 보면 PK, FK 로 1:1 관계가 생성된걸 볼 수 있습니다.

    hibernate.cfg.xml
    <hibernate-configuration>
        <session-factory>
            ...

            <!-- 최초 시작(startup) 시 테이블을 새로 생성(drop and re-create database schema) -->
            <property name="hbm2ddl.auto">create</property>

            ...

        </session-factory>
    </hibernate-configuration>


    DDL(Data Definition Language) - MySQL 5.1 기준
    CREATE TABLE `tbl_board` (
      `board_id` INTEGER(11) NOT NULL AUTO_INCREMENT,
      `title` VARCHAR(255) COLLATE utf8_general_ci NOT NULL DEFAULT '',
      `user_name` VARCHAR(30) COLLATE utf8_general_ci NOT NULL DEFAULT '',
      `write_date` DATETIME NOT NULL,
      PRIMARY KEY (`board_id`)

    )ENGINE=InnoDB
    AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

    --

    CREATE TABLE `tbl_board_detail` (
      `board_id` INTEGER(11) NOT NULL,
      `email` VARCHAR(50) COLLATE utf8_general_ci DEFAULT NULL,
      `contents` LONGTEXT,
      PRIMARY KEY (`board_id`),
      KEY `FK658F66AB607D6F39` (`board_id`),
      CONSTRAINT `FK658F66AB607D6F39` FOREIGN KEY (`board_id`) REFERENCES `tbl_board` (`board_id`)

    )ENGINE=InnoDB
    CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';




    간단하게 CRUD 를 만들었습니다.




    ※ _mysql 과 _hsqldb 는 hibernate.cfg.xml 의 jdbc 설정과 lib 의 jdbc 라이브러만 다릅니다. 소스는 똑같습니다.

    MySQL 도 귀찮다! 하시면 hsqldb 로 만들걸 해보세요~ +_+


    중요한 부분은 저장할 때 서로를 참조 시켜주는 겁니다.

    proc.jsp
    ...

    Session sess = HibernateUtil.getCurrentSession();
    Transaction tx = sess.beginTransaction();

    // 등록
    if("write".equals(query)) {
        String title = request.getParameter("title");
        String userName = request.getParameter("userName");
        String email = request.getParameter("email");
        String contents = request.getParameter("contents");

        Board board = new Board();
        board.setTitle(title);
        board.setUserName(userName);
     
        BoardDetail boardDetail = new BoardDetail();
        boardDetail.setEmail(email);
        boardDetail.setContents(contents);
     
        // 게시물과 게시물상세의 1:1 관계 설정
        board.setBoardDetail(boardDetail);
        boardDetail.setBoard(board);
     
        sess.save(board);        // Board 저장 (insert 쿼리)
    }

    ...

    tx.commit();        // BoardDetail 저장 (insert 쿼리)
    HibernateUtil.closeSession();


    Board는 sess.save(baord); 에서 저장 하는데, BoardDetail은 tx.commit(); 에서 저장을 하네요... 음... ^O^




    참고 사이트
    http://javacan.tistory.com/entry/102
    http://javacan.tistory.com/entry/103
    http://javacan.tistory.com/entry/104
    http://javacan.tistory.com/entry/105

    반응형

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

    PK 두개 이상시 매핑  (2) 2009.12.24
    Reverse Engineering  (0) 2009.12.24
    Hibernate 시작하기  (0) 2009.12.23
    Criteria 사용하여 질의 하기 #2  (1) 2009.10.03
    Criteria 사용하여 질의 하기 #1  (4) 2009.09.10
    다대다(n:m) 관계 설정하기  (0) 2009.09.02
    일대다(1:n) 관계 설정하기  (1) 2009.08.31
    하이버네이트(Hibernate) 사용하기  (6) 2009.08.24
    //