Hibernate学习笔记(五)映射组件属性

  • 组件属性为集合
  • 集合属性的元素为组件
  • 组件作为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属性。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值