一对多数据关联

一.单向一对多数据关联
一个用户有多个地址,在用户类TUser中包含地址类TAddress集合。

1.数据模型

2.表定义sql
use sample;

DROP TABLE T_Address;
DROP TABLE T_User;

CREATE TABLE T_User (
       id 
INT NOT NULL AUTO_INCREMENT
     , name 
VARCHAR(50)
     , age 
INT
     , 
PRIMARY KEY (id)
);

CREATE TABLE T_Address (
       id 
INT NOT NULL AUTO_INCREMENT
     , address 
VARCHAR(200)
     , zipcode 
VARCHAR(10)
     , tel 
VARCHAR(20)
     , type 
VARCHAR(20)
     , 
user_id INT NOT NULL
     , idx 
INT
     , 
PRIMARY KEY (id)
     , 
INDEX (user_id)
     , 
CONSTRAINT FK_T_Address_1 FOREIGN KEY (user_id)
                  
REFERENCES T_User (id)
);


3.POJO类
TUser.java
package cn.blogjava.start;

import java.util.Set;

public class TUser  implements java.io.Serializable {
    
// Fields    
     private Integer id;
     
private Integer age;
     
private String name;
     
private Set address;


    
// Constructors

    
public Integer getAge() {
        
return age;
    }

    
public void setAge(Integer age) {
        
this.age = age;
    }


    
public Set getAddress() {
        
return address;
    }

    
public void setAddress(Set address) {
        
this.address = address;
    }

    
/** default constructor */
    
public TUser() {
    }
    
    
/** constructor with id */
    
public TUser(Integer id) {
        
this.id = id;
    }

    
// Property accessors

    
public Integer getId() {
        
return this.id;
    }
    
    
public void setId(Integer id) {
        
this.id = id;
    }

    
public String getName() {
        
return this.name;
    }
    
    
public void setName(String name) {
        
this.name = name;
    }
}

TAddress.java
package cn.blogjava.start;

import java.io.Serializable;

public class TAddress implements Serializable {
    
    
private Integer id;
    
private String address;
    
private String zipcode;
    
private String tel;
    
private String type;
    
private Integer userId;
    
private Integer idx;
    
    
public Integer getId() {
        
return id;
    }
    
public void setId(Integer id) {
        
this.id = id;
    }
    
public String getAddress() {
        
return address;
    }
    
public void setAddress(String address) {
        
this.address = address;
    }
    
public Integer getIdx() {
        
return idx;
    }
    
public void setIdx(Integer idx) {
        
this.idx = idx;
    }
    
public String getTel() {
        
return tel;
    }
    
public void setTel(String tel) {
        
this.tel = tel;
    }
    
public String getType() {
        
return type;
    }
    
public void setType(String type) {
        
this.type = type;
    }
    
public Integer getUserId() {
        
return userId;
    }
    
public void setUserId(Integer userId) {
        
this.userId = userId;
    }
    
public String getZipcode() {
        
return zipcode;
    }
    
public void setZipcode(String zipcode) {
        
this.zipcode = zipcode;
    }

}

3.配置文件
TUser.hbm.xml
<?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>
    
<class name="cn.blogjava.start.TUser" table="T_User" catalog="sample"
     dynamic-update
="true" dynamic-insert="true"
    
>
        
<id name="id" type="integer">
            
<column name="id" />
            
<generator class="native" />
        
</id>
        
<property name="name" type="string" column="name" />
        
<property name="age" type="java.lang.Integer" column="age" />

        
<set name="address" table="t_address" cascade="all" order-by="zipcode asc">
            
<key column="user_id">
            
</key>
            
<one-to-many class="cn.blogjava.start.TAddress" />
        
</set>
    
</class>
</hibernate-mapping>

TAddress.hbm.xml
注意:没有配置user_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>
    
<class name="cn.blogjava.start.TAddress" table="T_Address" catalog="sample">
        
<id name="id" type="integer">
            
<column name="id" />
            
<generator class="native" />
        
</id>
        
<property name="address" type="string" column="address" />
        
<property name="zipcode" type="string" column="zipcode" />
        
<property name="tel" type="string" column="tel" />
        
<property name="type" type="string" column="type" />
        
<property name="idx" type="java.lang.Integer" column="idx" />
    
</class>
</hibernate-mapping>

4.测试代码

package cn.blogjava.start;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import junit.framework.Assert;
import junit.framework.TestCase;

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


public class HibernateTest extends TestCase {
    
    Session session 
= null;

    
protected void setUp() {
        
try {
            Configuration config 
= new Configuration().configure();
            SessionFactory sessionFactory 
= config.buildSessionFactory();
            session 
= sessionFactory.openSession();
            
        } 
catch (HibernateException e) {
            e.printStackTrace();
        }        
    }

    
protected void tearDown() {
        
try {
            session.close();        
        } 
catch (HibernateException e) {
            e.printStackTrace();
        }        
    }    
    
    
/**
     * 对象持久化测试(Insert方法)
     
*/        
    
public void testInsert() {
        Transaction tran 
= null;
        
try {
        
            TUser user 
= new TUser();
            user.setName(
"byf");
            user.setAge(
new Integer(26));
            
            TAddress addr 
= new TAddress();
            addr.setTel(
"1123");
            addr.setZipcode(
"233123");
            addr.setAddress(
"HongKong");
            
            TAddress addr2 
= new TAddress();
            addr2.setTel(
"139");
            addr2.setZipcode(
"116001");
            addr2.setAddress(
"dalian");            

            TAddress addr3 
= new TAddress();
            addr3.setTel(
"136");
            addr3.setZipcode(
"100080");
            addr3.setAddress(
"beijing");
            
            
//设置关联
            HashSet set = new HashSet();
            set.add(addr);
            set.add(addr2);
            set.add(addr3);
            user.setAddress(set);
                                   
            tran 
= session.beginTransaction();                                
            
//插入user信息
            session.save(user);
            session.flush();
            tran.commit();
            Assert.assertEquals(user.getId().intValue()
>0 ,true);
        } 
catch (HibernateException e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
            
if(tran != null) {
                
try {
                    tran.rollback();
                } 
catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        }
    }
    
    
/**
     * 对象读取测试(Select方法)
     
*/            
    
public void testSelect(){
        String hql 
= " from TUser where name='byf'";
        
try {
            List userList 
= session.createQuery(hql).list();
            TUser user 
= (TUser)userList.get(0);
            System.out.println(
"user name is " + user.getName());
            
            
for (Iterator iter = user.getAddress().iterator(); iter.hasNext();) {
                TAddress addr 
= (TAddress) iter.next();
                System.out.println(
"user address is " + addr.getAddress());                
            }
            Assert.assertEquals(user.getName(), 
"byf");
        } 
catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
        }
    }
}

说明:
一个问题,由于是单向关联,为了保持关联关系,我们只能通过主控方对被动方进行级联更新。如果被关联方的字段为NOT NULL属性,当Hibernate创建或者更新关联关系时,可能出现约束违例。
例子中T_Address表中的user_id 为NOT NULL,如果在TAddress.hbm.xml映射了全部字段时。创建一个用户并赋予她地址信息,对于T_Address表而言,hibernate会执行两条sql语句来保存地址信息。

要执行两条SQL语句,是因为关联是单向的,就是说对于TAddress对象而言,并不知道自己应该与那一个TUser对象关联,只能先将user_id设为一个空值。
之后,根据配置文件
        <set name="address" table="t_address" cascade="all" order-by="zipcode asc">
            
<key column="user_id">
            
</key>
            
<one-to-many class="cn.blogjava.start.TAddress" />
        
</set>
由TUser对象将自身的id赋给addr.user_id,这样导致addr属性值变动,在事物提交的时候,会进行update。

1)当save该用户的时候,
insert into t_address  (user_id, address, zipcode, tel) value (null, "HongKong", "233123", "1123")

2)当tx.commit()时:
update t_address user_id="1", address="HongKong", zipcode="233123",tel="1123" where id=2;

这样,在save user时,就会出现约束违例。

调整方法:
可以在定义数据表字段时候,不加NOT NULL约束。或者在开始为user_id随意赋一个非空值(因为还要update,不正确也没关系),或者将user_id字段从TAddress.hbm.xml中删除(本例就是这样实现)。

但是这些都是权宜之计,用两条SQL语句完成一次数据库操作,性能低下。
而双向一对多解决了这个问题。
下面来实现双向关联:
修改配置文件
TUser.hbm.xml
<?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>
    
<class name="cn.blogjava.start.TUser" table="T_User" catalog="sample"
     dynamic-update
="true" dynamic-insert="true"
    
>
        
<id name="id" type="integer">
            
<column name="id" />
            
<generator class="native" />
        
</id>
        
<property name="name" type="string" column="name" />
        
<property name="age" type="java.lang.Integer" column="age" />

        
<set 
            
name="address" 
            table
="t_address" 
            inverse
="true"
            cascade
="all" 
            order-by
="zipcode asc"
            
>
            
<key column="user_id">
            
</key>
            
<one-to-many class="cn.blogjava.start.TAddress" />
        
</set>
    
</class>
</hibernate-mapping>

设定inverse="true",表明将TUser类作为被动类,将数据关联的维护工作交给关联对象TAddress来管理。
在one-to-many模型中,将many一方设为主控方有助于性能的改善。(让总理记住每个人困难,但是每个人记住总理方便)

TAddress.hbm.xml
<?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>
    
<class name="cn.blogjava.start.TAddress" table="T_Address" catalog="sample">
        
<id name="id" type="integer">
            
<column name="id" />
            
<generator class="native" />
        
</id>
        
<property name="address" type="string" column="address" />
        
<property name="zipcode" type="string" column="zipcode" />
        
<property name="tel" type="string" column="tel" />
        
<property name="type" type="string" column="type" />
        
<property name="idx" type="java.lang.Integer" column="idx" />
        
<many-to-one
                  
name="user" 
                  class
="cn.blogjava.start.TUser"

                  cascade
="none"
                  outer-join
="auto"
                  update
="true"                  
                  insert
="true"

                  access
="property"
                  column
="user_id"
                  not-null
="true"
        
/>
    
</class>
</hibernate-mapping>

2.对TAddress.java做如下改造:
去掉user_id字段,增加user字段,和getter,setter方法。
package cn.blogjava.start;

import java.io.Serializable;

public class TAddress implements Serializable {
    
    
private Integer id;
    
private String address;
    
private String zipcode;
    
private String tel;
    
private String type;
    
private Integer idx;
    
private TUser user;
    
    
public TUser getUser() {
        
return user;
    }
    
public void setUser(TUser user) {
        
this.user = user;
    }
    
public Integer getId() {
        
return id;
    }
    
public void setId(Integer id) {
        
this.id = id;
    }
    
public String getAddress() {
        
return address;
    }
    
public void setAddress(String address) {
        
this.address = address;
    }
    
public Integer getIdx() {
        
return idx;
    }
    
public void setIdx(Integer idx) {
        
this.idx = idx;
    }
    
public String getTel() {
        
return tel;
    }
    
public void setTel(String tel) {
        
this.tel = tel;
    }
    
public String getType() {
        
return type;
    }
    
public void setType(String type) {
        
this.type = type;
    }
    
public String getZipcode() {
        
return zipcode;
    }
    
public void setZipcode(String zipcode) {
        
this.zipcode = zipcode;
    }

}

4.测试代码
既然TUser不维护关联关系,需要TAddress需要自己来维护TUser,所以需要addr.setUser(user);
package cn.blogjava.start;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import junit.framework.Assert;
import junit.framework.TestCase;

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


public class HibernateTest extends TestCase {
    
    Session session 
= null;

    
protected void setUp() {
        
try {
            Configuration config 
= new Configuration().configure();
            SessionFactory sessionFactory 
= config.buildSessionFactory();
            session 
= sessionFactory.openSession();
            
        }
 catch (HibernateException e) {
            e.printStackTrace();
        }
        
    }


    
protected void tearDown() {
        
try {
            session.close();        
        }
 catch (HibernateException e) {
            e.printStackTrace();
        }
        
    }
    
    
    
/**
     * 对象持久化测试(Insert方法)
     
*/
        
    
public void testInsert() {
        Transaction tran 
= null;
        
try {
        
            TUser user 
= new TUser();
            user.setName(
"byf");
            user.setAge(
new Integer(26));
            
            TAddress addr 
= new TAddress();
            addr.setTel(
"1123");
            addr.setZipcode(
"233123");
            addr.setAddress(
"HongKong");
            addr.setUser(user);
            
            TAddress addr2 
= new TAddress();
            addr2.setTel(
"139");
            addr2.setZipcode(
"116001");
            addr2.setAddress(
"dalian");       
            addr2.setUser(user);

            TAddress addr3 
= new TAddress();
            addr3.setTel(
"136");
            addr3.setZipcode(
"100080");
            addr3.setAddress(
"beijing");
            addr3.setUser(user);
            
            
//设置关联
            HashSet set = new HashSet();
            set.add(addr);
            set.add(addr2);
            set.add(addr3);
            user.setAddress(set);
                                   
            tran 
= session.beginTransaction();                                
            
//插入user信息
            session.save(user);
            session.flush();
            tran.commit();
            Assert.assertEquals(user.getId().intValue()
>0 ,true);
        }
 catch (HibernateException e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
            
if(tran != null{
                
try {
                    tran.rollback();
                }
 catch (Exception e1) {
                    e1.printStackTrace();
                }

            }

        }

    }

    
    
/**
     * 对象读取测试(Select方法)
     
*/
            
    
public void testSelect(){
        String hql 
= " from TUser where name='byf'";
        
try {
            List userList 
= session.createQuery(hql).list();
            TUser user 
= (TUser)userList.get(0);
            System.out.println(
"user name is " + user.getName());
            
            
for (Iterator iter = user.getAddress().iterator(); iter.hasNext();) {
                TAddress addr 
= (TAddress) iter.next();
                System.out.println(
"user address is " + addr.getAddress());                
            }

            Assert.assertEquals(user.getName(), 
"byf");
        }
 catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
        }

    }

}
posted on 2006-07-05 15:13 knowhow 阅读(549) 评论(1)  编辑  收藏 所属分类: ORM:Hibernate及其他

只有注册用户登录后才能发表评论。


网站导航: