[標(biāo)題]:Hibernate一對(duì)多(單向)
[時(shí)間]:2009-6-12
[摘要]:單向一對(duì)多關(guān)聯(lián)只需要在"一方"進(jìn)行配置即可,"多方"無(wú)需額外配置。
[關(guān)鍵字]:外鍵,inverse,Hibernate,Set,一對(duì)多,單向,ORM,mapping,關(guān)系數(shù)據(jù)庫(kù),映射
[環(huán)境]:MyEclipse7 , JDK6,Hibernate3.2,Tomcat6,MySQL 5.1.34-community
[作者]:Winty (wintys@gmail.com) http://m.tkk7.com/wintys
[正文]:
Hibernate一對(duì)多關(guān)聯(lián),例如一個(gè)用戶有多張銀行卡(只考慮用戶到銀行卡的單向一對(duì)多關(guān)聯(lián))。由于是學(xué)習(xí)Hibernate原理,并沒(méi)有使用工具自動(dòng)生成代碼等。
單向一對(duì)多關(guān)聯(lián)只需要在"一方"進(jìn)行配置即可,"多方"無(wú)需額外配置。
a.Java程序中所要做的一對(duì)多:
public class User{
...
private Set<Card> cards;
...
}
public class Card{
...
}
b.Hibernate中所要做的一對(duì)多:
User.hbm.xml:
...
<set name="cards" inverse="false" cascade="all">
<key column="userId" />
<one-to-many class="wintys.hibernate.onetomany.Card" />
</set>
...
c.數(shù)據(jù)庫(kù)中的一對(duì)多:
而對(duì)應(yīng)的數(shù)據(jù)庫(kù)中,只要相應(yīng)在Card對(duì)應(yīng)的物理表中添加外鍵userId(不要設(shè)為NOT NULL)即可。
詳細(xì)的MyEclipse WebProject如下:
1、實(shí)體類:
用戶類User.java:
package wintys.hibernate.onetomany;
import java.util.Set;
public class User {
private String id;
private String name;
private Set<Card> cards;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setCards(Set<Card> cards) {
this.cards = cards;
}
public Set<Card> getCards() {
return cards;
}
}
銀行卡類Card.java:
package wintys.hibernate.onetomany;
public class Card {
private String id;
private float balance;
public Card(){
}
public Card(float balance){
this.balance = balance;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public float getBalance() {
return balance;
}
public void setBalance(float balance) {
this.balance = balance;
}
}
2、數(shù)據(jù)庫(kù)表:
數(shù)據(jù)庫(kù)是MySQL 5.1.34-community。
用戶表:
CREATE TABLE myuser(
id VARCHAR(50) NOT NULL,
name VARCHAR(100),
PRIMARY KEY(id)
);
銀行卡表:
CREATE TABLE mycard(
id VARCHAR(50) NOT NULL,
balance FLOAT(7,2),
userId VARCHAR(50),
PRIMARY KEY(id)
);
3、映射文件:
用戶類映射文件/src/wintys/hibernate/onetomany/User.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="wintys.hibernate.onetomany.User" table="myuser" catalog="db">
<id name="id" type="string">
<column name="id" not-null="true"/>
<generator class="uuid.hex" />
</id>
<property name="name" type="java.lang.String" column="name" />
<set name="cards" inverse="false" cascade="all">
<key column="userId" />
<one-to-many class="wintys.hibernate.onetomany.Card" />
</set>
</class>
</hibernate-mapping>
銀行卡映射文件/src/wintys/hibernate/onetomany/User.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="wintys.hibernate.onetomany.Card" table="mycard" catalog="db">
<id name="id" type="string">
<column name="id" not-null="true"/>
<generator class="uuid.hex" />
</id>
<property name="balance" />
</class>
</hibernate-mapping>
Hibernate配置文件:/src/hibernate.cfg.xml:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<property name="connection.username">root</property>
<property name="connection.url">
jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=utf-8
</property>
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="myeclipse.connection.profile">MySQLDriver</property>
<property name="connection.password">root</property>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="show_sql">true</property>
<mapping resource="wintys/hibernate/onetomany/User.hbm.xml" />
<mapping resource="wintys/hibernate/onetomany/Card.hbm.xml" />
</session-factory>
</hibernate-configuration>
4、使用測(cè)試:
/src/wintys/hibernate/onetomany/HibernateDAO.java:
package wintys.hibernate.onetomany;
import java.util.List;
public interface HibernateDAO {
public void insert();
public List<User> selectAll();
}
/src/wintys/hibernate/onetomany/HibernateDAOBean.java:
package wintys.hibernate.onetomany;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
public class HibernateDAOBean implements HibernateDAO {
public void insert() throws HibernateException {
Transaction tc = null;
try{
Set<Card> cards = new HashSet<Card>();
Card c1,c2,c3;
c1 = new Card(7641.96f);
c2 = new Card(654.8f);
c3 = new Card(3650f);
cards.add(c1);
cards.add(c2);
cards.add(c3);
User user = new User();
user.setName("Tom");
user.setCards(cards);
Session session = HibernateUtil.getSession();
tc = session.beginTransaction();
/*
配置文件中的cascade="true"時(shí),所以無(wú)需手動(dòng)保存c1,c2,c3
session.save(c1);
session.save(c2);
session.save(c3);
*/
session.save(user);
tc.commit();
}catch(HibernateException e){
try{
if(tc != null)
tc.rollback();
}catch(Exception ex){
System.err.println(ex.getMessage());
}
System.err.println(e.getMessage());
}finally{
HibernateUtil.closeSession();
}
}
@SuppressWarnings("unchecked")
public List<User> selectAll() throws HibernateException {
List<User> users = null;
Transaction tc = null;
try{
Session session = HibernateUtil.getSession();
tc = session.beginTransaction();
Query query = session.createQuery("from User");
users = query.list();
tc.commit();
}catch(HibernateException e){
try{
if(tc != null){
tc.rollback();
users = null;
}
}catch(Exception ex){
System.err.println(ex.getMessage());
}
System.err.println(e.getMessage());
}finally{
//HibernateUtil.closeSession();
}
return users;
}
}
/src/wintys/hibernate/onetomany/HibernateUtil.java:
package wintys.hibernate.onetomany;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
* Hibernate Session管理
* @author Winty
*/
public class HibernateUtil {
private static SessionFactory factory = null;
private static ThreadLocal<Session> threadLocal;
static {
try{
factory = new Configuration()
.configure()
.buildSessionFactory();
}catch(HibernateException e){
System.err.println(e.getMessage());
}
threadLocal = new ThreadLocal<Session>();
}
private HibernateUtil(){
}
public static Session getSession()throws HibernateException{
Session session = threadLocal.get();
if(session == null){
session = factory.openSession();
threadLocal.set(session);
}
return session;
}
public static void closeSession()throws HibernateException{
Session session = threadLocal.get();
if(session != null){
session.close();
}
threadLocal.set(null);
}
}
/index.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%@ page import="wintys.hibernate.onetomany.*"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
<%
List<User> users = null;
HibernateDAO dao = new HibernateDAOBean();
dao.insert();
users = dao.selectAll();
Iterator<User> it = users.iterator();
while(it.hasNext()){
User user = it.next();
String id = user.getId();
String name = user.getName();
out.println("id:" + id + "<br />");
out.println("name:" + name + "<br />");
out.println("cards:<br />");
Set<Card> cards = user.getCards();
Iterator<Card> itc = cards.iterator();
while(itc.hasNext()){
Card card = itc.next();
String cardId = card.getId();
float balance = card.getBalance();
out.println(" cardId:" + cardId + "<br />");
out.println(" balance:" + balance + "<br />");
}
out.println("<hr/>");
}
%>
</body>
</html>
5、運(yùn)行結(jié)果:
控制臺(tái)顯示:
......
Hibernate: insert into db.myuser (name, id) values (?, ?)
Hibernate: insert into db.mycard (balance, id) values (?, ?)
Hibernate: insert into db.mycard (balance, id) values (?, ?)
Hibernate: insert into db.mycard (balance, id) values (?, ?)
Hibernate: update db.mycard set userId=? where id=?
Hibernate: update db.mycard set userId=? where id=?
Hibernate: update db.mycard set userId=? where id=?
......
index.jsp頁(yè)面顯示:
id:402881e421d4d0be0121d4d20e140005
name:Tom
cards:
cardId:402881e421d4d0be0121d4d20e230008
balance:654.8
cardId:402881e421d4d0be0121d4d20e230006
balance:7641.96
cardId:402881e421d4d0be0121d4d20e230007
balance:3650.0

6、注意的問(wèn)題:
a、錯(cuò)誤提示:Field 'userId' doesn't have a default value。
一開始把"userId"設(shè)成NOT NULL,但是Hibernate先執(zhí)行的是:
"insert into db.mycard (balance, id) values (?, ?)"
然后才執(zhí)行"update db.mycard set userId=? where id=?",
而userId在insert時(shí)是沒(méi)有寫入值的,所以就會(huì)報(bào)錯(cuò)。把userId的NOT NULL去掉即可。
b、User.hbm.xml中要設(shè)置cascade="all",或其它有效值,不然,在保存User對(duì)象時(shí),相關(guān)的Card對(duì)象不會(huì)被保存。
c、User.hbm.xml中set標(biāo)簽的inverse屬性不能設(shè)置為"true",inverse的默認(rèn)值是"false",所以不加inverse也可以??磿险f(shuō):在一對(duì)多的關(guān)聯(lián)關(guān)系實(shí)現(xiàn)中,最好設(shè)置inverse="true",將有助于性能的改善。所以一開始就用了inverse="true",User和Card對(duì)象都分別正確寫入數(shù)據(jù)庫(kù)了,但是就是userId字段沒(méi)有被自動(dòng)寫入。
myuser表:
+--------------------------------------------+------+
| id | name |
+--------------------------------------------+------+
| 402881e421d4d0be0121d4d20e140005 | Tom |
+--------------------------------------------+------+
mycard表:
+--------------------------------------------+---------+---------
| id | balance | userId
+--------------------------------------------+---------+---------
| 402881e421d4d0be0121d4d20e230006 | 7641.96 | NULL
| 402881e421d4d0be0121d4d20e230007 | 3650.00 | NULL
|
| 402881e421d4d0be0121d4d20e230008 | 654.80 | NULL
+--------------------------------------------+---------+---------
搞了半天,原來(lái)在本例應(yīng)該把inverse設(shè)為false。inverse還是很有用的,只是用錯(cuò)了地方。
[參考資料]:
[1] Hibernate 一對(duì)多外鍵單向關(guān)聯(lián)--熔 巖 : http://lavasoft.blog.51cto.com/62575/39317
[2] Hibernate應(yīng)用(二)單向一對(duì)多及雙向一對(duì)多的配置: http://suhaoyuan.spaces.live.com/Blog/cns!2659D3AC8253C554!217.entry
[3] Hibernate單向一對(duì)多應(yīng)注意的問(wèn)題 : http://blog.chinaunix.net/u2/88320/showart_1716296.html
[4] hibernate一對(duì)多例子-已更新(二) : http://m.tkk7.com/wujun/archive/2008/08/04/39700.html
原創(chuàng)作品,轉(zhuǎn)載請(qǐng)注明出處。
作者:Winty (wintys@gmail.com)
博客:http://m.tkk7.com/wintys
posted on 2009-06-13 00:12
天堂露珠 閱讀(12838)
評(píng)論(4) 編輯 收藏 所屬分類:
Hibernate