1>.学过hibernate的都知道用来配置主键生成策略,显然,它只能配置主键是单列的表,对于联合主键的情况就无能为力了。今天翻到个复合主键映射的资料,觉得还是有一定价值的,就写了下来,以备后用。
    2>.hibernate处理复合主键映射是基于这样一种做法:把目标类中所有主键关联属性抽出来,单独写成一个类(我暂且叫它主键类),目标类就只需持有主键类对象,而不必再包含各个主键属性;在映射文件中使用标签来配置主键对象并指出关联属性,普通属性照常配置;hibernate只会创建一张表在,并且把主键设置为主键类各属性的联合主键,存储和加载数据时,会自然的把关联对象各属性跟表中的主键字段对应起来。在这个操作过程中,它要求主键类必须是序列化的,并且要覆盖equals方法,最好覆盖hashCode方法。(我试验过了,不覆盖equals和hashCode,结果没有出现任何问题,而且从输出的sql语句看,也没有影响数据库操作,不知道它是否还有更深的含义?还希望高手给指点下)
    3>.例子。一个规模较大公司的部门表(hibernate_dept_compositePK),由所在区域(area),部门名(name),本部门人数(empCount),组建时间(birthday)等字段组成,我们使用所在区域和部门名做联合主键:
 
1.目标类:Department.java
public class Department {
   /** 把主键关联属性抽象出来单独写成一个类 */
   //private String area;
   //private String name;
   /**把主键类对象作为成员变量*/
   private DepartmentPK departmentPK;
   private int empCount;
   private Date birthday;

//  public String getArea() {
//    return area;
//  }
//
//  public void setArea(String area) {
//    this.area = area;
//  }
//
//  public String getName() {
//    return name;
//  }
//
//  public void setName(String name) {
//    this.name = name;
//  }
    
   public int getEmpCount() {
     return empCount;
  }

   public void setEmpCount( int empCount) {
     this.empCount = empCount;
  }

   public Date getBirthday() {
     return birthday;
  }

   public void setBirthday(Date birthday) {
     this.birthday = birthday;
  }

   public DepartmentPK getDepartmentPK() {
     return departmentPK;
  }

   public void setDepartmentPK(DepartmentPK departmentPK) {
     this.departmentPK = departmentPK;
  }

}
2.主键类:DepartmentPK.java
public class DepartmentPK implements Serializable {

   private static final long serialVersionUID = -288002855915204255L;
   private String area;
   private String name;
   /**
    * 覆盖hashCode方法(根据area和name判断)
    */

   //@Override
   public int hashCode() {
     final int prime = 31;
     int result = 1;
    result = prime * result + ((area == null) ? 0 : area.hashCode());
    result = prime * result + ((name == null) ? 0 : name.hashCode());
     return result;
  }
   /**
    * 覆盖equals(根据area和name判断)
    */

  @Override
   public boolean equals(Object obj) {
     if ( this == obj)
       return true;
     if (obj == null)
       return false;
     if (getClass() != obj.getClass())
       return false;
     final DepartmentPK other = (DepartmentPK) obj;
     if (area == null) {
       if (other.area != null)
         return false;
    } else if (!area.equals(other.area))
       return false;
     if (name == null) {
       if (other.name != null)
         return false;
    } else if (!name.equals(other.name))
       return false;
     return true;
  }

   public String getArea() {
     return area;
  }

   public void setArea(String area) {
     this.area = area;
  }

   public String getName() {
     return name;
  }

   public void setName(String name) {
     this.name = name;
  }
}
3.映射文件Department.hbm.xml
xml version ="1.0" ?>
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
< hibernate-mapping >
   < class name ="com.yangfei.hibernate.compositePk.entity.Department" table ="hibernate_dept_compositePK" >
    
    
     < composite-id name ="departmentPK" >
      
       < key-property name ="area" />
       < key-property name ="name" />
     composite-id >
    
     < property name ="empCount" length ="4" />
     < property name ="birthday" type ="date" />
   class >
hibernate-mapping >
4.hibernate配置文件hibernate.cfg.xml
xml version ='1.0' encoding ='UTF-8' ?>
                    "-//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.Oracle9Dialect property >
                 < property name ="connection.url" >jdbc:oracle:thin:@127.0.0.1:1521:orcl10 property >
                 < property name ="connection.username" >scott property >
                 < property name ="connection.password" >yf123 property >
                 < property name ="connection.driver_class" >oracle.jdbc.driver.OracleDriver property >
                 < property name ="hibernate.show_sql" >true property >
            
           < mapping resource ="com/yangfei/hibernate/compositePk/entity/Department.hbm.xml" />
         session-factory >
hibernate-configuration >
5.测试类:DepartmentTest.java
public class DepartmentTest extends TestCase {
   /**
    * 测试插入数据
    */

   public void save() {
    Session session = HibernateUtils.getSession();
    Transaction t = session.beginTransaction();
     try {
      Department dept = new Department();
       /** 生成主键对象 */
      DepartmentPK deptPK = new DepartmentPK();
      deptPK.setArea( "北京");
      deptPK.setName( "研发部");
      dept.setDepartmentPK(deptPK);

      dept.setEmpCount(100);
      dept.setBirthday( new Date());

      session.save(dept);
      t.commit();
    } catch (HibernateException e) {
      e.printStackTrace();
      t.rollback();
    } finally {
      HibernateUtils.closeSession(session);
    }
  }
   /**
    * 测试加载数据
    */

   public void load() {
    Session session = HibernateUtils.getSession();
    Transaction t = session.beginTransaction();
     try {
       /** 生成主键对象 */
      DepartmentPK deptPK = new DepartmentPK();
      deptPK.setArea( "北京");
      deptPK.setName( "研发部");

      Department dept=(Department)session.load(Department. class, deptPK);
      System.out.println(dept.getDepartmentPK().getArea()+ ","+dept.getDepartmentPK().getName()+ ","+dept.getEmpCount()+ ","+dept.getBirthday());
    } catch (HibernateException e) {
      e.printStackTrace();
      t.rollback();
    } finally {
      HibernateUtils.closeSession(session);
    }
  }
    
   /**
    * 测试修改数据
    */

   public void update() {
    Session session = HibernateUtils.getSession();
    Transaction t = session.beginTransaction();
     try {
       /** 生成主键对象 */
      DepartmentPK deptPK = new DepartmentPK();
      deptPK.setArea( "北京");
      deptPK.setName( "研发部");

      Department emp=(Department)session.load(Department. class, deptPK);
      System.out.println(emp.getDepartmentPK().getArea()+ ","+emp.getDepartmentPK().getName()+ ","+emp.getEmpCount()+ ","+emp.getBirthday());
      emp.setEmpCount(100);
      session.saveOrUpdate(emp);
        
       /** 生成主键对象 */
      DepartmentPK deptPK2 = new DepartmentPK();
      deptPK2.setArea( "北京");
      deptPK2.setName( "研发部");
      Department dept=(Department)session.load(Department. class, deptPK2);
      System.out.println(dept.getDepartmentPK().getArea()+ ","+dept.getDepartmentPK().getName()+ ","+dept.getEmpCount()+ ","+dept.getBirthday());
      t.commit();
    } catch (HibernateException e) {
      e.printStackTrace();
      t.rollback();
    } finally {
      HibernateUtils.closeSession(session);
    }
  }
    
   /**
    * 测试删除数据
    */

   public void delete() {
    Session session = HibernateUtils.getSession();
    Transaction t = session.beginTransaction();
     try {
       /** 生成主键对象 */
      DepartmentPK deptPK = new DepartmentPK();
      deptPK.setArea( "北京");
      deptPK.setName( "研发部");
        
      Department dept=(Department)session.load(Department. class, deptPK);
      session.delete(dept);
      t.commit();
    } catch (HibernateException e) {
      e.printStackTrace();
      t.rollback();
    } finally {
      HibernateUtils.closeSession(session);
    }
  }
}
 
4.一般来说,复合主键映射用起来是很复杂的,无论是开发时还是升级时。所以,人们往往宁愿选择增加个主键字段,也不尽量不采用它。