다대다(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  (3) 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

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

일대다(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  (3) 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
  1. 초보
    잘 보고 갑니다.

    그리고 조금 퍼가겠습니다 감사합니다.

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

일대일(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  (3) 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

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기