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