`
anxin587
  • 浏览: 23422 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

hibernate初级 映射一对多关联关系

阅读更多

在域模型中,类之间最普遍的关系是关联关系,关联是有方向的,分为单向关联和双向关联,在关系数据库中,为了避免冗余,只存在many 方参照one方,所以关系数据库实际上面是多对一,一对一的单向关联,

在类和类之间各种关系中,多对一的单向关联和关系数据库的外键参照关系最匹配,下面一个简单例子来说明这些

两个实体,分别为Customer和orders,同时一个customer可以拥有0个或者多个orders,他们的DDL如下所示

CREATE TABLE CUSTOMERS (ID INT AUTO_INCREMENT PRIMARY KEY,NAME VARCHAR(20))

CREATE TABLE ORDERS
(
 ID INT AUTO_INCREMENT PRIMARY KEY,
 CUSTOMER_ID INT,
 ORDERNUMBER INT,
 CONSTRAINT FK FOREIGN KEY(CUSTOMER_ID) REFERENCES CUSTOMERS(ID)
)

Customer类如下

package entity;
import java.util.*;
public class Customer
{
 private Long id;
 private String name;
 private Set orders=new HashSet();

 private void setId(Long id)
 {
  this.id=id;
 }
 public Long getId()
 {
  return this.id;
 }

 public void setName(String name)
 {
  this.name=name;
 }
 public String getName()
 {
  return this.name; 
 }

 public void setOrders(Set orders)
 {
  this.orders=orders;
 }
 public Set getOrders()
 {
  return this.orders;
 }
}

orders类如下

package entity;
public class Order
{
 private Long id;
 private Customer customer;
 private int ordernumber;

 private void setId(Long id)
 {
  this.id=id;
 }
 public Long getId()
 {
  return this.id;
 }

 public void setCustomer(Customer customer)
 {
  this.customer=customer;
 }
 public Customer getCustomer()
 {
  return this.customer;
 }

 public void setOrdernumber(int ordernumber)
 {
  this.ordernumber=ordernumber;
 }

 public int getOrdernumber()
 {
  return this.ordernumber;
 }
}

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="entity">
 <class name="Order" table="ORDERS">
  <id name="id" column="ID">
   <generator class="increment"/>
  </id>
  <property name="ordernumber"  column="ORDERNUMBER"/>
  <many-to-one name="customer" column="CUSTOMER_ID"     class="Customer" cascade="save-update"/>
 </class>
</hibernate-mapping>

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="entity">
 <class name="Customer" table="CUSTOMERS">
  <id name="id" column="ID">
   <generator class="increment"/>
  </id>
  <property name="name" type="string" column="NAME"/>
  <set name="orders" cascade="delete">
   <key column="CUSTOMER_ID"/>
   <one-to-many class="Order"/>
  </set>
 </class>

package service;
import util.*;
import entity.*;
import org.hibernate.*;
import java.util.*;
public class Test
{
 public static void saveCustomerAndOrder()
 {
  Session session=HibernateUtil.getSessionFactory().openSession();
  session.beginTransaction();
  
  Customer customer=new Customer();
  customer.setName("andy");
  session.save(customer);
  
  Order order=new Order();
  Order order1=new Order();
  order.setOrdernumber(1);
  order.setCustomer(customer);
  order1.setOrdernumber(2);
  order1.setCustomer(customer);
  session.save(order);
  session.save(order1);

  session.getTransaction().commit();
 }

 public static void saveCustomerAndOrderWithCascade()
 {
  Session session=HibernateUtil.getSessionFactory().openSession();
  session.beginTransaction();
  
  Customer customer=new Customer();
  customer.setName("cao");
  //session.save(customer);
  
  Order order=new Order();
  Order order1=new Order();
  order.setOrdernumber(1);
  order.setCustomer(customer);
  order1.setOrdernumber(2);
  order1.setCustomer(customer);
  session.save(order);
  session.save(order1);

  session.getTransaction().commit();

 }
 
 public static Customer getCustomerThOrder()
 {
  Session session=HibernateUtil.getSessionFactory().openSession();
  session.beginTransaction();
  
  Customer customer=((Order)session.load(Order.class,new Long(1))).getCustomer();
   
  session.getTransaction().commit();
  if(customer!=null)
   return customer;
  return null;
 }

 public static Set getOrderThCustomer()
 {
  Session session=HibernateUtil.getSessionFactory().openSession();
  session.beginTransaction();
  
  Set orders=((Customer)session.load(Customer.class,new Long(1))).getOrders();

  session.getTransaction().commit();
  if(orders.size()>0)
   return orders;
  return null;
 }

 public static void updateCustomerAndOrders()
 {
  Session session=HibernateUtil.getSessionFactory().openSession();
  session.beginTransaction();

  Customer customer=(Customer)session.load(Customer.class,new Long(1));
  Order order = (Order)session.load(Order.class,new Long(3));

  order.setCustomer(customer);
  customer.getOrders().add(order);

  session.getTransaction().commit();
 }

 public static void delCustomer()
 {
  Session session=HibernateUtil.getSessionFactory().openSession();
  session.beginTransaction();
  
  Customer customer=(Customer)session.load(Customer.class,new Long(1));
  session.delete(customer);

  session.getTransaction().commit();
 }

 public static void main(String[] args)
 {
  Test.delCustomer();
  //Customer cst=Test.getCustomerThOrder();
  //System.out.println(cst.getName());
  //Test.updateCustomerAndOrders(); 
  /*
  Set orders=Test.getOrderThCustomer();
  Iterator it=null;
  for(it=orders.iterator();it.hasNext();)
  {
   Order order=(Order)it.next();
   System.out.println(order.getId()+" || "+order.getOrdernumber()+" || "+order.getCustomer().getName());
  }
  //*/
  //Test.saveCustomerAndOrderWithCascade();
//System.out.println(  HibernateUtil.getSessionFactory());
 }
}


</hibernate-mapping>

建立多对一的单向关联,也就是在order类里面建立到customer的关联,即在order类里面增加customer属性,这样通过order类即可以获得与之对应的customer信息,同时配置文件里面增加

<many-to-one name="customer" // 为order类里面增加的属性的名称

column=‘customer id’这个是外键的列的名称

class =“mypack。customer” 这个是外键所关联的那一个类 >

name:设定待映射的持久化类的属性名 column:设定和持久化类的属性对应的表的外键, class 设定持久化类的属性类型

这样建立的单向关联就可以进行单向的访问,

public static void saveCustomerAndOrder()
 {
  Session session=HibernateUtil.getSessionFactory().openSession();
  session.beginTransaction();
  
  Customer customer=new Customer();
  customer.setName("andy");
  session.save(customer);
  
  Order order=new Order();
  Order order1=new Order();
  order.setOrdernumber(1);
  order.setCustomer(customer);
  order1.setOrdernumber(2);
  order1.setCustomer(customer);
  session.save(order);
  session.save(order1);

  session.getTransaction().commit();
 }
上面的方法就是先保存customer然后在保存orders,其中order多属于customer的,如果我们仅仅是定义了customer对象不对它进行持久化,而我们又象对order进行持久化这样就会违背数据库的参照完整性,所以hibernate会抛出相应的异常,

org.hibernate.PropertyValueException : not-null property references a null or transientvalue 也就是告诉我们说order类的customer属性是不允许为空的 ,我们可以设定级联保存和更新来解决这个问题,

<many-to-one name="customer" column="CUSTOMER_ID"     class="Customer" cascade="save-update"/>

在hibernate持久化一个临时对象(transient object)对象时,它不会自动持久化所关联的其他临时对象,这样就会抛出我们上面的那个异常,我们设定cascade为save-update,默认是none,这样保存orders的同时会先保存customer。

通过多对一的单向关联,我们可以通过order很容易的获得customer信息,但是如果反过来,现在我们需要根据customer信息来获得order信息,所以这样的时候,我们需要建立一对多的双向关联在customer类里面增加Set orders并且给出对应的get set方法,然后在hbmxml里面增加

<set name="orders">//这个是customer类里面的属性的名称

<key column="customer_id"/> 元素设定与所关联的持久化类对应的表的外键

<one-to -many class="order"/>  设定所关联的持久化类,此处为orders

</set>

通过这些我们就可以根据customer来获得自己的orders信息了,上面配置注意点:不要巴一个单元故意用enter来分成多行显示,否则会出现error

set元素的inverse

public static void updateCustomerAndOrders()
 {
  Session session=HibernateUtil.getSessionFactory().openSession();
  session.beginTransaction();

  Customer customer=(Customer)session.load(Customer.class,new Long(1));
  Order order = (Order)session.load(Order.class,new Long(3));

  order.setCustomer(customer);
  customer.getOrders().add(order);

  session.getTransaction().commit();
 }

上面这个update方法在hibernate中实际上面是执行了两句sql语句update orders set ordernumber='aaaa' customerid='2' where customerid='2' , update orders set customerid=2 where id=2,

因为hibernate是根据内存中持久化对象的状态变化来决定需要执行那些sql语句的。inverse=true加上以后则表明customer端的关联只是order端关联的镜像,这样的话,就只会执行一条sql语句

  <set name="orders" cascade="delete" inverse="true">
   <key column="CUSTOMER_ID"/>
   <one-to-many class="Order"/>
  </set>

public static void updateCustomerAndOrders()
 {
  Session session=HibernateUtil.getSessionFactory().openSession();
  session.beginTransaction();

  Customer customer=(Customer)session.load(Customer.class,new Long(1));
  Order order = (Order)session.load(Order.class,new Long(3));

  ??order.setCustomer(customer); 如果保留这一句会去更新database
  //customer.getOrders().add(order); 如果只保留这句的话数据库并不会被更新

  session.getTransaction().commit();
 }

所以这就要求我们最好在写代码的时候写好双向关联的代码,提高程序的健壮性,而不去依赖底层的数据库或者hibernate来解决这些问题,一般情况下many端的inverse设成true,在上面的应用中也就是customer端,好像这里说many端会有些迷惑,向这set端就对了

映射一对多自身双向关联,上一个项目中就有这样的情况,一个公司 公司有0个或1个自己的母公司,当然一个公司也就可能容有0个或n个子公司,这样数据库设计一般为

create table company( id int auto_increment primary key,name varchar(20), Company_id int

constraint fk company_id references company(id));

也就是公司表里面有一个属性关联着自己的主键,自身关联

package entity;
import java.util.*;
public class Company
{
 private Long id;
 private String name;
 private Company company;
 private Set companys=new HashSet();

 private void setId(Long id)
 {
  this.id=id;
 }
 public Long getId()
 {
  return this.id;
 }
 public void setName(String name)
 {
  this.name=name;
 }
 public String getName()
 {
  return this.name;
 }
 public void setCompany(Company company)
 {
  this.company=company;
 }
 public Company getCompany()
 {
  return this.company;
 }
 public void setCompanys(Set companys)
 {
  this.companys=companys;
 }
 public Set getCompanys()
 {
  return this.companys;
 }
}

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="entity">
 <class name="Company" table="COMPANY">
  <id name="id" column="ID">
   <generator class="increment"/>
  </id>
  <property name="name"  column="NAME" type="string"/>
  <many-to-one name="company" column="COMPANY_ID"     class="Company" cascade="save-update"/>
  <set name="companys">
   <key column="COMPANY_ID"/>
   <one-to-many class="Company"/>
  </set>
 </class>
</hibernate-mapping>

public static void saveCompany()
 {
  Session session=HibernateUtil.getSessionFactory().openSession();
  session.beginTransaction();
  Company company=new Company();
  company.setName("anxin_2");
  company.setCompany((Company)session.load(Company.class,new Long(1)));
  session.save(company);
  session.getTransaction().commit();
 }
 
 public static String findVdrCompany()
 {
  Session session=HibernateUtil.getSessionFactory().openSession();
  session.beginTransaction();
  Company company=(Company)session.load(Company.class,new Long(2));
  String r=company.getCompany().getName();
  session.getTransaction().commit();
  return r;
 }
上面就是自身关联相关的代码,但是有一个注意就是不要给里面的那个外键设定notnull,要不永远也加不进去数据了,呵呵

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics