- 组件属性为集合
- 集合属性的元素为组件
- 组件作为map的索引
- 组件作为复合主键
- 多列作为联合主键
组件属性的意思是:非基本数据类型、字符串、日期等类型,而是一个复合类型的对象,在持久化的过程中,它仅仅当作值类型,而并非引用另一个持久化实体。
如下实例:
组件属性无法用
元素来映射(如上面的name属性)。上面的Name类中包含一个owner属性,该owner属性指向包含该Name属性的容器实体(也就是Person对象)。
1、为了映射组件属性,需要使用元素。下面介绍元素常见的一下属性:
name:指定该组件属性的名称。
class:指定组件类的类名。可以不指定,不指定的话,Hibernate通过反射来得到该组件的类型。
insert:指定被映射的字段是否出现在SQL的insert语句中。
update:指定被映射的字段是否出现在SQL的update语句中。
access:指定Hibernate访问该组件属性的访问策略,默认是property。
lazy:设置该组件是否在持久化对象第一次被访问时启用延迟加载,默认是true。
optimistic-lock:设置更新该组件属性是否需要获取乐观锁,如果该属性设置为true,则当修改该组件属性时,持久化对象的版本号会增加。
unique:指定是否在该组件映射的所有字段上添加唯一性约束。
2、元素的子元素:
:用于映射组件类内一个指向其容器实体的引用,该元素只有一个name属性,其值为引用容器实体的属性名。
:如果组件属性仍然是组件,则在元素里再次使用子元素进行映射。
集合映射元素:如果组件类型里的属性时数组类型,集合类型等,可以在里使用、、等子元素来映射这些集合属性。
关联映射元素:如果组件属性里的属性时另外一个持久化实例的应用,还可以在里使用、等子元素,这就变成了Hibernate关联映射中的一种特例了。
3、对于只包含普通标量属性的组件类型而言,Hibernate的处理策略非常简单,组件里每个属性映射一个数据列即可。
组件属性为集合:
组件属性为集合,上篇文章中介绍集合属性的映射配置是一样的,没有任何区别。
集合属性的元素为组件:
集合中除了可以存放基本类型、字符串、日期类型外,还能存放组件对象(也就是符合类型)。和映射集合属性类似,只是将
、
、
、
元素下的
子元素改为
子元素。需要指定一个class属性,值为集合里组建对象的类型。
元素和元素用法非常相似:
如果组件的属性是基本类型、字符串和日期类型,使用
子元素映射这些属性。
如果组件的属性又是组件类型,则使用元素映射这些嵌套组件属性。
如果组件的属性引用其他持久化实体,则应使用元素来映射该属性。
元素也可以接受
子元素来引用包含该组件属性的持久化实体。
注意:Hibernate不允许元素里再使用集合映射,如等集合映射,否则映射关系无线复杂。
组件作为map的索引:
1、Map集合可以允许使用复合类型对象作为Map的key值,Hibernate使用来映射符合类型的key(回忆以下:基础类型的key使用来映射的)。元素需要class属性来表示key的类名。
2、可以下列两个子元素:
:当组件里的属性是基本类型、字符串、日期类型时,使用该元素来映射这些属性即可。
:当组件里的属性时对其他持久化实体的引用时,则使用该元素来映射这些属性,这也是一种关联映射。
3、
元素的子元素可视为一种特殊的
元素,它一样可以接受name、access、type、column、length等属性,这些属性的作用和
元素中的完全一样。
4、由于Map key的特殊性,所以程序必须重写该组件类的equals()和hashCode()方法。
示例如下:
POJO类:
public class Person {
private int id;
private int age;
//组件的属性为集合,Name类中有Map power,power属性为Map集合
private Name name;
//集合属性元素为组件
private List nicks = new ArrayList();
//组件作为Map的索引
private Map nickPower = new HashMap();
//省略getter 和 setter方法
}
相关的Java Bean类:
public class Name {
private String first;
private String last;
private Person owner;
private Map power = new HashMap();
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):
测试类为:
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 power = new HashMap();
power.put("key", 12);
name.setPower(power);
rmd.setName(name);
rmd.setAge(26);
Name2 n1 = new Name2("n1");
Name2 n2 = new Name2("n2");
List list = new ArrayList();
list.add(n1);
list.add(n2);
rmd.setNicks(list);
Map nickPower = new HashMap();
nickPower.put(n1, 100);
rmd.setNickPower(nickPower);
// Person rmd = (Person) sess.get(Person.class, 1);
// 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、使用来映射这种复合主键,需要指定name和class,仍然可以指定access、 unsave-value等可选属性。
3、在
元素里使用
元素来映射组件类的个属性。注意:不支持在
元素里使用
子元素。
示例如下:我们在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映射组件中的基础属性
测试类:
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();
}
}
测试类允许后,生成表,如果所示,
多列作为联合主键:
和上面组件作为复合主键相似,只是去掉元素中的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();
}
}
需要持久化类实现Serializable接口并重写equals和hashCode方法,去掉composite-id的name和class属性。