映射组件属性
映射组件属性定义
组件属性,看到组件,你肯定就会想到是由一系列东西组成的东西,才可以称之为组件吧。
组件属性的意思是: 持久化类的属性并不是基本数据类型,也不是字符串、 日期等标量类型的变量,而是一个复合类型的对象, 在持久化的过程中,它仅仅被当做值类型, 而并非引用另一个持久化实体。
为了映射组件属性,Hibermate提供了<component…>元素,每个<component…>元素映射一一个组件属性。使用<component…>元素映射组件属性时需要指定-一个name属性,用于指定该组件属性的名称。除此之外,使用<component…/> 元素还需要如下几个可选属性。
- class: 指定组件类的类名。如果不指定该属性,Hibernate将通过反射来得到该组件的类型。
- insert:指定被映射的字段是否出现在SQL的insert语句中。
- update:指定被映射的字段是否出现在SQL的update语句中。
- access : 指定Hibernate访问该组件属性的访问策略,默认是property。
- lazy: 设置该组件是否在持久化对象第–次被访问时启用延迟加载,该属性默认是true。
- optimistic-ock: 设置更新该组件属性是否需要获取乐观锁,如果该属性设置为true,则当修改该组件属性时,持久化对象的版本号会增加。
- unique:指定是否在该组件映射的所有字段上添加唯一性约束。(对应到数据库,就是创建了一个索引)
一个 自定义类通常还包括其他属性,因此还应该为<componen…/>兀素增加<property…>子兀素来映射组件属性的子属性。此处的<property…> 子元素与<las…/元素里<property…>子元素的用法完全相似,因此当组件类型的属性是基本数据类型、String 类型、 日期类型时,使用<property…/> 元索进行映射。
使用注意
<component…/>: 如果该组件属性里的属性不是基本类型、String 类型、日期类型等, 而是另一个组件类型时,则在<componet…/> 里再次使用<component…>子元素进行映射。
集合映射元素:如果组件类型里的属性是数组类型、集合类型等,则可以在<componet…/>里使用<se…/>、<lis…/>. <map…/>等子元素来映射这些集合属性。
关联映射元素: 如果组件属性里的属性是另外一个持久化实例的引用,还可以在<component…/>里使用<many-to-one…/>、<one-to-one…/>等 子元素,这就变成Hibernate关联映射。
对于组件属性,Hibernate 组件里的每一个属性添加一个数据列即可。
使用例子
public class Person {
private Integer id;
private Integer age;
private Name name;
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
public class Name {
private String first;
private String last;
private Person p;// 引用该name的Person 对象
public Person getP() {
return p;
}
public void setP(Person p) {
this.p = p;
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
}
<component name="name" class="Name" unique="true" ><!-- name是对应POJO 的属性名 ,class是指定的组件的POJO类-->
<parent name="p"></parent><!-- parent 是指定引用该容器实体的对象的属性 ,保证该标签是在<property>之前,否则会报错-->
<property name="first"></property>
<property name="last"></property>
</component>
组件属性为集合
如果组件里再次包括了List、 Set、 Map等集合属性,则我们可以在<componen…元素里使用、和
在原先的NAME POJO基础上,添加如下内容:
private Map<String, Integer> map = new java.util.HashMap<String, Integer>();
public Map<String, Integer> getMap() {
return map;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
<component name="name" class="Name" unique="true" ><!-- name是对应POJO 的属性名 ,class是指定的组件的POJO类 -->
<parent name="p"></parent><!-- parent 是指定引用该容器实体的对象的属性 ,保证该标签是在<property>之前,否则会报错 -->
<property name="first"></property>
<property name="last"></property>
<map name="map" table="NAME_ADD">
<key column="id" not-null="true"></key>
<map-key type="string" column="name_number"></map-key>
<element type="integer" column="name_number1"></element>
</map>
</component>
上面的配置文件中可以看出,该<map…/>元素的配置方法与前面映射集合属性时所用的<map…/>元素没有任何区别。没错!实际上就是没有任何区别,只是之前把<map…/>作为<class…>的子元素使用,现在把<map…/>作为<component…/>的子元素而已。
集合属性元素是组件
集合除了可以存放基本类型、字符串、日期类型之外,还可以存放组件对象(也就是复合类型)。实际上, 在更多情况下,集合里存放的都是组件对象。对于这种持久化类的映射稍有不同,我们映射集合属性时依然使用<ist…/>. <se…>和<map…/>等元素,只是不再使用<element…/>元素来映射集合元素, 而是用<composite-elemen…/>元素来映射集合元素。
配置<composite-elemen…/>元素时需要指定-一个class属性,其值指定了集合里组件对象的类型。由于<composite-elemen…/>元素映射的是集合里的组件(还是组件),所以<composite-element…/>元素和<component…>元素的用法非常相似。
如果集合组件的属性是基本类型、字符串和日期等类型,使用<property…> 子元素映射这些属性即可。
如果集合组件的属性又是组件类型,则使用<nested-composite-element…/>元素映射这些嵌套组件属性。
如果集合组件的属性引用其他持久化实体,则应该使用<many-to-one…/>元素来映射该属性。
与<component…>元素类似的是,<composite-elemen…> 元素也可接受一~个<parent…/> 子元素,用于引用包含该组件属性的持久化实体。
Hibermate为了简化管理,不再允许在<composite-element…/>元素里使用<list…>.<set…>. <map…/>等 集合映射元素!否则将形成无尽循环:集合里的元素是组件类型,而组件类型的属性再次是集合,集合又可包含组件类型…这势必导致映射关系无限复杂。(你记住只有在集合的属性是组件,才涉及到使用<composite-element…/>,一定要有集合,才会使用到composite,千万不要乱)
使用例子
在原来Person POJO 基础上添加:
private Map<String,Name> nicks = new java.util.HashMap<String, Name>();
public Map<String, Name> getNicks() {
return nicks;
}
public void setNicks(Map<String, Name> nicks) {
this.nicks = nicks;
}
public Name getName() {
return name;
}
<map name="nicks" table="NICKS">
<key column="id" not-null="true"></key>
<map-key type="string" > </map-key>
<composite-element class="Name"><!-- 注意这里的map的value 是复杂类型 ,需要指定相应的class,即组件的类型 -->
<parent name="p"/>
<property name="first" type="string"></property><!-- 组件中的属性是基本属性,还是用property 标签 -->
<property name ="last" type = "string"></property>
</composite-element>
</map>
可以看出只是原来使用<element…>元素映射Map集合的value,而现在使用<composite-element…>元素映射Map集合的value 因为此时的value的类型是Name。
组件作为Map的索引
由于Map集合的特殊性,它允许使用复合类型的对象作为Map的key。
Hibermate使用
在<composite-map-key…/>元素里可以出现如下两种子元素:
- <key-property…/>:当组件里的属性是基本类型、字符串和日期类型时,这些属性即可。
- <key-many-to-one…>:当组件里的属性是对其他持久化实体的引用时,则使用该元素来映射这些属性,这也是一种特殊的关联映射。
使用例子
在原先的Person POJO 添加如下:
private Map<Name,Integer> nickspower = new java.util.HashMap<Name, Integer>();
public Map<Name, Integer> getNickspower() {
return nickspower;
}
public void setNickspower(Map<Name, Integer> nickspower) {
this.nickspower = nickspower;
}
自定义Key对象需要重写对象的equals和hashcode,同时该类还需要实现序列化接口。
@Override
public boolean equals(java.lang.Object obj) {
// TODO Auto-generated method stub
if(this==obj){
return true;
}
if(obj!=null&&obj.getClass()==Name.class){
Name target = (Name)obj;
if(target.first==first&&target.last==last){
return true;
}
}
return false;//如果if中,都不执行,需要这里指定一个返回值。
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return this.first.hashCode()*13+this.last.hashCode();
}
<map name="nickspower" table="NICKPOWER"><--NICKPOWER 这张表的主键是由id、first和last 组成-->
<key column="id" not-null="true"></key>
<composite-map-key class="Name">
<key-property name="first" type="string"></key-property>
<key-property name="last" type="string"></key-property>
</composite-map-key>
<element column="nickpower_value" type="integer"></element>
</map>
Map<String, Name> map= new java.util.HashMap<String,Name>();
map.put("liu", n);
map.put("new", n);
p.setNicks(map);
Map<Name, Integer> map1 = new java.util.HashMap<Name, Integer>();
map1.put(n, 12);
map1.put(n, 13);
p.setNickspower(map1);
ss.save(p);
组件作为复合主键
如果数据库采用简单的逻辑主键,则不会出现组件类型的主键。但在一.些特殊的情况下,总会出现组件类型主键, Hibernate也为这种组件类型的主键提供了支持。
使用组件作为复合主键,也就是使用组件作为持久化类的标识符,则该组件类必须满足以下要求:
- 必须实现java.io.Serializable接口。
- 必须正确地重写equals()和hashCode()方法,也就是根据组件类的关键属性来区分组件对象。
当使用组件作为复合主键时,Hibemate无法为这种复合主键自动生成主键值,所以程序必须为持久化实例分配这种组件标识符。(所以你在映射文件也没有必要添加标签)
在<composite-id…>元素里使用<key-propery…/>元素来映射组件类的各属性。
使用例子:
public class Name implements java.io.Serializable {
private String first;
private String last;
@Override
public boolean equals(java.lang.Object obj) {
// TODO Auto-generated method stub
if (this == obj) {
return true;
}
if (obj != null && obj.getClass() == Name.class) {
Name target = (Name) obj;
if (target.first == first && target.last == last) {
return true;
}
}
return false;// 如果if中,都不执行,需要这里指定一个返回值。
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return this.first.hashCode() * 13 + this.last.hashCode();
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
}
<class name="Student" table="student">
<composite-id class="Name" name="name"><!-- 这里是复合主键,需要指定复合类 -->
<key-property name="first" type="string"></key-property>
<key-property name = "last" type="string"></key-property>
</composite-id>
<property name="age" type="integer"></property>
</class>
Student s = new Student();
Name n = new Name();
n.setFirst("sss");
n.setLast("jjj");
s.setAge(11);
s.setName(n);
ss.save(s);
多列作为联合主键
Hibernate还提供了另-一种联合主键支持,Hibemate 允许直接将持久化类的多列映射成联合主键。如果需要直接将持久化类的多列映射成联合主键,则该持久化类必须满足如下两个条件:
- 实现java.io.Serializable接口。
- 根据联合主键列所映射的属性来重写equals()和hashCode()方法。
使用例子:
public class Student implements java.io.Serializable {
private String first;
private String last;
private Integer age;
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
<class name="Student" table="student">
<composite-id >
<key-property name="first" type="string"></key-property>
<key-property name = "last" type="string"></key-property>
</composite-id>
<property name="age" type="integer"></property>
</class>
Student s = new Student();Student s = new Student();
s.setFirst("sss");
s.setLast("jjj");
s.setAge(11);
ss.save(s);
映射first 和last 两个标识属性,同样使用<composite-d…/>元素,此时的<composite-d…/>元素不需要name和class属性,因为标识属性没有实现类,所以不需要class属性; 因为不是一个真实存在的属性,所以不需要name属性。