论坛首页 Java企业应用论坛

一个简单的复合主键的做关联类的例子

浏览 39739 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-10-20  
我写这个例子的来由是因为haha1903提了一个这种类型的问题

http://forum.iteye.com/viewtopic.php?t=8285

场景是这样的:

用户类User,物品类Goods,每次记录用户使用物品的情况,情况包括谁在什么时间借了什么物品。其中有一个约束条件就是用户只能对同一物品使用一次。使用记录类为Record类。我们可以看出User对Record是1:n多的关系,Record对Goods是n:1的关系,而User和Goods之间没有之间的关系。

RecordId类是复合主键类,分别以n:1关联User类,n:1关联Goods类。RecordId类需要实现equals方法,需要实现Serializable。而Record类用RecordId来做主键。

类定义如下:

/*
 * Created on 2004-10-20
 *
 */
package com.javaeye;

import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;

/**
 * @author robbin
 *  
 */
public class User {

    private Long id;

    private String name;

    private Set usingRecords = new HashSet();

    /**
     * @return Returns the id.
     */
    public Long getId() {
        return id;
    }

    /**
     * @param id
     *            The id to set.
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * @return Returns the name.
     */
    public String getName() {
        return name;
    }

    /**
     * @param name
     *            The name to set.
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return Returns the usingRecords.
     */
    public Set getUsingRecords() {
        return usingRecords;
    }

    /**
     * @param usingRecords
     *            The usingRecords to set.
     */
    public void setUsingRecords(Set usingRecords) {
        this.usingRecords = usingRecords;
    }

    public void useGoods(Goods goods) {
        RecordId id = new RecordId();
        id.setUser(this);
        id.setGoods(goods);
        Record record = new Record();
        record.setRecordId(id);
        record.setRecordTime(Calendar.getInstance());
        usingRecords.add(record);
    }

    public void removeRecord(Record record) {
        usingRecords.remove(record);
    }
}



/*
 * Created on 2004-10-20
 *
 */
package com.javaeye;

import java.util.HashSet;
import java.util.Set;

/**
 * @author robbin
 *  
 */
public class Goods {

    private Long id;

    private String name;

    private Set usedRecords = new HashSet();

    /**
     * @return Returns the id.
     */
    public Long getId() {
        return id;
    }

    /**
     * @param id
     *            The id to set.
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * @return Returns the name.
     */
    public String getName() {
        return name;
    }

    /**
     * @param name
     *            The name to set.
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return Returns the usedRecords.
     */
    public Set getUsedRecords() {
        return usedRecords;
    }

    /**
     * @param usedRecords
     *            The usedRecords to set.
     */
    public void setUsedRecords(Set usedRecords) {
        this.usedRecords = usedRecords;
    }

}


/*
 * Created on 2004-10-20
 *
 */
package com.javaeye;

import java.io.Serializable;

/**
 * @author robbin
 *  
 */
public class RecordId implements Serializable {

    private User user;

    private Goods goods;

    /**
     * @return Returns the goods.
     */
    public Goods getGoods() {
        return goods;
    }

    /**
     * @param goods
     *            The goods to set.
     */
    public void setGoods(Goods goods) {
        this.goods = goods;
    }

    /**
     * @return Returns the user.
     */
    public User getUser() {
        return user;
    }

    /**
     * @param user
     *            The user to set.
     */
    public void setUser(User user) {
        this.user = user;
    }

    public boolean equals(Object obj) {
        return (obj instanceof RecordId)
                && (this.getUser().equals(((RecordId) obj).getUser()))
                && (this.getGoods().equals(((RecordId) obj).getGoods()));
    }

    public int hashCode() {
        return this.getUser().hashCode() ^ this.getGoods().hashCode();
    }
}


/*
 * Created on 2004-10-20
 *
 */
package com.javaeye;

import java.util.Calendar;

/**
 * @author robbin
 *  
 */
public class Record {

    private RecordId recordId;

    private Calendar recordTime;

    /**
     * @return Returns the recordId.
     */
    public RecordId getRecordId() {
        return recordId;
    }

    /**
     * @param recordId
     *            The recordId to set.
     */
    public void setRecordId(RecordId recordId) {
        this.recordId = recordId;
    }

    /**
     * @return Returns the recordTime.
     */
    public Calendar getRecordTime() {
        return recordTime;
    }

    /**
     * @param recordTime
     *            The recordTime to set.
     */
    public void setRecordTime(Calendar recordTime) {
        this.recordTime = recordTime;
    }
}
   发表时间:2004-10-20  
映射文件配置如下:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
	"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
	"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>
	<class name="com.javaeye.User">
		<id name="id" unsaved-value="null">
			<generator class="native"/>
		</id>
		
		<property name="name"/>
		
		<set name="usingRecords" inverse="true" lazy="true" cascade="all-delete-orphan">
			<key column="user_id" />
			<one-to-many class="com.javaeye.Record"/>
		</set>
	</class>
	
</hibernate-mapping>



<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
	"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
	"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>
	<class name="com.javaeye.Goods">
		<id name="id" unsaved-value="null">
			<generator class="native"/>
		</id>
		
		<property name="name"/>
		
		<set name="usedRecords" inverse="false" lazy="true" cascade="all-delete-orphan">
			<key column="goods_id" />
			<one-to-many class="com.javaeye.Record"/>
		</set>
	</class>
	
</hibernate-mapping>


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
	"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
	"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>
	<class name="com.javaeye.Record">
		<composite-id name="recordId" class="com.javaeye.RecordId" unsaved-value="any" >
			<key-many-to-one name="user" column="user_id" class="com.javaeye.User" />
			<key-many-to-one name="goods" column="goods_id" class="com.javaeye.Goods" />
		</composite-id>
		
		<property name="recordTime" type="calendar"/>

	</class>
	
</hibernate-mapping>
0 请登录后投票
   发表时间:2004-10-20  
记录物品使用情况的代码书写如下:

Goods goods = new Goods();
goods.setName("book");
s.save(goods);
User user = new User();
user.setName("robbin");
s.save(user);

// 用户使用物品
user.useGoods(goods);


当用户重复使用物品的时候,调用userGoods方法,Hibernate会抛出主键重复的错误。

判断用户是否使用某物品的办法如下:

RecordId id = new RecordId();
id.setUser(user);
id.setGoods(goods);

Record record = (Record) session.get(Record.class, id);

if (record == null) {
    user.usGoods(goods);
} else {
    throw new UsedGoodsException("...");
}


然而需要指出的是,Gavin King并不提倡使用composite-id,如果你不是基于已有的数据库编程,而是重新设计数据库结构,那么建议使用UserType。你可以自定义一个UserType,包括User和Goods,并且在hbm中定义该UserType为unique的,同样可以达到目的。而这种方式的好处则是不需要你来手工维护id,而由Hibernate自动维护。UserType的使用方法参考手册5.2.4节和Hibernate自带的示例中的net.sf.hibernate.test.DoubleStringType。
0 请登录后投票
   发表时间:2004-11-17  
是把是的usertype改为新建一个主键,由数据库自已维护是不是更好一些
0 请登录后投票
   发表时间:2004-11-17  
refactor一下,

Record rename to "Rent" or "借物事"更形象一些,
哈哈.
0 请登录后投票
   发表时间:2005-02-26  
如果有这样一个查询要求:”某个特定的用户具体使用了什么物品“。
也就是说这三张表要进行关联查询,该如何写HQL语句呢?我试了好多种方法都不成。
0 请登录后投票
   发表时间:2005-09-23  
受益非浅!
0 请登录后投票
   发表时间:2005-09-26  
使用usertype  .会在domain层引入hibernate 的usertype接口。
我觉得这是对业务层很大的侵入,

我的理想情况是, domain对象中不引入任何hiberante 类, 不引入任何java,sql 类,完全独立于持久化实现。
0 请登录后投票
   发表时间:2005-09-28  
引用

使用usertype .会在domain层引入hibernate 的usertype接口。
我觉得这是对业务层很大的侵入,

我的理想情况是, domain对象中不引入任何hiberante 类, 不引入任何java,sql 类,完全独立于持久化实现。


你的意思是说domain对象最好不要有logic代码,是这个意思吗?
0 请登录后投票
   发表时间:2005-09-28  
usingRecords既不应该属于User,也不应该属于Goods,就应该是一个单独的东西。
错误在于下面:
Goods goods = new Goods();
goods.setName("book");
s.save(goods);
User user = new User();
user.setName("robbin");
s.save(user);

// 用户使用物品
user.useGoods(goods);
Goods中的usingRecords没加上这个Record。
即使加上了,就成为冗余,似乎没什么太大的必要。
所以usingRecords还是应该独立出来。
当然,和hb无关。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics