三元关系的另外两种处理方式:
帮助手册中的chapter6中Advanced collectionMapping中的Ternary associations就是讲解三元关系处理的。
1 使用Map,让关系成为其索引。(仅能单纯的映射三元关系)
2 将一个关系重构为实体类,这是我们应用的最多的
3 使用组成
例1:
配置文件:<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"><hibernate-configuration> <session-factory> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql:///struts</property> <property name="connection.username">root</property> <property name="connection.password">root</property> <property name="show_sql">true</property> <property name="format_sql">true</property> <property name="current_session_context_class">thread</property> </session-factory></hibernate-configuration>
classroom、course和user三个表
关系表classroom_tea_course:
字段:tea_id: INTEGERclass_id: INTEGERcourse_id: INTEGER
以上三个每一个都是外键,都对应着一个表
package cn.itcast.vo;
import java.util.HashSet;import java.util.Set;
public class Teacher { private int id;
private String empNo;
private Float salary;
}
----------------------------------------package cn.itcast.vo;
public class Course { private int id;
private String courseName;
public boolean equals(Object obj) { // TODO Auto-generated method stub if(obj==null) return false; if(obj==this) return true; if(obj instanceof Course) { Course c = (Course) obj; if(c.getId()==id) return true; } return false; }
public int hashCode() { // TODO Auto-generated method stub return id; }}
------------------------------------------package cn.itcast.vo;
import java.util.HashMap;import java.util.Map;import java.util.Set;
public class Classroom { private int id;
private String location;
private int maxSeat; private Map teacherCourses = new HashMap();}--------------------------------------------
<?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 package="cn.itcast.vo" auto-import="true">
<class name="Teacher" table="teachers"> <id name="id" column="user_id"> <generator class="identity" /> </id> <property name="empNo" column="emp_no" /> <property name="salary" column="salary" />
</class>
<class name="Course" table="courses"> <id name="id" column="course_id"> <generator class="identity" /> </id> <property name="courseName" column="course_name" /> </class>
<class name="Classroom" table="classrooms"> <id name="id" column="classroom_id"> <generator class="identity" /> </id> <property name="location" /> <property name="maxSeat" column="max_seat" /> <map name="teacherCourses" table="classroom_tea_course"> <key column="class_id" /> // <map-key-many-to-many class="Teacher" column="tea_id" /> <many-to-many class="Course" column="course_id"/> //用classroom_tea_course的外键对Course的主键。 </map> </class>//键是老师,值是Course,教室号是外键</hibernate-mapping>
public class Demo{ public static void main(String[] args) { SessionFactory factory = new Configuration() .addResource("cn/itcast/vo/User.hbm.xml") .configure() .buildSessionFactory() ; //由于工程里面的实体类中没有User,所以这里必须用addResource! Session session = factory.getCurrentSession() ; session.beginTransaction() ; Teacher t = (Teacher)session.get(Teacher.class,new Integer(1)); Course c = (Course)session.get(Course.class,new Integer(1)); Classroom cr = (Classroom)session.get(Classroom.class,new Integer(1)); cr.getTeacherCourses().put(t,c); //三个实体都是通过get方法加载进来的,所以都处于持久状态…… cr.getTeacherCourses().put(t,new Course().setId(2)); //由于是map,所以上面这句话将会覆盖上面的c……其实说白了teacher和classroom由于 //在map里面是键值对的关系,所以teacher和classroom必须是一对一或是多对一的关系,这就对 //多元关系造成了一定的局限……………………所以这种方法几乎不太用。(key和value必须是一对一或是多对一 //但是绝对不可以是一对多,否则绝对会出现覆盖现象) session.getTransaction().commit() ; session.close() ; factory.close() ; }}
三者之间的关系:Teacher和Course:一对一或多对一,Teacher对的Course永远只有一个。但是也可以让一个老师对应多门课程,通过重载equals方法的方式、让key以某种方式和上一个不一样就可以了。
教室和老师课程的实体map之间是多对多关系。
其实由于一门课程只能在一个教室里面上,一个教室可以上多门课程,所以最好应该让Classroom和Course之间组成map,而且要让课程为key,教室为value;----------------------------------------------------------------------------------------
<map name="contracts"> <key column="employer_id" not-null="true"/> <map-key-many-to-many column="employee_id" class="Employee"/> <one-to-many class="Contract"/></map>以上应用在没有关系表的情况下,要求在Contract中必须有外键employer_id和employee_id,即one-to-many对应的表中有key和map-key-many-to-many所声明的外键
<map name="connections"> <key column="incoming_node_id"/> <map-key-many-to-many column="outgoing_node_id" class="Node"/> <many-to-many column="connection_id" class="Connection"/></map>以上应用在有关系表的情况下,但map-key-many-to-many和many-to-many之间只能是多对一的关系。
====================================================================================================================================================下午课程开始:
组成:
组成代表了对象间不可分割的联系,成员与整体共存亡。使用组成可以提高代码的可重用性,降低维护难度,并且易于理解。例如:人和姓名之间就是组成关系,如果人不存在了其姓名也就没有存在的价值了。
Person:id、name(Name)、age、gender、birthdayName:firstName、lastName
Person中持有Name的实体,在数据表中把两个实体映射到一张表中去:person表:person_id first_name last_name age gender birthday
映射文件:
<class name="Person" table="person" > <id name="id" column="person_id"> <generator class="identity" /> </id> <property name="age"/> <property name="gender"/> <property name="birthday"/> <component name="name" class="Name"> <parent name="owner" />//指明谁拥有name <property name="firstName" column="first_name"/> <property name="lastName" column="last_name"/> </component> </class>
写两个类,一个Person,一个Name,然后Name中持有一个owner,指明谁拥有这个名字。
Name name = new Name();name.setFirstName("bill");name.setLastName("gates");
Person p = new Person();p.setName(name);
session.save(p);
有些时候组成整体的部分并非仅有一个,如一栋楼由多个单元组成,一个单元由多个房间组成,一个房间又由多个室组成。在组成成为集合时,必须由独立的表存储组成的成员,然后通过主外键关系组成成员。需要指明组成外键。在使用上与一对多的关系相差无几。----------------------------------------------------------再来考虑订单和订单项之间的关系
订单Orders有属性id、name、date和items订单项Lineitem中有属性lineNumber、order、quantity、price
Orders和Lineitem之间是一对多的关系。
orders表:order_id、order_name、order_dateline_items表:item_number、order_id、quantity、price
映射文件:
<class name="Order" table="orders" > <id name="orderId" column="order_id"> <generator class="identity" /> </id> <property name="name" column="order_name" /> <property name="date" column="order_date" /> <set name="items" table="line_items"> <key column="order_id" /> <composite-element class="Lineitem"> <property name="lineNum" column="item_number" /> <property name="quantity" /> <property name="price" /> </composite-element> </set> </class>
动态组成以Map表示组成成员,key为属性名,value为属性值:<dynamic-component name="userAttributes"> <property name="foo" column="FOO" type="string"/> <property name="bar" column="BAR" type="integer"/> <many-to-one name="baz" class="Baz" column="BAZ_ID"/></dynamic-component>
=============================================================
映射三元关系的第三种方式:组成方式。
其中第一种Map还有另外一种不使用关系表的方式,详见手册的6.3.4节。
<map name="contracts"><key column="employer_id" not-null="true"/><map-key-many-to-many column="employee_id" class="Employee"/><one-to-many class="Contract"/></map>//没有关系表
<map name="connections"><key column="incoming_node_id"/><map-key-many-to-many column="outgoing_node_id" class="Node"/><many-to-many column="connection_id" class="Connection"/></map>//有关系表
-------------------------------------------------------手册的第8章节Component Mapping里面就是说的这种组合方式解决三元关系,也是对关系重构的一种方式。
<class name="eg.Order" .... >....<set name="purchasedItems" table="purchase_items" lazy="true"><key column="order_id"><composite-element class="eg.OrderLine"><many-to-one name="purchaseDetails class="eg.Purchase"/><many-to-one name="item" class="eg.Item"/></composite-element></set></class>
联合主键:8.4节:
<composite-id name=“propertyName” class="ClassName" mapped=“true|false” access="field|property|ClassName"> <key-property name="propertyName" type="typename" column="column_name"/> <key-many-to-one name="propertyName class="ClassName“ column="column_name"/>......</composite-id>
对于使用联合主键的POJO对象必须要重载equals方法和hashCode方法<composite-id><key-property name=“orderId"/><key-property name=“itemId"/></composite-id>
注意事项:
You may use a component as an identifier of an entity class. Your component class must satisfy certain requirements:
It must implement java.io.Serializable.It must re-implement equals() and hashCode(), consistently with the database's notion of composite key equality.
类必须实现Serializable接口并且要重载equals和hashCode方法,但是对于Hibernate3来说,第二个要求并不是必须的,但是你最好还是重载它。
-------------------------------------------------------------------------------
下面拿订单和订单项来举例,联合主键是订单号和订单项的号联合起来作为主键。
先不做成组成的关系,让订单和订单项先映射成各自独立的实体。
Order里面持有一个Set,Set里面都是订单项,然后LineItem中持有一个Order实体对象。
写一个类继承自Serializable接口
弓燕军写的配置文件:
<hibernate-configuration> <session-factory> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql:///netstore?useUnicode=true& characterEncoding=gbk</property> <property name="connection.username">root</property> <property name="connection.password">1234</property> <property name="show_sql">true</property> <property name="format_sql">true</property> <property name="current_session_context_class">thread</property> <mapping resource="org/hero/netstore/vo/Order.hbm.xml"/> <mapping resource="org/hero/netstore/vo/LineItem.hbm.xml"/> </session-factory></hibernate-configuration>---------------------------------------------------------------------------
<hibernate-mapping auto-import="true" package="org.hero.netstore.vo"> <class name="Order" table="orders"> <id name="id" column="order_id" type="integer"> <generator class="native"/> </id> <property name="orderNumber" type="string"> <column name="ORDER_NUMBER"/> </property> <set name="lineItems"> <key column="order_id"/> <one-to-many class="LineItem"/> </set> </class> <class name="LineItem" table="lineitems"> <composite-id name="key" class="CompositeKey"> <key-property name="item_id" column="item_id" type="integer"/> <key-many-to-one name="order" class="Order" column="order_id"/> </composite-id> <property name="base_price" type="double"/> <property name="quantity" type="integer"/> </class></hibernate-mapping>------------------------------------------------------------------------------订单的实体类:
public class Order implements Serializable{ /** * */ private static final long serialVersionUID = 0; private int id; private String orderNumber; private int item_id; private Set lineItems = new HashSet();
public boolean equals(Object obj) { if(obj == null) return false; if(obj == this) return true; if(obj instanceof Order) { Order o = (Order)obj; if(o.getId() == id) { return true; } } return false; } public int hashCode() { return id; }
}
订单项:
public class LineItem { private CompositeKey key; private double base_price; private int quantity;}
联合主键也需要一个类:
public class CompositeKey implements Serializable{ private static final long serialVersionUID = 0; private Order order; private int item_id; public int getItem_id() { return item_id; } public void setItem_id(int item_id) { this.item_id = item_id; } public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } @Override public boolean equals(Object obj) { if(obj == null) return false; if(obj == this) return true; if(!(obj instanceof CompositeKey)) return false; CompositeKey c = (CompositeKey)obj; if(!c.getOrder().equals(order)) return false; if(c.getItem_id() != item_id) return false; return true; } @Override public int hashCode() { return order.getId() + item_id; } }
-----------------------------------------
测试类:
public class Demo {
/** * @param args */ public static void main(String[] args) { SessionFactory factory = new Configuration().configure().buildSessionFactory(); Session session = null; try { session = factory.getCurrentSession(); session.beginTransaction(); Order order = new Order(); order.setOrderNumber("111111"); session.save(order); CompositeKey key = new CompositeKey(); key.setOrder(order); LineItem item1 = new LineItem(); item1.setKey(key); item1.setBase_price(20.5); item1.setQuantity(5); LineItem item2 = new LineItem(); item2.setKey(key); item2.setBase_price(80.9); item2.setQuantity(10); session.save(item1); session.save(item2); session.getTransaction().commit(); } catch (HibernateException e) { if(session != null && session.getTransaction() != null) { session.getTransaction().rollback(); } e.printStackTrace(); }finally{ if(session != null) { session.close(); session = null; } } factory.close();
}
}