- 组件属性为集合
- 集合属性的元素为组件
- 组件作为map的索引
- 组件作为复合主键
- 多列作为联合主键
组件属性的意思是:非基本数据类型、字符串、日期等类型,而是一个复合类型的对象,在持久化的过程中,它仅仅当作值类型,而并非引用另一个持久化实体。
如下实例:
组件属性无法用<property../>元素来映射(如上面的name属性)。上面的Name类中包含一个owner属性,该owner属性指向包含该Name属性的容器实体(也就是Person对象)。
1、为了映射组件属性,需要使用<component ../>元素。下面介绍<component../>元素常见的一下属性:
name:指定该组件属性的名称。
class:指定组件类的类名。可以不指定,不指定的话,Hibernate通过反射来得到该组件的类型。
insert:指定被映射的字段是否出现在SQL的insert语句中。
update:指定被映射的字段是否出现在SQL的update语句中。
access:指定Hibernate访问该组件属性的访问策略,默认是property。
lazy:设置该组件是否在持久化对象第一次被访问时启用延迟加载,默认是true。
optimistic-lock:设置更新该组件属性是否需要获取乐观锁,如果该属性设置为true,则当修改该组件属性时,持久化对象的版本号会增加。
unique:指定是否在该组件映射的所有字段上添加唯一性约束。
2、<component../>元素的子元素:
<property../>:这里的<property../>子元素与<class ../>元素下的<property../>子元素用法完全相似。
<parent../>:用于映射组件类内一个指向其容器实体的引用,该元素只有一个name属性,其值为引用容器实体的属性名。
<component../>:如果组件属性仍然是组件,则在<component../>元素里再次使用<component../>子元素进行映射。
集合映射元素:如果组件类型里的属性时数组类型,集合类型等,可以在<component../>里使用<set../>、<list../>、<map../>等子元素来映射这些集合属性。
关联映射元素:如果组件属性里的属性时另外一个持久化实例的应用,还可以在<component../>里使用<many-to-one../>、<one-to-one../>等子元素,这就变成了Hibernate关联映射中的一种特例了。
3、对于只包含普通标量属性的组件类型而言,Hibernate的处理策略非常简单,组件里每个属性映射一个数据列即可。
组件属性为集合:
组件属性为集合,上篇文章中介绍集合属性的映射配置是一样的,没有任何区别。
集合属性的元素为组件:
集合中除了可以存放基本类型、字符串、日期类型外,还能存放组件对象(也就是符合类型)。和映射集合属性类似,只是将<list../>、<set../>、<map../>、<array../>元素下的<element../>子元素改为<composite-element../>子元素。需要指定一个class属性,值为集合里组建对象的类型。
<composite-element../>元素和<component../>元素用法非常相似:
如果组件的属性是基本类型、字符串和日期类型,使用<property../>子元素映射这些属性。
如果组件的属性又是组件类型,则使用<nested-composite-element../>元素映射这些嵌套组件属性。
如果组件的属性引用其他持久化实体,则应使用<many-to-one../>元素来映射该属性。
<composite-element../>元素也可以接受<parent../>子元素来引用包含该组件属性的持久化实体。
注意:Hibernate不允许<composite-element../>元素里再使用集合映射,如<list../><set../><map../>等集合映射,否则映射关系无线复杂。
组件作为map的索引:
1、Map集合可以允许使用复合类型对象作为Map的key值,Hibernate使用<composite-map-key../>来映射符合类型的key(回忆以下:基础类型的key使用<map-key../>来映射的)。<composite-map-key../>元素需要class属性来表示key的类名。
2、<composite-map-key../>可以下列两个子元素:
<key-property../>:当组件里的属性是基本类型、字符串、日期类型时,使用该元素来映射这些属性即可。
<key-many-to-one../>:当组件里的属性时对其他持久化实体的引用时,则使用该元素来映射这些属性,这也是一种关联映射。
3、<key-property../>元素的子元素可视为一种特殊的<property../>元素,它一样可以接受name、access、type、column、length等属性,这些属性的作用和<property../>元素中的完全一样。
4、由于Map key的特殊性,所以程序必须重写该组件类的equals()和hashCode()方法。
示例如下:
POJO类:
public class Person {
private int id;
private int age;
//组件的属性为集合,Name类中有Map<String,Integer> power,power属性为Map集合
private Name name;
//集合属性元素为组件
private List<Name2> nicks = new ArrayList<Name2>();
//组件作为Map的索引
private Map<Name2,Integer> nickPower = new HashMap<Name2,Integer>();
//省略getter 和 setter方法
}
相关的Java Bean类:
public class Name {
private String first;
private String last;
private Person owner;
private Map<String,Integer> power = new HashMap<String,Integer>();
public Name(String string, String string2) {
this.first=string;
this.last = string2;
}
public Name() {
}
//省略getter 和 setter方法
}
public class Name2 {
private String nickname;
//省略getter setter 带参数和无参数的构造方法
//重写equals方法,根据first、last进行判断
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj != null
&& obj.getClass() == Name2.class)
{
Name2 target = (Name2)obj;
if (target.getNickname().equals(getNickname()))
{
return true;
}
}
return false;
}
//重写hashCode方法,根据first、last计算hashCode值
public int hashCode()
{
return getNickname().hashCode() * 13;
}
}
映射文件(Person.hbm.xml):
<hibernate-mapping package="org.crazyit.app.domain">
<class name="Person" table="person_inf">
<id name="id" column="person_id">
<generator class="identity"/>
</id>
<property name="age" type="int" />
<!-- 组件的属性为集合时,使用component代替property,集合属性映射和之前讲的集合属性映射一样,注意下面type="int"少了会报错 -->
<component name="name" class="Name" unique="true" >
<parent name="owner"/>
<property name="first"/>
<property name="last"/>
<map name="power" table="name_power">
<key column="person_name_id" not-null="true"></key>
<map-key column="name_aspect" type="string"></map-key>
<element column="name_power" type="int"/> <!-- type="int" 不能少,少了会报错的。 -->
</map>
</component>
<!-- 集合元素为组件,使用 composite-element元素代替element元素,其他和集合映射配置一样。-->
<list name="nicks" table="person_nick">
<key column="person" not-null="true"></key>
<list-index column="person_nick_id"></list-index>
<composite-element class="Name2">
<property name="nickname" ></property>
<!-- 用来映射组件的属性还是组件的情况 、 组件的属性是其他持久化实体
<nested-composite-element name="" class=""></nested-composite-element>
<many-to-one name=""></many-to-one>
-->
</composite-element>
</list>
<!-- Map的key为组件,使用composite-map-key代替map-key元素,
key-property来映射组件中的普通属性,key-many-to-one用来映射属性对应其他持久化实体
Map nickPower的key类型为Name2,Name2类必须重写equals和hashCode方法-->
<map name="nickPower" table="person_nickPower">
<key column="person_name_id" not-null="true"></key>
<composite-map-key class="Name2">
<key-property name="nickname"></key-property>
</composite-map-key>
<element column="name_power" type="int"/>
</map>
</class>
</hibernate-mapping>
测试类为:
public class PersonManager {
public static void main(String[] args) {
Session sess = MyHibernateUtil.currSession();
Transaction tx = sess.beginTransaction();
Person rmd = new Person();
Name name = new Name();
name.setFirst("first1");
name.setLast("last2");
Map<String ,Integer> power = new HashMap<String,Integer>();
power.put("key", 12);
name.setPower(power);
rmd.setName(name);
rmd.setAge(26);
Name2 n1 = new Name2("n1");
Name2 n2 = new Name2("n2");
List<Name2> list = new ArrayList<Name2>();
list.add(n1);
list.add(n2);
rmd.setNicks(list);
Map<Name2 ,Integer> nickPower = new HashMap<Name2,Integer>();
nickPower.put(n1, 100);
rmd.setNickPower(nickPower);
//<span> </span>Person rmd = (Person) sess.get(Person.class, 1);
//<span> </span>System.out.println(rmd.getNickPower().get(new Name2("n1")));
sess.save(rmd);
测试结果为:
INFO: HHH000232: Schema update complete
Hibernate: /* insert org.crazyit.app.domain.Person */ insert into person_inf (age, first, last) values (?, ?, ?)
Hibernate: /* insert collection row org.crazyit.app.domain.Person.name.power */ insert into name_power (person_name_id, name_aspect, name_power) values (?, ?, ?)
Hibernate: /* insert collection row org.crazyit.app.domain.Person.nicks */ insert into person_nick (person, person_nick_id, nickname) values (?, ?, ?)
Hibernate: /* insert collection row org.crazyit.app.domain.Person.nicks */ insert into person_nick (person, person_nick_id, nickname) values (?, ?, ?)
Hibernate: /* insert collection row org.crazyit.app.domain.Person.nickPower */ insert into person_nickPower (person_name_id, nickname, name_power) values (?, ?, ?)
从图中我们可以看到创建第一张表”person_inf“时,使用Person中的常规数据类型的属性(id,age)和属性为组件的属性(Name组件中的first last)作为列建表和存储数据。
当持久化类为集合属性,且集合元素为组件时,如Person中的List集合nicks中放的元素是Name2,和之前集合映射一样,都另外新建一个表,此处建的表是图中的表二”person_nick“,person_nick_id列是映射List集合的序号,person是对应person的序号,nickname为Name2中定义的属性对应的列。
第三张表”person_nickPower“分别是对应person的序号,value对应的值,key对应的对象。我们在测试类中打开注释的代码,允许结果如下:
INFO: HHH000232: Schema update complete
Hibernate: select person0_.person_id as person_i1_1_0_, person0_.age as age2_1_0_, person0_.first as first3_1_0_, person0_.last as last4_1_0_ from person_inf person0_ where person0_.person_id=?
Hibernate: select nickpower0_.person_name_id as person_n1_1_0_, nickpower0_.name_power as name_pow2_3_0_, nickpower0_.nickname as nickname3_0_ from person_nickPower nickpower0_ where nickpower0_.person_name_id=?
100
如果我们不重写Name2类中的equals和hashCode方法的话,java是无法知道new Name2("n1")和Map中的key n1是相同的对象,则无法取出value的值,显示输出内容为null
第四张表”name_power“是因为由于我们在Name类中定义了Map属性的power,所以在映射的时候,建立新表了。我们使用下列代码可以输出map中的value值为12:
// Person rmd = (Person) sess.get(Person.class, 1);
// System.out.println(rmd.getName().getOwner().getAge());
组件作为复合主键:
1、使用组件作为数据库主键,需要满足下列两个要求:
必须实现java.io.Serizlizable接口。
必须正确地重写equals()和hashCode()方法,也就是根据组件累的关键属性来区分组件对象。
在Hibernate3中,第二个要求并不是必须的,但最好这样做。因为这样做能从Java语义上更好地区分两个标识属性值,这样Hibernate能将他们当作两条记录的主键。
2、使用<composite-id../>来映射这种复合主键,需要指定name和class,仍然可以指定access、 unsave-value等可选属性。
3、在<composite-id../>元素里使用<key-property../>元素来映射组件类的个属性。注意:不支持在<composite-id../>元素里使用<parent../>子元素。
示例如下:我们在POJO Person类中定义name组件为主键
public class Person {
private Name name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
}
要求Name必须实现Serializable接口,且实现equals和hashCode方法
public class Name implements Serializable{
private String first;
private String last;
public Name(String string, String string2) {
this.first=string;
this.last = string2;
}
public Name() {
}
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;
}
//重写equals方法,根据first、last进行判断
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj != null
&& obj.getClass() == Name.class)
{
Name target = (Name)obj;
if (target.getFirst().equals(getFirst())
&& target.getLast().equals(getLast()))
{
return true;
}
}
return false;
}
//重写hashCode方法,根据first、last计算hashCode值
public int hashCode()
{
return getFirst().hashCode() * 13
+ getLast().hashCode();
}
}
映射主键是,如果主键是组件的话,使用composite-id代替id元素,使用key-property映射组件中的基础属性
<hibernate-mapping package="org.crazyit.app.domain">
<class name="Person" table="person_inf">
<composite-id name="name" class="Name">
<key-property name="first" />
<key-property name="last" />
</composite-id>
<property name="age" type="int" />
</class>
</hibernate-mapping>
测试类:
public class PersonManager {
public static void main(String[] args) {
Session sess = MyHibernateUtil.currSession();
Transaction tx = sess.beginTransaction();
Person rmd = new Person();
Name name = new Name();
name.setFirst("first1");
name.setLast("last2");
rmd.setName(name);
rmd.setAge(26);
sess.save(rmd);
tx.commit();
MyHibernateUtil.closeSession();
}
}
测试类允许后,生成表,如果所示,
多列作为联合主键:
和上面组件作为复合主键相似,只是去掉<composite-id../>元素中的name和class属性。
public class Person implements Serializable{
//private Name name;
private int age;
private String first;
private String last;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = 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;
}
//重写equals方法,根据first、last进行判断
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj != null
&& obj.getClass() == Name.class)
{
Name target = (Name)obj;
if (target.getFirst().equals(getFirst())
&& target.getLast().equals(getLast()))
{
return true;
}
}
return false;
}
//重写hashCode方法,根据first、last计算hashCode值
public int hashCode()
{
return getFirst().hashCode() * 13
+ getLast().hashCode();
}
}
<hibernate-mapping package="org.crazyit.app.domain">
<span style="white-space:pre"> </span><class name="Person" table="person_inf">
<span style="white-space:pre"> </span><composite-id >
<span style="white-space:pre"> </span><key-property name="first" />
<span style="white-space:pre"> </span><key-property name="last" />
<span style="white-space:pre"> </span></composite-id>
<span style="white-space:pre"> </span><property name="age" type="int" />
<span style="white-space:pre"> </span></class>
</hibernate-mapping>
需要持久化类实现Serializable接口并重写equals和hashCode方法,去掉composite-id的name和class属性。