单向1:N关系映射
1.无关联表的映射
由于不存在关联表,所以需要在N的一端来存储1的外键。由于N端不负责维护关系,所以N端的映射文件保持不变,仍然为以下所示:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.longtech.hibernate.domain.Department" table="Department"> <id name="id" column="id" type="java.lang.Long" > <meta attribute="field-description">唯一标识</meta> <generator class="native"/> </id> <property name="name" type="java.lang.String" column="name" length="20"> <meta attribute="field-description">部门名称</meta> </property> </class> </hibernate-mapping>然后就是1端的映射文件,需要使用one-to-many标签。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.longtech.hibernate.domain.Employee" table="Employee"> <id name="id" column="id" type="java.lang.Long" > <meta attribute="field-description">唯一标识</meta> <generator class="native"/> </id> <property name="name" type="java.lang.String" column="name" length="20"> <meta attribute="field-description">员工名称</meta> </property> <set name="departments"> <key column="employid"></key> <one-to-many class="com.longtech.hibernate.domain.Department"/> </set> </class> </hibernate-mapping>
这个地方为什么不使用many-to-many,然后加上unique=”true”呢?原因是多对多关联必须使用中间表,但是咱们这里是一对多 ,不使用关联表,所以不能使用many-to-many。
下面是储存程序
1,先来个有错误的。
Employee e=new Employee(); e.setName("employ1"); session.save(e); Department dept1=new Department(); dept1.setName("dept1"); e.getDepartments().add(dept1); Department dept2=new Department(); dept2.setName("dept2"); e.getDepartments().add(dept2); transaction.commit();
这段代码在储存的时候会抛出异常,为什么呢,因为使用了一个瞬时对象department
但是为什么会是瞬时的呢,因为hibernate在存储的时候是先储存多端,然后再储存1端,最后再用update来更新多端的外键信息。所以当你在insert了employee后,hibernate会去更新department的信息,然后就会找不到相关对象(因为数据库里不存在,如果在数据库里直接执行语句是不会出现更新异常的,但是hibernate会先把数据从数据库中取出来转化为对象再来更新,所以就会出现找不到对象的错误了。
Hibernate: insert into Employee (name) values (?) Hibernate: update Department set employid=? where id=? org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.longtech.hibernate.domain.Department at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:243)
在这里,先储存department,再来存储employee的话就不会有问题了。
通过上面的描述,应该可以看到。这个映射方式的效率是比较低的(因为需要额外的更新语句),并且,外键列上无法增加非空约束。所以来说,这种关系一般会用双向1:N关联。
2.有关联表的映射
这种方式和前面的一种差别不大,主要是增加了一个关联表。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.longtech.hibernate.domain.Employee" table="Employee"> <id name="id" column="id" type="java.lang.Long" > <meta attribute="field-description">唯一标识</meta> <generator class="native"/> </id> <property name="name" type="java.lang.String" column="name" length="20"> <meta attribute="field-description">员工名称</meta> </property> <set name="departments" table="employ_depart"> <key column="employid"></key> <many-to-many class="com.longtech.hibernate.domain.Department" column="deptid" unique="true"></many-to-many> </set> </class> </hibernate-mapping>
要使1,2中的程序正常运行,程序代码需要调整为:
Department dept1=new Department(); dept1.setName("dept1"); session.save(dept1); Department dept2=new Department(); dept2.setName("dept2"); session.save(dept2); Employee e=new Employee(); e.setName("employ1"); session.save(e); e.getDepartments().add(dept1); e.getDepartments().add(dept2); transaction.commit();
结束。