最近,在hibernate中使用JPA注解方式配置bean
遇到联合主键时采用
@EmbeddedId
private BeanPK id;再顶一个 BeanPK 对象
@Embeddable
public class BeanPK implements Serializable{}看文档中,要求@Embeddable的BeanPK需要重写hashCode、equals方法;
equals好说;
但hashCode问题就来了
就我所知 java中的hashCode是根据对象的内存地址计算得到的;
String特殊一些,根据字符串内容计算得到的;而String常出现在常量池中。
这样java可以保证每个对象的hashCode是一个固定值。而我重写BeanPK中的hashCode方法,只能根据BeanPK中的属性的hashCode计算而出。
这样一旦BeanPK中的某个属性改变(例如:调用BeanPK.setXXXX()方法)后 hashCode的值就会改变。这样就会带来几个问题:
1.当BeanPK放入到集合中
由于对象存放到集合中的位置依赖对象的hashCode值。
那么,当对象放入集合后,再改变hashCode的值,集合就找不到该对象了。
Set中也就无法过滤重复对象。
例子:
BeanPK pk = new BeanPK("id","name");
Bean b = new Bean(pk,"age");
Map<BeanPK,Bean> map = new HashMap<BeanPK,Bean>();
map.put(pk,b);
pk.setName("newName");//BeanPK 的hashCode会根据id和name计算,setName后hashCode改变
map.get(pk);//由于hashCode改变,Map已经找不到匹配的key了,这里返回null
2.由于@Embeddable要求实现Serializable
那么当反序列后,hashCode不一致会不会带来同样的问题?希望高手指定一下,
另外:在使用@Embeddable时,需不需要重写hashCode方法?
遇到联合主键时采用
@EmbeddedId
private BeanPK id;再顶一个 BeanPK 对象
@Embeddable
public class BeanPK implements Serializable{}看文档中,要求@Embeddable的BeanPK需要重写hashCode、equals方法;
equals好说;
但hashCode问题就来了
就我所知 java中的hashCode是根据对象的内存地址计算得到的;
String特殊一些,根据字符串内容计算得到的;而String常出现在常量池中。
这样java可以保证每个对象的hashCode是一个固定值。而我重写BeanPK中的hashCode方法,只能根据BeanPK中的属性的hashCode计算而出。
这样一旦BeanPK中的某个属性改变(例如:调用BeanPK.setXXXX()方法)后 hashCode的值就会改变。这样就会带来几个问题:
1.当BeanPK放入到集合中
由于对象存放到集合中的位置依赖对象的hashCode值。
那么,当对象放入集合后,再改变hashCode的值,集合就找不到该对象了。
Set中也就无法过滤重复对象。
例子:
BeanPK pk = new BeanPK("id","name");
Bean b = new Bean(pk,"age");
Map<BeanPK,Bean> map = new HashMap<BeanPK,Bean>();
map.put(pk,b);
pk.setName("newName");//BeanPK 的hashCode会根据id和name计算,setName后hashCode改变
map.get(pk);//由于hashCode改变,Map已经找不到匹配的key了,这里返回null
2.由于@Embeddable要求实现Serializable
那么当反序列后,hashCode不一致会不会带来同样的问题?希望高手指定一下,
另外:在使用@Embeddable时,需不需要重写hashCode方法?
set是要求无序不重复的 而要保证添加进去的每个对象都是不重复的那么只能重写该对象的equals和hashcode方法。在添加的时候就已经改变了hashcode。也许文不对题 但我就是这样去记住使用的 期待大神的解答。
那么作为 实体识别 的 @EmbeddedId 能否做key呢!?
“
就我所知 java中的hashCode是根据对象的内存地址计算得到的;
String特殊一些,根据字符串内容计算得到的;而String常出现在常量池中。
”常规设计的时候,当页面需要批量添加Bean实体时
Action 通常 会传递一个 List<Bean> 或者 Map<BeanPK,Bean> 进入Service层
但在Action层中 BeanPK中的 id 还没生成,可能需要到Service层或者Dao层时才分配id那么在设计的时候 是否应该在 Action 和 Service 以及Dao 层中传递不同的集合?
(也就是说 从Action 开始传递的那个集合 到Service 或者 Dao 层 可能由于BeanPK的hashcode改变 而造成 get() 是一个null)
在设计中如何避免此类问题,
我的印象中 除了 String 以及 基本类型的包装类 以外 基本上都是使用 Object的hashcode方法
而 Object的hashcode 是使用 JNI 方式 直接使用内存地址 计算得出的hashcode既然如此,是否可在设计时 @EmbeddedId 的对象 不重写hashcode方法,只重写equals方法???
String 是immutable的类,所以不存在你说的问题,另外 String 的 hashCode 实现比较特殊,可以叫“lazy hashcode”,不建议模仿。"既然如此,是否可在设计时 @EmbeddedId 的对象 不重写hashcode方法,只重写equals方法??? "
那么你的equals方法又要依据什么来判断呢?
只重写equals方法通常是不对的。因为可能出现两个对象 equals 为 true,而hashCode却不相等的情况,这与HashMap和HashSet是一定不兼容的。“Action 通常 会传递一个 List<Bean> 或者 Map<BeanPK,Bean> 进入Service层
但在Action层中 BeanPK中的 id 还没生成,可能需要到Service层或者Dao层时才分配id”这句话听起来就是有问题的,"BeanPK中的 id 还没生成",如果BeanPK是依赖id来比较equals,那id应该满足以下条件:1 - 在构造方法中就已经安全的赋值
2 - 在对象构造以后不能再改变简单的说,就是 final + immutable(或primitive)
用于hashCode的成员也一样要满足以上条件。换句话说,如果BeanPK中的id不满足以上条件,那id不应该用作equals和hashCode的依据。
看来 BeanPK中的属性 需要使用 final 才能避免一些问题。
但这里还有一个问题,
由于 我使用hibernate,当hibernate从数据库中取出对象后,貌似实例对象时使用的是对象的无参构造器,然后调用 set方法
那么 hibernate 会不会调用 BeanPK() 然后再 setId(String id) 呢!!???
如果hibernate按照上述步骤实例,那么 id 设置成final就不行咯!
上面说的情况比较严格,说的是安全的 hash key 应该满足的,——按这个标准所有的 JavaBean 大概都不满足条件(或者只能用默认的 equals 和 hashCode)……——事实上只要保证:当一个对象作为 key 被加进 HashMap 或者 HashSet 以后,使用 HashMap 和 HashSet 期间,这个对象的 equals 和 hashCode 不发生变化就行了。