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”的情况下的结果