一对多数据关联
一.单向一对多数据关联
一个用户有多个地址,在用户类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());
}
}
}