您的位置:首页 > 其它

Hibernate中复合主键的配置和使用

2012-07-13 18:44 489 查看

有关Hibernate的联合主键

对于新系统的设计开发而言,我们应尽量避免在库表中引入与业务逻辑相关的主键关系。

将业务逻辑主键引入库表,以后业务逻辑的变化,将很可能对底层数据库结构产生连带影响。

复合主键的引入,很大程度上意味着业务逻辑已经侵入到数据存储逻辑之中。

因此,应尽量避免。但实际情况中,我们必须面对遗留系统的旧表开发,这时,对现有复合主键的支持就非常必要。

hibernate中,通过composite-id节点对复合主键进行定义。

我们可以通过两种形式确定主键:

1)基于实体类属性的复合主键

2)基于主键类的复合主键

下面通过例子看一下两种方式的用法。

我们建一个用户表user(firstname, lastname, age),以firstname, lastname作为复合主键。建表语句:

CREATE TABLE `hbm_test`.`user` (

`firstname` VARCHAR(50) NOT NULL DEFAULT '',

`lastname` VARCHAR(50) NOT NULL DEFAULT '',

`age` INTEGER UNSIGNED DEFAULT 0,

PRIMARY KEY(`firstname`, `lastname`)

)

ENGINE = InnoDB;

1)基于实体类属性的复合主键

映射文件如下:

<class name="User" table="user">

<composite-id>

<key-property

name="Lastname"

column="lastname"

type="string"

/>

<key-property

name="Firstname"

column="firstname"

type="string"

/>

</composite-id>

<property

name="Age"

column="age"

type="integer"

not-null="false"

length="10"

/>

</class>

通过composite-id节点声明了一个复合主键,是由“firstname" "lastname"组成。

实体类User中包含了复合主键firstname lastname,hibernate要求复合主键类实现equals hashCode,

以作为不同数据间的识别的标志。

public class User implements Serializable{

private String firstname;

private String lastname;

private int age;

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public String getFirstname() {

return firstname;

}

public void setFirstname(String firstname) {

this.firstname = firstname;

}

public String getLastname() {

return lastname;

}

public void setLastname(String lastname) {

this.lastname = lastname;

}

public boolean equals(Object obj){

if(!(obj instanceof User)){

return false;

}else{

User user = (User)obj;

return new EqualsBuilder().appendSuper(super.equals(obj))

.append(this.firstname, user.firstname)

.append(this.lastname, user.lastname)

.isEquals();

}

}

public int hashCode(){

return new HashCodeBuilder(-528253723, -475504089)

.appendSuper(super.hashCode())

.append(this.firstname)

.append(this.lastname)

.toHashCode();

}

}

EqualsBuilder HashCodeBuilder均为 apache common lang包中的工具类。

对于Session.load方法,我们可将User类对象本身作为查询条件:

User user = new User();

user.setFirstname("hello");

user.setLastname("world");

user = (User)session.load(User.class, user);

System.out.println("age: " + user.getAge());

2)基于主键类的复合主键

我们可以将主键逻辑加以分离,以一个单独的主键类对复合主键进行描述。

现在把User中的firstname lastname提取到一个独立的主键类UserPK中:

public class UserPK implements Serializable{

private String firstname;

private String lastname;

public String getFirstname() {

return firstname;

}

public void setFirstname(String firstname) {

this.firstname = firstname;

}

public String getLastname() {

return lastname;

}

public void setLastname(String lastname) {

this.lastname = lastname;

}

}

之后修改映射文件的composite-id节点:

<composite-id name="userPk" class="UserPK">

<key-property

name="Lastname"

column="lastname"

type="string"

/>

<key-property

name="Firstname"

column="firstname"

type="string"

/>

</composite-id>

<property

name="Age"

column="age"

type="integer"

not-null="false"

length="10"

/>

只是配置了name和class属性,name指定了实体类中的主键类属性名,class指定了主键类类型。

User.java修改如下:

public class User implements Serializable{

private UserPK userPk;

private int age;

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public UserPK getUserPk() {

return userPk;

}

public void setUserPk(UserPK userPk) {

this.userPk = userPk;

}

}

之后,我们即可通过UserPK进行数据查询:

UserPK userPk = new UserPK();

userPk.setFirstname("hello");

userPk.setLastname("world");

User user = (User)session.load(User.class, userPk);

System.out.println("age: " + user.getAge());

via: http://www.zhuoda.org/irini/73145.html
========================================================

给出我的项目中完整的一个映射配置:其中 name="dchnrestermdepotPK"中的dchnrestermdepotPK即是

sitech.res.s99800.s99870.model.Dchnrestermdepot这个实体类的一个属性,

<?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>

<class name="sitech.res.s99800.s99870.model.Dchnrestermdepot" table="dchnrestermdepot">

<composite-id name="dchnrestermdepotPK" class="sitech.res.s99800.s99870.model.DchnrestermdepotPK" >

<key-property name="group_id" column="group_id" type="string"/>

<key-property name="res_code" column="res_code" type="string"/>

</composite-id>

<property name="num_up" column="num_up" type="int" not-null="true"/>

<property name="num_down" column="num_down" type="int" not-null="true"/>

<property name="storage_age" column="storage_age" type="int" not-null="true"/>

</class>

</hibernate-mapping>

实体类Dchnrestermdepot:

package sitech.res.s99800.s99870.model;

import com.sitech.base.VO;

public class Dchnrestermdepot extends VO {

private DchnrestermdepotPK dchnrestermdepotPK;

public DchnrestermdepotPK getDchnrestermdepotPK() {

return dchnrestermdepotPK;

}

public void setDchnrestermdepotPK(DchnrestermdepotPK dchnrestermdepotPK) {

this.dchnrestermdepotPK = dchnrestermdepotPK;

}

private int num_up;

private int num_down;

private int storage_age;

public int getNum_up() {

return num_up;

}

public void setNum_up(int num_up) {

this.num_up = num_up;

}

public int getNum_down() {

return num_down;

}

public void setNum_down(int num_down) {

this.num_down = num_down;

}

public int getStorage_age() {

return storage_age;

}

public void setStorage_age(int storage_age) {

this.storage_age = storage_age;

}

}

主键类,注意重写了equals和hashCode方法:

package sitech.res.s99800.s99870.model;

import com.sitech.crmpd.core.util.HashCodeUtil;

public class DchnrestermdepotPK implements java.io.Serializable {

private String group_id;

private String res_code;

public String getGroup_id() {

return group_id;

}

public void setGroup_id(String group_id) {

this.group_id = group_id;

}

public String getRes_code() {

return res_code;

}

public void setRes_code(String res_code) {

this.res_code = res_code;

}

// equals method

public boolean equals(Object o) {

if (this == o)

return true;

if (!(o instanceof DchnrestermdepotPK))

return false;

final DchnrestermdepotPK other = (DchnrestermdepotPK) o;

if (!(group_id.equals(other.getGroup_id())))

return false;

if (!(res_code.equals(other.getRes_code())))

return false;

return true;

}

// hashcode method

public int hashCode() {

int result = HashCodeUtil.SEED;

result = HashCodeUtil.hash(result, group_id);

result = HashCodeUtil.hash(result, res_code);

return result;

}

}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

复合(联合)主键映射的时候,通常将复合主键相关的属性,单独放到一个类中,那么需要满足以下条件

①此类必须实现序列化接口 implements java.io.Serializable

②覆写hashcode和equals方法



类:

import java.io.Serializable;

// 主键类

public class NamePK implements Serializable{

private static final long serialVersionUID = 8313037022835155821L;

private String firstName;

private String lastName;


public String getFirstName() {

return firstName;

}

public void setFirstName(String firstName) {

this.firstName = firstName;

}

public String getLastName() {

return lastName;

}

public void setLastName(String lastName) {

this.lastName = lastName;

}

// hashcode和equals方法交给交给eclipse自己来写,source/generator hashCode() and equals()

public int hashCode() {

final int PRIME = 31;

int result = 1;

result = PRIME * result + ((firstName == null) ? 0 : firstName.hashCode());

result = PRIME * result + ((lastName == null) ? 0 : lastName.hashCode());

return result;

}



public boolean equals(Object obj) {

if (this == obj)

return true;

if (obj == null)

return false;

if (getClass() != obj.getClass())

return false;

final NamePK other = (NamePK) obj;

if (firstName == null) {

if (other.firstName != null)

return false;

} else if (!firstName.equals(other.firstName))

return false;

if (lastName == null) {

if (other.lastName != null)

return false;

} else if (!lastName.equals(other.lastName))

return false;

return true;

}

}



// 实体类

public class UserInfo {

private NamePK namePk;

private String sex;

private int age;

}





hbm.xml

UserInfo.hbm.xml

<?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="com.zd.model">

<class name="UserInfo" table="user_info" >

<composite-id name="namePk">

<key-property name="firstName" column="first_name" length="12"/>

<key-property name="lastName" column="last_name" length="12"/>

</composite-id>

<property name="sex" length="12"/>

<property name="age" />

</class>

</hibernate-mapping>





测试用例:

public void testCompositeId(){

Session session = null;

Transaction ta = null;

try{

session = HibernateUtil.getSession();

ta = session.beginTransaction();

NamePK namePk = new NamePK();

namePk.setFirstName("Zhang");

namePk.setLastName("san");

UserInfo ui = new UserInfo();

ui.setNamePk(namePk);

ui.setSex("M");

ui.setAge(12);

session.save(ui);

ta.commit();

}catch(Exception e){

e.printStackTrace();

if(ta != null){

ta.rollback();

}

}finally{

//关闭session,user变为detached离线对象

HibernateUtil.closeSession(session);

}



}



public void testgetId(){

Session session = null;

Transaction ta = null;

try{

session = HibernateUtil.getSession();

ta = session.beginTransaction();

NamePK namePk = new NamePK();

namePk.setFirstName("Zhang");

namePk.setLastName("san");

//get or load均是要序列化的类,所以要把主键implements Serializable

UserInfo ui = (UserInfo) session.get(UserInfo.class, namePk);

System.out.println(ui.getAge());

System.out.println(ui.getSex());

ta.commit();

}catch(Exception e){

e.printStackTrace();

if(ta != null){

ta.rollback();

}

}finally{

//关闭session,user变为detached离线对象

HibernateUtil.closeSession(session);

}

}





原帖地址:http://hi.baidu.com/fytcm/blog/item/5124737b38ea56fe0bd187fa.html







////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Filed Under: Java — Loutilities @ 2006年01月25日, 06:08:00 上午 PST

Recently, I ran into a situation where I needed to create a n-ary table mapping within
Hibernate. Recall this relationship as having three primary keys that map to some attribute in an intermediary table. When I was first attempting this, I was getting "ORA-00932:
inconsistent datatypes: expected NUMBER got BINARY" because the docs didn't have a type field and I misconstrued this as meaning the adjoining table's type. With a little help from tenwit in the forums I was able to overcome some of the deficiencies in the
documentation. I figure this might be a good thing to share with you all until the docs are updated.

Basically what you need to do is use a composite key in the intermediary table using the PK values that you want to map with as follows:

<class

name="LogTypeUser"

table="LOG_TYPE_USER"

>

<composite-id name="id" class="LogTypeUserPK" >

<key-property name="logAppId" column="LOG_APP_ID" type="java.lang.Long" />

<key-property name="logTypeId" column="LOG_TYPE_ID" type="java.lang.Long"/>

<key-property name="logUserId" column="LOG_USER_ID" type="java.lang.Long"/>

</composite-id>

<version name="olvn" column="OLVN" type="java.lang.Long" unsaved-value="null"/>

<property

name="notificationInterval"

column="NOTIFICATION_INTERVAL"

type="string"

not-null="true"

length="20"

/>

<many-to-one

name="logApp"

class="LogApp"

insert="false"

update="false"

>

<column name="LOG_APP_ID"/>

</many-to-one>

<many-to-one

name="logType"

class="LogType"

insert="false"

update="false"

>

<column name="LOG_TYPE_ID"/>

</many-to-one>

<many-to-one

name="logUser"

class="LogUser"

insert="false"

update="false"

>

<column name="LOG_USER_ID"/>

</many-to-one>

</class>

It is key that you map the type of the key to Long or Integer if that is what you're using and it is helpful to use the column field otherwise the name field would need to be the column name from the database. In the adjoining table mapping you will just need
to add a many-to-one as follows:

<set

inverse="true"

lazy="true"

name="logTypeUsers"

>

<key column="ID"/>

<one-to-many class="LogTypeUser" />

</set>

That's about it! One thing that I'll add is that if your keys in the adjoining tables are generated by the database, you will need to first save those objects first so the PKs update in the object. Here's a rough estimation of how to do that:

LogUser logUser = logTypeUser.getLogUser();

logTypeDao.saveOrUpdate(logUser);

LogApp logApp = logTypeUser.getLogApp();

logTypeDao.saveOrUpdate(logApp);

LogType logType = logTypeUser.getLogType();

logTypeDao.saveOrUpdate(logType);

LogTypeUserPK logTypeUserPK = new LogTypeUserPK();

logTypeUserPK.setLOG_APP_ID(logApp.getId());

logTypeUserPK.setLOG_TYPE_ID(logType.getId());

logTypeUserPK.setLOG_USER_ID(logUser.getId());

logTypeUser.setId(logTypeUserPK);



logTypeDao.saveOrUpdate(logTypeUser);

logTypeDao.flushHibernateSession();

Note that I have saved the value and then instantiate the PK class for LogTypeUser and set the values for the IDs to the ones I just got back from saving the object. After that, you can save the intermediary table with those values in hand. If you don't do
this, you'll get nasty errors like: "ORA-01400: cannot insert NULL into blah, blah, blah field."
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: