? Hibernate关系映射 收藏
???? 总结下实体与实体之间的多对一、一对多、一对一、多对多如何与Java对象之间进行映像,采用Oracle。
1、多对一
例如在学校寝室中,学生与寝室的关系就是多对一的关系,多个学生可以居住于一个寝室。
//创建一个表空间
create tablespace allenspace datafile 'c:\allenspace' size 15M autoExtend on next
10M permanent online;
//创建表user1
create table user1(id int primary key,name varchar2(20)default '' not null,roomid int)tablespace allenspace;
//创建表room
create table room(id int primary key,address varchar2(50)default '' not null)tablespace allenspace;
?
User.java和Room.java类
User类中有一room属性,将参考至Room实例,多个User实例可共同参考一个Room实例。
?
package cn.jjm.hibernate;
public class User {
??? private int id;
??? private String name;
??? private Room room;
????
??? public User(){
????????
??? }
??? public int getId() {
??????? return id;
??? }
??? public void setId(int id) {
??????? this.id = id;
??? }
??? public String getName() {
??????? return name;
??? }
??? public void setName(String name) {
??????? this.name = name;
??? }
??? public Room getRoom() {
??????? return room;
??? }
??? public void setRoom(Room room) {
??????? this.room = room;
??? }???
}
package cn.jjm.hibernate;
public class Room {
??? private int id;
??? private String address;
????
??? public Room(){
????????
??? }
????
??? public String getAddress() {
??????? return address;
??? }
??? public void setAddress(String address) {
??????? this.address = address;
??? }
??? public int getId() {
??????? return id;
??? }
??? public void setId(int id) {
??????? this.id = id;
??? }
}
映射文件。。。。
Room.hbm.xml
<hibernate-mapping>
??? <class name="cn.jjm.hibernate.Room" table="ROOM" schema="SYS">
??????? <id name="id" type="java.lang.Integer">
??????????? <column name="ID" precision="22" scale="0" />
??????????? <generator class="assigned" />
??????? </id>
??????? <property name="address" type="java.lang.String">
??????????? <column name="ADDRESS" length="50" not-null="true" />
??????? </property>
????????
??? </class>
</hibernate-mapping>
User.hbm.xml
<hibernate-mapping>
??? <class name="cn.jjm.hibernate.User" table="USER1" schema="SYS">
??????? <id name="id" type="java.lang.Integer">
??????????? <column name="ID" precision="22" scale="0" />
??????????? <generator class="assigned" />
??????? </id>
??????? <property name="name" type="java.lang.String">
??????????? <column name="NAME" length="20" not-null="true" />
??????? </property>
????????
??????? <many-to-one name="room"?
???????????????????? column="ROOMID"?
???????????????????? class="cn.jjm.hibernate.Room"
???????????????????? cascade="all"
???????????????????? outer-join="true"/>?????????
??? </class>
</hibernate-mapping>
在<many-to-one>的设定中,cascade表示主控方(User)进行save-update、delete等相关操作时,被控方(Room)是否也一并进行相关操作,简单的说,也就是您储存或更新User实例时,当中的Room实例是否一并对数据库发生储存或操作,设定为 all,表示主控方任何操作,被控方也进行对应操作。
存储测试:
??
?? Room room1 = new Room();?
?? room1.setId(1001);
?? room1.setAddress("NTU-M8-419");?
?? Room room2 = new Room();?
?? room2.setId(1002);
?? room2.setAddress("NTU-G3-302");?
?? User user1 = new User();
?? user1.setId(101);
?? user1.setName("allen");?
?? user1.setRoom(room1);?
?? User user2 = new User();
?? user2.setId(102);
?? user2.setName("candy");?
?? user2.setRoom(room1);?
?? User user3 = new User();
?? user3.setId(103);
?? user3.setName("momor");?
?? user3.setRoom(room2);
?? session.save(user1);
?? session.save(user2);
?? session.save(user3);
?
2、一对多
在多对一中,User对Room是多对一的关系,User实例维护着对Room实例的参考,如果将这个关系反过来,由Room实例维护对多个User实例的数据,就是一对多的关系。
?
User.java和Room.java类
package cn.jjm.hibernate;
public class User {
??? private int id;
??? private String name;
????
??? public User(){
????????
??? }
??? public int getId() {
??????? return id;
??? }
??? public void setId(int id) {
??????? this.id = id;
??? }
??? public String getName() {
??????? return name;
??? }
??? public void setName(String name) {
??????? this.name = name;
??? }
}
package cn.jjm.hibernate;
import java.util.Set;
public class Room {
??? private int id;
??? private String address;
??? private Set users;
????
??? public Room(){
????????
??? }
??? public String getAddress() {
??????? return address;
??? }
??? public void setAddress(String address) {
??????? this.address = address;
??? }
??? public int getId() {
??????? return id;
??? }
??? public void setId(int id) {
??????? this.id = id;
??? }
??? public Set getUsers() {
??????? return users;
??? }
??? public void setUsers(Set users) {
??????? this.users = users;
??? }
??? //添加User对象
??? public void addUser(User user){
??????? users.add(user);
??? }
??? //删除User对象
??? public void removeUser(User user){
??????? users.remove(user);
??? }
}
这种方式即所谓单向一对多关系,也就是Room实例知道User实例的存在,而User实例则没有意识到Room实例。
(在多对一中,则是单向多对一关系,即User知道Room的存在,但Room不知道User的存在。)
?
映射文件。。。
User.hbm.xml
<hibernate-mapping>
??? <class name="cn.jjm.hibernate.User" table="USER1" schema="SYS">
??????? <id name="id" type="java.lang.Integer">
??????????? <column name="ID" precision="22" scale="0" />
??????????? <generator class="assigned" />
??????? </id>
??????? <property name="name" type="java.lang.String">
??????????? <column name="NAME" length="20" not-null="true" />
??????? </property>
??? </class>
</hibernate-mapping>
Room.hbm.xml
<hibernate-mapping>
??? <class name="cn.jjm.hibernate.Room" table="ROOM" schema="SYS">
??????? <id name="id" type="java.lang.Integer">
??????????? <column name="ID" precision="22" scale="0" />
??????????? <generator class="assigned" />
??????? </id>
??????? <property name="address" type="java.lang.String">
??????????? <column name="ADDRESS" length="50" not-null="true" />
??????? </property>
????????
??????? <set name="users" table="user1" cascade="all">
??????????? <key column="ROOMID"/>
??????????? <one-to-many class="cn.jjm.hibernate.User"/>
??????? </set>??????
??? </class>
</hibernate-mapping>
存储测试。。。
User user1 = new User();
user1.setId(104);
user1.setName("张无忌");?
User user2 = new User();
user2.setId(105);
user2.setName("周芷若");?
User user3 = new User();?
user3.setId(106);
user3.setName("张三丰");?
Room room1 = new Room();?
room1.setId(1003);
room1.setUsers(new HashSet());
room1.setAddress("NTU-M8-345");
room1.addUser(user1);
room1.addUser(user2);
Room room2 = new Room();
room2.setId(1004);
room2.setUsers(new HashSet());
room2.setAddress("NTU-G3-322");
room2.addUser(user3);
session.save(room1);
session.save(room2)
3、双向关联(inverse)
在多对一、一对多中都是单向关联,也就是其中一方关联到另一方,而另一方不知道自己被关联。
如果让双方都意识到另一方的存在,这就形成了双向关联。
User.java和Room.java类
package cn.jjm.hibernate;
public class User {
??? private int id;
??? private String name;
??? private Room room;
????
??? public User(){
????????
??? }
??? public int getId() {
??????? return id;
??? }
??? public void setId(int id) {
??????? this.id = id;
??? }
??? public String getName() {
??????? return name;
??? }
??? public void setName(String name) {
??????? this.name = name;
??? }
??? public Room getRoom() {
??????? return room;
??? }
??? public void setRoom(Room room) {
??????? this.room = room;
??? }???
}
package cn.jjm.hibernate;
import java.util.Set;
public class Room {
??? private int id;
??? private String address;
??? private Set users;
????
??? public Room(){
????????
??? }
??? public String getAddress() {
??????? return address;
??? }
??? public void setAddress(String address) {
??????? this.address = address;
??? }
??? public int getId() {
??????? return id;
??? }
??? public void setId(int id) {
??????? this.id = id;
??? }
??? public Set getUsers() {
??????? return users;
??? }
??? public void setUsers(Set users) {
??????? this.users = users;
??? }
??? //添加User对象
??? public void addUser(User user){
??????? users.add(user);
??? }
??? //删除User对象
??? public void removeUser(User user){
??????? users.remove(user);
??? }
}
如此,User实例可参考至Room实例而维持多对一关系,而Room实例记得User实例而维持一对多关系。
映射文件。。。
User.hbm.xml
<hibernate-mapping>
??? <class name="cn.jjm.hibernate.User" table="USER1" schema="SYS">
??????? <id name="id" type="java.lang.Integer">
??????????? <column name="ID" precision="22" scale="0" />
??????????? <generator class="assigned" />
??????? </id>
??????? <property name="name" type="java.lang.String">
??????????? <column name="NAME" length="20" not-null="true" />
??????? </property>??
??????? <many-to-one name="room"?
???????????????????? column="ROOMID"?
???????????????????? class="cn.jjm.hibernate.Room"
???????????????????? cascade="save-update"
???????????????????? outer-join="true"/>?????????
??? </class>
</hibernate-mapping>
Room.hbm.xml
<hibernate-mapping>
??? <class name="cn.jjm.hibernate.Room" table="ROOM" schema="SYS">
??????? <id name="id" type="java.lang.Integer">
??????????? <column name="ID" precision="22" scale="0" />
??????????? <generator class="assigned" />
??????? </id>
??????? <property name="address" type="java.lang.String">
??????????? <column name="ADDRESS" length="50" not-null="true" />
??????? </property>
??????? <set name="users" table="user1" cascade="save-update">
??????????? <key column="ROOMID"/>
??????????? <one-to-many class="cn.jjm.hibernate.User"/>
??????? </set>??????
??? </class>
</hibernate-mapping>
映像文件双方都设定了cascade为save-update,所以您可以用多对一的方式来维持关联:
User user1 = new User();
user1.setId(104);
user1.setName("张无忌");?
User user2 = new User();
user2.setId(105);
user2.setName("周芷若");?
Room room1 = new Room();?
room1.setId(1003);
room1.setAddress("NTU-M8-345");
user1.setRoom(room1);
user2.setRoom(room1);
session.save(user1);
session.save(user2);
或者反过来由一对多的方式来维持关联:
User user1 = new User();
user1.setId(104);
user1.setName("张无忌");?
User user2 = new User();
user2.setId(105);
user2.setName("周芷若");?
Room room1 = new Room();?
room1.setId(1003);
room1.setUsers(new HashSet());
room1.setAddress("NTU-M8-345");
room1.addUser(user1);
room1.addUser(user2);
session.save(room1);
在一对多、多对一形成双向关联的情况下,可以将关联维持的控制权交给多的一方,这样会比较有效率,理由不难理解,就像是领导记住下属员工的姓名快,还是所有员工记住部门领导的姓名快。
?
以在一对多、多对一形成双向关联的情况下,可以在「一」的一方设定控制权反转,也就是当储存「一」的一方时,将关联维持的控制权交给「多」的一方。来看Room.hbm.xml
?
<hibernate-mapping>
??? <class name="cn.jjm.hibernate.Room" table="ROOM" schema="SYS">
??????? <id name="id" type="java.lang.Integer">
??????????? <column name="ID" precision="22" scale="0" />
??????????? <generator class="assigned" />
??????? </id>
??????? <property name="address" type="java.lang.String">
??????????? <column name="ADDRESS" length="50" not-null="true" />
??????? </property>
??????? <set name="users" table="user1" cascade="save-update" inverse="true">
??????????? <key column="ROOMID"/>
??????????? <one-to-many class="cn.jjm.hibernate.User"/>
??????? </set>??????
??? </class>
</hibernate-mapping>
由于关联的控制权交给「多」的一方了,所以直接储存「一」方前,「多」的一方必须意识到「一」的存在,所以..
User user1 = new User();
user1.setId(104);
user1.setName("张无忌");?
User user2 = new User();
user2.setId(105);
user2.setName("周芷若");?
Room room1 = new Room();?
room1.setId(1003);
room1.setAddress("NTU-M8-345");
user1.setRoom(room1);
user2.setRoom(room1);
session.save(room1);
4、一对一 唯一外键关联
每一个User配给一间Room,形成一对一。看类。。。
package cn.jjm.hibernate;
public class User {
??? private int id;
??? private String name;
??? private Room room;
????
??? public User(){
????????
??? }
??? public int getId() {
??????? return id;
??? }
??? public void setId(int id) {
??????? this.id = id;
??? }
??? public String getName() {
??????? return name;
??? }
??? public void setName(String name) {
??????? this.name = name;
??? }
??? public Room getRoom() {
??????? return room;
??? }
??? public void setRoom(Room room) {
??????? this.room = room;
??? }???
}
package cn.jjm.hibernate;
import java.util.Set;
public class Room {
??? private int id;
??? private String address;
??? private Set users;
????
??? public Room(){
????????
??? }
??? public String getAddress() {
??????? return address;
??? }
??? public void setAddress(String address) {
??????? this.address = address;
??? }
??? public int getId() {
??????? return id;
??? }
??? public void setId(int id) {
??????? this.id = id;
??? }
??? public Set getUsers() {
??????? return users;
??? }
??? public void setUsers(Set users) {
??????? this.users = users;
??? }
??? //添加User对象?
??? public void addUser(User user){
??????? users.add(user);
??? }
??? //删除User对象?
??? public void removeUser(User user){
??????? users.remove(user);
??? }
}
用外键来完成一对一,其实就是限制多对一关系中,「多」的一方只能有一个参考至「一」的一方,也就是多对一关系的一个特例,这可以在映像文件中使用<many-to-one>标签时,加上"unique"属性来设定。
User.hbm.xml
<hibernate-mapping>
??? <class name="cn.jjm.hibernate.User" table="USER1" schema="SYS">
??????? <id name="id" type="java.lang.Integer">
??????????? <column name="ID" precision="22" scale="0" />
??????????? <generator class="assigned" />
??????? </id>
??????? <property name="name" type="java.lang.String">
??????????? <column name="NAME" length="20" not-null="true" />
??????? </property>
????????
??????? <many-to-one name="room"?
???????????????????? column="ROOMID"?
???????????????????? class="cn.jjm.hibernate.Room"
???????????????????? cascade="all"
???????????????????? outer-join="true"
???????????????????? unique="true"/>?????????
??? </class>
</hibernate-mapping>
如果要再完成双向一对一的关系,则可以在Room.hbm.xml中使用<one-to-one>标签来定义...
<hibernate-mapping>
??? <class name="cn.jjm.hibernate.Room" table="ROOM" schema="SYS">
??????? <id name="id" type="java.lang.Integer">
??????????? <column name="ID" precision="22" scale="0" />
??????????? <generator class="assigned" />
??????? </id>
??????? <property name="address" type="java.lang.String">
??????????? <column name="ADDRESS" length="50" not-null="true" />
??????? </property>
??????? <one-to-one name="user"
??????????????????? class="cn.jjm.hibernate.User"
??????????????????? property-ref="room"/>
??? </class>
</hibernate-mapping>
在<one-to-one>中,property-ref告诉Hibernate,查询出user并将其参考至room。
存储测试...
User user1 = new User();
user1.setId(104);
user1.setName("张无忌");?
Room room1 = new Room();?
room1.setId(1003);
room1.setAddress("NTU-M8-345");
user1.setRoom(room1);
User user2 = new User();
user2.setId(105);
user2.setName("周芷若");?
Room room2 = new Room();
room2.setId(1004);
room2.setAddress("NTU-G3-322");
user2.setRoom(room2);
session.save(user1);
session.save(user2);
5、一对一 主键关联
一对一关联的另一种方式,是限制两个实体的主键必须一致,如此直接透过两个表格的主键就可确定一对一关联,而不用额外的外键参考。
User类别与Room类别的设计使用一对一(唯一外键关联)中的设计即可。
User.hbm.xml
<hibernate-mapping>
??? <class name="cn.jjm.hibernate.User" table="USER1" schema="SYS">
??????? <id name="id" type="java.lang.Integer">
??????????? <column name="ID" precision="22" scale="0" />
??????????? <generator class="assigned" />
??????? </id>
??????? <property name="name" type="java.lang.String">
??????????? <column name="NAME" length="20" not-null="true" />
??????? </property>
????????
??????? <one-to-one name="room"?
???????????????????? column="ROOMID"?
???????????????????? class="cn.jjm.hibernate.Room"
???????????????????? cascade="all"/>?????????
??? </class>
</hibernate-mapping>
Room.hbm.xml
<hibernate-mapping>
??? <class name="cn.jjm.hibernate.Room" table="ROOM" schema="SYS">
??????? <id name="id" type="java.lang.Integer">
??????????? <generator class="foreign">?
??????????????? <param name="property">user</param>
??????????? </generator>
??????? </id>
??????? <property name="address" type="java.lang.String">
??????????? <column name="ADDRESS" length="50" not-null="true" />
??????? </property>
??????? <one-to-one name="user"
??????????????????? class="cn.jjm.hibernate.User"
??????????????????? constrained="true"/>
??? </class>
</hibernate-mapping>
在Room的id主键上,使用foreign表示与外键共享主键,也就是与User实体共享主键,而constrained设定为true,表示约束room的主键必须与user中对应数据的主键相同。]
存储测试。。。
User user1 = new User();
user1.setId(104);
user1.setName("张无忌");?
Room room1 = new Room();?
room1.setId(1003);
room1.setAddress("NTU-M8-345");
user1.setRoom(room1);
room1.setUser(user1);
User user2 = new User();
user2.setId(105);
user2.setName("周芷若");?
Room room2 = new Room();
room2.setId(1004);
room2.setAddress("NTU-G3-322");
user2.setRoom(room2);
room2.setUser(user2);
session.save(user1);
session.save(user2);
6、多对多关联
在数据库表格上要进行多对多对应,可以藉由一个中介表格来完成,也就是藉由多对一、一对多来完成多对多关联。
多对多由于使用了中介表格,在查询效率不彰,且在程序的对象模式上,多对多会使得对象与对象之间彼此依赖,并不是一个很好的设计方式,在设计上应避免使用多对多关系。
这里就不做多对多了。。。
附录:
1、<many-to-one/>的cascade属性
没有session.save(对象),即只执行保存多的方面,不执行保存一的方面对象。
如果没有cascade属性,则这样会抛异常。
有session.save(room1),两方面对象都保存。没有cascade属性也可。
2、<set/>的inverse属性
执行session.save(对象)的时候
默认为false
Hibernate: select user_.ID, user_.NAME as NAME1_ from USER1 user_ where user_.ID=?
Hibernate: select user_.ID, user_.NAME as NAME1_ from USER1 user_ where user_.ID=?
Hibernate: select user_.ID, user_.NAME as NAME1_ from USER1 user_ where user_.ID=?
Hibernate: insert into ROOM (ADDRESS, ID) values (?, ?)
Hibernate: insert into USER1 (NAME, ID) values (?, ?)
Hibernate: insert into USER1 (NAME, ID) values (?, ?)
Hibernate: insert into USER1 (NAME, ID) values (?, ?)
Hibernate: update USER1 set ROOMID=? where ID=?
Hibernate: update USER1 set ROOMID=? where ID=?
Hibernate: update USER1 set ROOMID=? where ID=?
设置为true
Hibernate: select user_.ID, user_.NAME as NAME1_ from USER1 user_ where user_.ID=?
Hibernate: select user_.ID, user_.NAME as NAME1_ from USER1 user_ where user_.ID=?
Hibernate: select user_.ID, user_.NAME as NAME1_ from USER1 user_ where user_.ID=?
Hibernate: insert into ROOM (ADDRESS, ID) values (?, ?)
Hibernate: insert into USER1 (NAME, ID) values (?, ?)
Hibernate: insert into USER1 (NAME, ID) values (?, ?)
Hibernate: insert into USER1 (NAME, ID) values (?, ?)
3、<set/>的cascade属性
若解除user对象与room的关系,希望Hibernate自动删除,避免不必要的SQL更新操作。
设置cascade为all-delete-orphan。
?