您的位置:首页 > 其它

hibernate一对多 多对一 的 双向关联

2014-08-20 16:28 393 查看
hibernate中 听说一对多 还有多对一 用的比较多,于是作为菜鸟的我 准备先从这个开始下刀学习-。-

下面是我看到的一篇很好的文章 讲述的很清晰很好,尤其是对生成的sql做了对比,设计到 hibernate以后的优化,要用好hibernate就要从这里开始=、=

双向关联:



以上多对一,一对多的关系都是单向关联,也就是一方联系到另一方,而另一方不知道自己被关联。



cascade:设置级联

sava-update:级联保存、更新

delete:级联删除

none:不级联,默认值

all:级联保存、更新、删除





inverse:在映射一对多关系时,一般将该属性设置为true,表示表间的关联关系由一方设置,减少update语句,提高性能。

如果上方都意识到另一方的存在,则形成双向关联。现将上面的代码改写如下:



在User类中追加Room类型的字段:



package com.hb3.pack_20.model;



public class User {



private Integer id;

private String name;

private Room room;



public Room getRoom() {

return room;

}



public void setRoom(Room room) {

this.room = room;

}



public User() {

}



public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

User.hbm.xml中也同样追加关于Room的信息:



<?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">



<hibernate-mapping>



<class name="com.hb3.pack_20.model.User" table="user">



<id name="id" column="id" type="java.lang.Integer">

<generator class="native"/>

</id>



<property name="name" column="name" type="java.lang.String"/>



<many-to-one name="room"

column="room_id"

class="com.hb3.pack_20.model.Room"

cascade="save-update"

outer-join="true"/>



</class>



</hibernate-mapping>

这里我们将Room.hbm.xml文件里set的cascade属性也设为save-update:



<?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">



<hibernate-mapping>



<class name="com.hb3.pack_20.model.Room" table="room">



<id name="id" column="id">

<generator class="native"/>

</id>



<property name="address"

column="address"

type="java.lang.String"/>



<set name="users" table="user" cascade="save-update">

<key column="room_id"/>

<one-to-many class="com.hb3.pack_20.model.User"/>

</set>



</class>

</hibernate-mapping>

这样我们可以用多对一的方式来维持管理:



package com.hb3.pack_20;



import org.hibernate.Session;

import org.hibernate.SessionFactory;

import org.hibernate.Transaction;

import org.hibernate.cfg.Configuration;



import com.hb3.pack_20.model.Room;

import com.hb3.pack_20.model.User;



public class BusinessService {



public static void main(String[] args) {



Configuration config = new Configuration().configure();

SessionFactory sessionFactory = config.buildSessionFactory();

Session session = sessionFactory.openSession();





User user1 = new User();

user1.setName("chenyan");



User user2 = new User();

user2.setName("shenbin");



Room room1 = new Room();

room1.setAddress("NTU-M8-419");



user1.setRoom(room1);

user2.setRoom(room1);



Transaction tx = session.beginTransaction();

session.save(user1);

session.save(user2);

tx.commit();





session.close();

sessionFactory.close();

}

}

或者反过来由一对多的方式来维持关系:



package com.hb3.pack_20;



import java.util.HashSet;



import org.hibernate.Session;

import org.hibernate.SessionFactory;

import org.hibernate.Transaction;

import org.hibernate.cfg.Configuration;



import com.hb3.pack_20.model.Room;

import com.hb3.pack_20.model.User;



public class BusinessService {



public static void main(String[] args) {



Configuration config = new Configuration().configure();

SessionFactory sessionFactory = config.buildSessionFactory();

Session session = sessionFactory.openSession();





User user1 = new User();

user1.setName("chenyan");



User user2 = new User();

user2.setName("shenbin");



Room room1 = new Room();

room1.setUsers(new HashSet<User>());

room1.setAddress("NTU-M8-419");

room1.addUser(user1);

room1.addUser(user2);



Transaction tx = session.beginTransaction();

session.save(room1);

tx.commit();





session.close();

sessionFactory.close();

}

}

但是一对多的方式来维持的话,先看一下生成的SQL文:



Hibernate: insert into room (address) values (?)

Hibernate: insert into user (name, room_id) values (?, ?)

Hibernate: insert into user (name, room_id) values (?, ?)

Hibernate: update user set room_id=? where id=?

Hibernate: update user set room_id=? where id=?







可见,如果把一的一方Room作为主控方,多的一方User因为不知道Room的room_id是多少,所以必须等Room和User存储之后再更新room_id。所以在多对一,一对多形成双向关联的时候,应该把控制权交给多的一方,这样比较有效率。理由很简单,就像在公司里一样,老板记住所有员工的名字来得快,还是每个员工记住老板的名字来得快。







基于这个理由,我们对Room.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">



<hibernate-mapping>



<class name="com.hb3.pack_20.model.Room" table="room">



<id name="id" column="id">

<generator class="native"/>

</id>



<property name="address"

column="address"

type="java.lang.String"/>



<set name="users" table="user" cascade="save-update" inverse="true">

<key column="room_id"/>

<one-to-many class="com.hb3.pack_20.model.User"/>

</set>



</class>

</hibernate-mapping>

如此控制权就交给了多的一方。当直接存储一的一方前,必须让多的一方意识的一的一方的存在。



package com.hb3.pack_20;



import java.io.IOException;

import java.sql.SQLException;

import java.util.HashSet;



import org.hibernate.Session;

import org.hibernate.SessionFactory;

import org.hibernate.Transaction;

import org.hibernate.cfg.Configuration;



import com.hb3.pack_20.model.Room;

import com.hb3.pack_20.model.User;



public class BusinessService {



public static void main(String[] args) throws IOException, SQLException {



Configuration config = new Configuration().configure();

SessionFactory sessionFactory = config.buildSessionFactory();

Session session = sessionFactory.openSession();





User user1 = new User();

user1.setName("bush");



User user2 = new User();

user2.setName("caterpillar");



Room room1 = new Room();

room1.setUsers(new HashSet<User>());

room1.setAddress("NTU-M8-419");

room1.addUser(user1);

room1.addUser(user2);



//多方必须认识到单方的存在

user1.setRoom(room1);

user2.setRoom(room1);



Transaction tx = session.beginTransaction();

session.save(room1);

tx.commit();





session.close();

sessionFactory.close();

}

}

此时生成的SQL文为:



Hibernate: insert into room (address) values (?)

Hibernate: insert into user (name, room_id) values (?, ?)

Hibernate: insert into user (name, room_id) values (?, ?)



从而提高了效率。







如果把代码中user1.setRoom(room1);和user2.setRoom(room1);这2行移去,你会发现数据库中room_id的值为null。这个结果就好比在多对一的关系中没有分配给User一个Room,那么理所当然room_id的值为null了。







HIBERNATE一对多配置实例

1. 环境:假定班级和学生是一对多的关系,班级是一,学生是多,对应表格分别是:zlass ,student

2. 创建Zlass和Student对象

//Zlass对象---------------------------------------------------------

public class Zlass{



private String class_id;

private String class_name;

private java.util.Set students;



public void setId(String id){

this.class_id = id;

}



public void setClassName(String className){

this.class_name = className;

}



public void setStudents(java.util.Set students){

this.students = students;

}



public String getId(){

return class_id;

}



public String getClassName(){

return class_name;

}



public java.util.Set getStudents(){

return students;

}



}



//学生对象

public class Student{

private String student_id;

private String name;

private Address address;

private java.util.Set events;

private java.util.Set lessons;

private Zlass zlass;



public Zlass getZlass(){

return zlass;

}



public String getStudentId(){

return student_id;

}



public String getName(){

return name;

}



public Address getAddress(){

return address;

}



public java.util.Set getEvents(){

return events;

}



public java.util.Set getLessons(){

return lessons;

}



public void setZlass(Zlass zlass){

this.zlass = zlass;

}



public void setStudentId(String studentId){

this.student_id = studentId;

}



public void setName(String name){

this.name = name;

}



public void setAddress(Address address){

this.address = address;

}



public void setEvents(java.util.Set events){

this.events =events;

}



public void setLessons(java.util.Set lessons){

this.lessons = lessons;

}



}





//配置文件

//----------Zlass.hbm.xml---------------

<hibernate-mapping package="com.softWork.school">

<class name="Zlass" table="class">

<id name="id" column="id" type="string" length="20">

<generator class="assigned"/>

</id>

<property name="className" column="class_name" type="string" length="200"/>



<set name="students" inverse="false" cascade="all">

<key column="class_id"/>

<one-to-many class="Student"/>

</set>

</class>

</hibernate-mapping>

//-------------Student.hbm.xml---------------

<hibernate-mapping package="com.softWork.school">

<class name="Student" table="student">

<id name="studentId" column="student_id" type="string" length="20">

<generator class="assigned"/>

</id>

<property name="name" type="string" length="20"/>

<component name="address" class="Address">

<property name="state" column="state" type="string"></property>

<property name="city" column="city" type="string"></property>

<property name="street" column="street" type="string"></property>

</component>

<set name="events" inverse="false" cascade="all">

<key column="student_id"></key>

<one-to-many class="Event"></one-to-many>

</set>



<set name="lessons" table="student_lesson">

<key column="student_id"/>

<many-to-many class="Lesson" column="lesson_id" />

</set>

<many-to-one name="zlass" column="class_id" class="Zlass"/>

</class>

</hibernate-mapping>



以上使用的是班级一端维护关系,并级连操作



3. 使用级连操作数据

1) 新增班级

Zlass zlass = new Zlass();

zlass.setId("971002");

zlass.setClassName("机制97-1班");

session.saveOrUpdate(zlass);

2) 为班级新增学生

主动端操作:

Zlass zlass = (Zlass)session.load(Zlass.class,"971002");

Student student = new Student();

student.setStudentId("005");

student.setName("没名");

zlass.getStudents().add(student);

session.saveOrUpdate(zlass);

被动端操作:

Zlass zlass = (Zlass)session.load(Zlass.class,"971002");

Student student = new Student();

student.setStudentId("006");

student.setName("006");

student.setZlass(zlass);

session.saveOrUpdate(student);

3) 删除学生资料

主动端操作:

主动端除非删除自己,并设置了级连才能删除子对象,否则无法完成

//-----以下代码将只删除两者之间的关系,即将学生的class_id设置为null-----

Zlass zlass = (Zlass)session.load(Zlass.class,"971001");

java.util.Iterator iterator = zlass.getStudents().iterator();

if (iterator.hasNext())

zlass.getStudents().remove(iterator.next());

session.saveOrUpdate(zlass);

被动端操作:

Student student = (Student)session.load(Student.class,"002");

session.delete(student);

4) 修改学生资料

通过班级修改学生资料



Zlass zlass = (Zlass)session.load(Zlass.class,"971002");

java.util.Iterator iterator = zlass.getStudents().iterator();

if (iterator.hasNext()){

Student student = (Student)iterator.next();

student.setName("名字已修改");

}

session.saveOrUpdate(zlass);



读取返回的Set型数据:

java.util.Set set = student.getEvents();

java.util.Iterator iterator = set.iterator();

while(iterator.hasNext()){

evt = (Event)iterator.next();

System.out.println(evt.getContent());

System.out.println(evt.getDate().toString());

}



4. 注意:

如果需要从多放引导到一方,需要在一方配置文件中设置inverse=”true”参数,以设定一方到多方的设定是逆向映射,对关联的逆向端所做的修改将不会被持久化。

Inverse=”true”的表示两个实体的关系由对方去维护。

5. 推荐配置,一般的将”一”这一端设置为inverse=”false”,cascade=”all” ,这样一般的操作只需要在”一”这一端操作,此适合于标志性一对多的情况,如销售单和销售明细

如果是非标志性一对多,则一般的将inverse=”false”,cascade=”none”,这样关系永远在主动一端进行控制

示范:假如A对B是一对多,当A中原来有B1,B2,B3,当A中的集合中只包含B1,B2时,那么B中B3的外码将被设置为NULL,这个是CASCADE=“FALSE”的情况下的结果
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: