在域模型中,类和类之间最普通的关系就是关联关系。在UML语言中,关联是有方向的。以客户(Customer)和订单(Order)的关系为例,一个客户可以发出多个订单,而一个订单只能属于一个客户。
从Order到Customer的关联是多对一关联,这意味着每个Order对象都会引用一个Customer对象,因此在Order类中应该定义一个Customer类型的属性,来引用所关联的Customer对象。
从Customer到Order的关联是一对多的关联,这意味着每个Customer对象都会引用一组Order对象,因此在Customer类中应该定义一个集合类型的属性,来引用所有关联的Order对象。
一、建立多对一的单向关联关系
如上例中,我们只需在Order类中定义一个customer属性,而在Customer类中无需定义存放Order对象的集合属性。[Order 表中有Customer_ID,即Order知道Customer表的信息]
Order.java
- package mypack;
- publicclass Order implements java.io.Serializable {
- privatelong id;
- private String orderNumber;
- private Customer customer;//定义一个Customer属性
- public Order() {
- }
- public Order(Customer customer) {
- this.customer = customer;
- }
- public Order(String orderNumber, Customer customer) {
- this.orderNumber = orderNumber;
- this.customer = customer;
- }
- //省略了id,orderNumber的构造方法
- public Customer getCustomer() {
- returnthis.customer;
- }
- publicvoid setCustomer(Customer customer) {
- this.customer = customer;
- }
- }
Customer类的所有属性都是和CUSTOMERS表中的字段一一对应,因此可以直接使用如下的映射代码:[Customer 中没有Order属性信息,即,不知道Order的存在]
- <class name="mypack.Customer" table="CUSTOMERS" >
- <id name="id" type="long" column="ID">
- <generator class="increment"/>
- </id>
- <property name="name" type="string" >
- <column name="NAME" length="15" />
- </property>
- </class>
Order类的orderNumber属性和ORDERS表中ORDER_NUMBER字段对应,映射代码和上面类似,此处省去。我们关注的主要地方是,Order类中的customer属性,因为他是Customer类型的,是与ORDERS表的外键CUSTOMER_ID对应的,它的真实值是存在CUSTOMERS表中而ORDERS表存的只是对它的引用,因此customer的映射方法不能如上面一样。
- <many-to-one
- name="customer"
- column="CUSTOMER_ID"
- class="mypack.Customer"
- not-null="true"
- lazy="false"
- />
使用方法のBussiness.java演示:
- package mypack;
- import org.hibernate.*;
- import org.hibernate.cfg.Configuration;
- import java.util.*;
- publicclass BusinessService{
- publicstatic SessionFactory sessionFactory;
- static{
- try{
- // 初始化
- Configuration config = new Configuration();
- config.configure();
- sessionFactory = config.buildSessionFactory();
- }catch(RuntimeException e){e.printStackTrace();throw e;}
- }
- /*根据参数指定customer的customer_id找出记录*/
- public List findOrdersByCustomer(Customer customer){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- List orders=session.createQuery("from Order as o where o.customer.id="+customer.getId())
- .list();
- //Hibernate执行:select * from ORDERS where CUSTOMER_ID=customer.getId();
- tx.commit();
- return orders;
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- throw e;
- } finally {
- session.close();
- }
- }
- /*根据OID找出指定customer_id的记录*/
- public Customer findCustomer(long customer_id){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- Customer customer=(Customer)session.get(Customer.class,new Long(customer_id));
- tx.commit();
- return customer;
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- throw e;
- } finally {
- session.close();
- }
- }
- /*
- public void saveCustomerAndOrderWithCascade(){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- Customer customer=new Customer("Jack");//创建一个Customer持久化对象
- //不保存customer对象,这样执行的话会出现异常
- Order order1=new Order("Jack_Order001",customer);
- Order order2=new Order("Jack_Order002",customer);//创建两个Order对象
- session.save(order1);
- session.save(order2);
- tx.commit();
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- e.printStackTrace();
- } finally {
- session.close();
- }
- }
- */publicvoid saveCustomerAndOrder(){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- Customer customer=new Customer("Tom");//创建一个Customer持久化对象
- session.save(customer);
- Order order1=new Order("Tom_Order001",customer);
- Order order2=new Order("Tom_Order002",customer);//创建两个Order对象
- session.save(order1);
- session.save(order2);
- // 对同一个customerHibernate执行两次插入ORDERS表
- tx.commit();
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- throw e;
- } finally {
- session.close();
- }
- }
- publicvoid printOrders(List orders){
- for (Iterator it = orders.iterator(); it.hasNext();) {
- Order order=(Order)it.next();
- System.out.println("OrderNumber of "+order.getCustomer().getName()+ " :"+order.getOrderNumber());
- }
- }
- publicvoid test(){
- saveCustomerAndOrder();
- // saveCustomerAndOrderWithCascade();
- Customer customer=findCustomer(1);
- List orders=findOrdersByCustomer(customer);
- printOrders(orders);
- }
- publicstaticvoid main(String args[]){
- new BusinessService().test();
- sessionFactory.close();
- }
- }
- <span style="font-size:16px;color:#cc33cc;"><strong>
- </strong></span>
上述代码中方法 saveCustomerAndOrderWithCascade()如果没有session.save(customer)这一句,
执行时会抛出PropertyValueException异常,主要原因是:
在调用session.save(order1)方法之前,order1和customer对象都是临时的,临时对象是由new创建的,都是没有持久化的对象。假设 session.save(order1)被成功执行,order1会被成功持久化,变成持久化对象,但是Hibernate不会自动持久化order1所关联的customer对象。
在执行session.save(order1)时,插入ORDERS表记录的CUSTOMER_ID字段为null,这违反了数据库完整性约束,即ORDERS表中不允许CUSTOMER_ID为null。
疑问假设ORDERS表中CUSTOMER_ID字段允许为null:
- <many-to-one
- name="customer"
- column="CUSTOMER_ID"
- class="mypack.Customer"
- not-null="false"
- lazy="false"
- />
这样执行的话,能够成功的向ORDERS表中插入两条数据;但是当Hibernate自动清理(flush)缓存中所有持久化对象时,又会抛出新的异常
- org.hibernate.TransientObjectException:object references an unsaved transient instance -save the transient instance before flushing :mypack.Customer
所谓清理是指Hibernate按照持久化对象的属性变化来同步更新数据库。在清理的时候Hibernate会发现order1和order2都引用临时对象customer,而在ORDERS表中CUSTOMER_ID字段为null,这就意味着内存中持久化对象的属性和数据库中记录不一致。之所以会报错是因为order1中customer属性引用了一个临时对象Customer。
由此可见,Hibernate持久化一个对象时,默认情况下不会自动持久化所关联的其他对象。但是,我们我们希望当Hibernate持久化Order对象时自动持久化所关联的Customer对象,我们可以修改映射文件如下:
- <many-to-one [Ordrer 多方中的映射文件]
- name="customer"
- column="CUSTOMER_ID"
- class="mypack.Customer"
- cascade="save-update"
- not-null="false"
- lazy="false"
- />
当cascade属性为“save-update”,表明保存或更新对象时,会级联保存或更新与它所关联的对象。如上例中,执行saveCustomerAndOrderWithCascade()时,Hibernate会把order1与customer对象一起持久化,此时Hibernate会执行
- insert into CUSTOMERS(ID,NAME) values(2,"Jack");
- insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) value (3,"Jack_Order001",2);