使用乐观锁,在数据库中使用了一个 version 列,对应 Hibernate POJO有一个 version 属性。
在实现 HashCode, equals 方法时是不是不能包括 version 属性呢?
对于如下 POJO 类
public class User {
private Integer id;
private String name;
private String fist_name;
private String last_name;
private String password;
private String password_hint;
private String email;
private String address;
private String city;
private String post_code;
....
private Integer version;
...
}
主键是 Id, 关键属性是 name, email ,其它可以为 null。
在 HashCode,equals 中是包含 id,name,email ,还是包含除了 version 之外的所有列?
问题补充:
如果这样的类,该如何确定业务键属性:
public class Road{
private Integer Id;
private Set<location> locations = new HashSet<location>();
private Type type;
get/set
...
}
问题补充:
Road 类的含议是:
每条道路含有N个地点:locations
每条道路有一种类型,比如国家一级路,省级路等:type
在这种情况下,用locations,type可以确定一条线路。但是《深入浅出 Hibernate》上讲不能使用集合作为业务关键字,否则会引起很多问题。
而 Hibernate 又不能以 id 为业务关键字。那么就只能以Type为关键字,这样的话,根本不能生成有效的 HashCode()和equals()
怎么办?
问题补充:
to mccxj :
很有道理,谢谢。
现在,我想将 roadLocation 加入 Set 集合
public class RoadLocation implements java.io.Serializable {
private Integer id;
private Road road; //道路对象
private Location location; //地点对象
private Integer mileage; //里程
..
在 HashCode, equals中是否可以
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (getClass() != obj.getClass())
return false;
RoadLocation other = (RoadLocation) obj;
if (location == null) {
if (other.location != null)
return false;
} else if (!location.equals(other.location))
return false;
if (mileage == null) {
if (other.mileage != null)
return false;
} else if (!mileage.equals(other.mileage))
return false;
if (road == null) {
if (other.road != null)
return false;
} else if (!road.equals(other.road))
return false;
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = prime
+ ((location == null) ? 0 : location.hashCode());
result = prime * result + ((mileage == null) ? 0 : mileage.hashCode());
result = prime * result + ((road == null) ? 0 : road.hashCode());
return result;
}
这里是使用 road,location 比较,还是使用 road.getName location.getName 比较好呢?
问题补充:
不知我是不是太笨了,竟找不到javaeye回贴的submit,只能不停地用“问题补充”。
to mccxj :
你认为
location.equals(other.location)
road.equals(other.road)
这样的比较正确吗?还是
location.getName().equals(other.location.getName())
road.getName().equals(other.road.getName())
更好呢?
呵呵,下午睡觉和打球去了。。
location.getName().equals(other.location.getName())
road.getName().equals(other.road.getName())
这样的写法,岂不是把location和road的业务都给包含进去了嘛?
这些本来就属于他们的义务,不应该越俎代庖。。。
既然id是主键,为什么还需要别的那?关注中!!
这个要看你的业务,Java对于eqauls方法和hashCode方法是这样规定的:
1、如果两个对象相同,那么它们的hashCode值一定要相同;
2、如果两个对象的hashCode相同,它们并不一定相同
上面说的对象相同指的是用eqauls方法比较。
在做这样的建模的时候,eqauls方法和hashCode方法一般是跟你的业务模型挂钩的。
1.如果两个对象在业务上是等同的,那么hashcode应该是一样的
2.但是hashcode相同,并不代表两个对象在业务上看起来是一样的
所以你应该是考虑在业务上,这几个属性有没有挂钩,而不是担心所谓的特殊字段。
在针对hibernate来说
[code="java"] 如果你有如下需求,你必须重载 equals() 和 hashCode()方法:
* 想把持久类的实例放入Set中(当表示多值关联时,推荐这么做)
*想重用脱管实例
Hibernate保证,仅在特定会话范围内,持久化标识(数据库的行)和Java标识是等价的。因此,一旦 我们混合了从不同会话中获取的实例,如果希望Set有明确的语义,就必 须实现equals() 和hashCode()。
实现equals()/hashCode()最显而易见的方法是比较两个对象 标识符的值。如果值相同,则两个对象对应于数据库的同一行,因此它们是相等的(如果都被添加到 Set,则在Set中只有一个元素)。不幸的是,对生成的标识不能 使用这种方法。Hibernate仅对那些持久化对象赋标识值,一个新创建的实例将不会有任何标识值。此外, 如果一个实例没有被保存(unsaved),并且它当前正在一个Set中,保存它将会给这个对象 赋一个标识值。如果equals() 和 hashCode()是基于标识值 实现的,则其哈希码将会改变,这违反了Set的契约。建议去Hibernate的站点阅读关于这个 问题的全部讨论。注意,这不是Hibernate的问题,而是一般的Java对象标识和Java对象等价的语义问题。
我们建议使用业务键值相等(Business key equality)来实现equals() 和 hashCode()。业务键值相等的意思是,equals()方法 仅仅比较形成业务键的属性,它能在现实世界里标识我们的实例(是一个自然的候选码)。 [/code]
以上出自hibernate文档。例子就可以找:
[code="java"]public class Cat {
...
public boolean equals(Object other) {
if (this == other) return true;
if ( !(other instanceof Cat) ) return false;
final Cat cat = (Cat) other;
if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
if ( !cat.getMother().equals( getMother() ) ) return false;
return true;
}
public int hashCode() {
int result;
result = getMother().hashCode();
result = 29 * result + getLitterId();
return result;
}
}[/code]
注意,业务键不必像数据库的主键那样固定不变.更多的解释,可以参考hibernate的讨论:http://www.hibernate.org/109.html
如何确定业务键属性??
这取决于你的业务是怎样的情况
怎么看你这个road的东西是缺失了一些属性。不应该靠locations来识别road,这已经是另外一个对象了。road难道没有名称之类的属性?
另外,请看清楚,需要重定义的两点理由。
看起来蛮ok的,就是代码丑陋了点。。
给个文章,再研究研究把。
http://www.ibm.com/developerworks/cn/java/j-jtp05273/
location.equals(other.location)
road.equals(other.road)
偶觉得这样就还挺ok