假设Customer类中有orders属性和images属性,那么两者有相同的定义形式:
private Set orders = new HashSet();
private Set images = new HashSet();
orders属性与images属性的区别在于,前者存放的是实体类型的Order对象,而后者存放的是值类型的String对象。实体类型的对象有单独的OID和独立的生命周期,而值类型的对象没有单独的OID和独立的生命周期。
在映射文件中,用来映射值类型集合的元素有<set>、<idbag>、<list>和<map>。
1.映射Set
若Customer对象的images集合中不允许存放重复的照片名称,可以把images属性定义为Set类型,把IMAGES表的CUSTOMER_ID和FILENAME字段作为联合主键。
数据库表:
create table CUSTOMERS (
ID bigint not null,
NAME varchar(15),
AGE int,
primary key (ID)
);
create table IMAGES(
CUSTOMER_ID bigint not null,
FILENAME varchar(15) not null,
primary key (CUSTOMER_ID,FILENAME),
foreign key (CUSTOMER_ID) references CUSTOMERS(ID)
);
Customer.java:
public class Customer implements Serializable {
private Long id;
private String name;
private int age;
private Set images=new TreeSet();
Constructor()...; getter()...; setter()...;
}
Customer.hbm.xml:
<hibernate-mapping >
<class name="mypack.Customer" table="CUSTOMERS" >
<id name="id" type="long" column="ID">
<generator class="increment"/>
</id>
<property name="name" type="string" >
<column name="NAME" length="15" />
</property>
<property name="age" type="int" >
<column name="AGE" />
</property>
<set name="images" table="IMAGES" lazy="true" >
<key column="CUSTOMER_ID" />
<element column="FILENAME" type="string" not-null="true"/>
</set>
</class>
</hibernate-mapping>
<element>子元素指定和images集合中元素相对应的字段是 FILENAME。
lazy="true" 对images集合采用了延迟检索策略,当加载Customer对象时,需通过Hibernate的initialize()方法显式初始化images集合:
Hibernate.initialize(customer.getImages());
2.映射Bag
Bag集合中的对象不按特定方式排序,但允许有重复元素,Hibernate允许在持久化类中使用List来模拟Bag的行为。
若Customer对象的images集合中允许存放重复的照片文件名,可以把images属性定义为List类型,IMAGES表中需要定义一个代理主键ID。
数据库表为:
create table CUSTOMERS (
ID bigint not null,
NAME varchar(15),
AGE int,
primary key (ID)
);
create table IMAGES(
ID bigint not null,
CUSTOMER_ID bigint not null,
FILENAME varchar(15) not null,
primary key (ID),
foreign key (CUSTOMER_ID) references CUSTOMERS(ID)
);
Customer.java:
public class Customer implements Serializable {
private Long id;
private String name;
private int age;
private List images=new ArrayList();
Constructor()...; getter()...; setter()...;
}
Customer.hbm.xml:
<hibernate-mapping >
<class name="mypack.Customer" table="CUSTOMERS" >
<id name="id" type="long" column="ID">
<generator class="increment"/>
</id>
<property name="name" type="string" >
<column name="NAME" length="15" />
</property>
<property name="age" type="int" >
<column name="AGE" />
</property>
<idbag name="images" table="IMAGES" lazy="true">
<collection-id type="long" column="ID">
<generator class="increment"/>
</collection-id>
<key column="CUSTOMER_ID" />
<element column="FILENAME" type="string" not-null="true"/>
</idbag>
</class>
</hibernate-mapping>
<collection-id>子元素用于设置IMAGES表的ID主键。
3.映射List
当用<idbag>映射images属性时,images集合中的元素不会按照索引位置排序。
若希望images集合中允许存放重复元素,并且按照索引位置排序,可在IAMGES表中定义一个POSITION字段,用以代表索引位置,然后用<list>映射。
数据库表:
create table CUSTOMERS (
ID bigint not null,
NAME varchar(15),
AGE int,
primary key (ID)
);
create table IMAGES(
CUSTOMER_ID bigint not null,
POSITION int not null,
FILENAME varchar(15) not null,
primary key (CUSTOMER_ID,POSITION),
foreign key (CUSTOMER_ID) references CUSTOMERS(ID)
);
Customer.java:
public class Customer implements Serializable {
private Long id;
private String name;
private int age;
private List images=new ArrayList();
Constructor()...; getter()...; setter()...;
}
Customer.hbm.xml:
<hibernate-mapping >
<class name="mypack.Customer" table="CUSTOMERS" >
<id name="id" type="long" column="ID">
<generator class="increment"/>
</id>
<property name="name" type="string" >
<column name="NAME" length="15" />
</property>
<property name="age" type="int" >
<column name="AGE" />
</property>
<list name="images" table="IMAGES" lazy="true">
<key column="CUSTOMER_ID" />
<list-index column="POSITION" />
<element column="FILENAME" type="string" not-null="true"/>
</list>
</class>
</hibernate-mapping>
<list-index>子元素用于设置IMAGES表中代表索引位置的字段POSITION。
若将images属性定义为Java数组类型:
private String[] iamges;
由于Hibernate无法为数组创建代理,因此不能对数组类型的images集合使用延迟检索策略。
4.映射Map
若Customer类的images集合中的每一个元素包含一个键对象和值对象,那么应该把images集合定义为Map类型。在IMAGES表中定义一个IMAGE_NAME字段,和images集合中的键对象对应。
数据库表:
create table CUSTOMERS (
ID bigint not null,
NAME varchar(15),
AGE int,
primary key (ID)
);
create table IMAGES(
CUSTOMER_ID bigint not null,
IMAGE_NAME varchar(15) not null,
FILENAME varchar(15) not null,
primary key (CUSTOMER_ID,IMAGE_NAME),
foreign key (CUSTOMER_ID) references CUSTOMERS(ID)
);
Customer.java:
public class Customer implements Serializable {
private Long id;
private String name;
private int age;
private Map images=new HashMap();
Constructor()...; getter()...; setter()...;
}
Customer.hbm.xml:
<hibernate-mapping >
<class name="mypack.Customer" table="CUSTOMERS" >
<id name="id" type="long" column="ID">
<generator class="increment"/>
</id>
<property name="name" type="string" >
<column name="NAME" length="15" />
</property>
<property name="age" type="int" >
<column name="AGE" />
</property>
<map name="images" table="IMAGES" lazy="true">
<key column="CUSTOMER_ID" />
<map-key column="IMAGE_NAME" type="string"/>
<element column="FILENAME" type="string" not-null="true"/>
</map>
</class>
</hibernate-mapping>
<map-key>子元素用于设置IMAGES表中和images集合的键对象对应的 IMAGE_NAME字段。
5.对集合排序
Hibernate对集合中的元素支持两种排序方式:数据库排序和内存排序。
映射文件中,Hibernate用sort属性来设置内存排序,用order-by属性来设置数据库排序。
排序属性 | <set> | <idbag> | <list> | <map> |
sort(内存排序) | 支持 | 不支持 | 不支持 | 支持 |
order-by(数据库排序) | 支持 | 支持 | 不支持 | 支持 |
<set>、<idbag>和<map>均支持order-by属性。
<set name="images" table="IMAGES" lazy="true" order-by="FILENAME asc" >
<key column="CUSTOMER_ID" />
<element column="FILENAME" type="string" not-null="true"/>
</set>
order-by属性中可以加入SQL函数:
order-by="lower(FILENAME) desc"
<set>和<map>支持sort属性,可以设置自然排序或客户化排序。
<set name="images" table="IMAGES" lazy="true" sort="natural" >
<key column="CUSTOMER_ID" />
<element column="FILENAME" type="string" not-null="true"/>
</set>
<set name="images" table="IMAGES" lazy="true" sort="mypack.ReverseStringComparator">
<key column="CUSTOMER_ID" />
<element column="FILENAME" type="string" not-null="true"/>
</set>
ReverseStringComparator.java:
public class ReverseStringComparator implements Comparator{
public int compare(Object o1,Object o2){
String s1=(String)o1;
String s2=(String)o2;
if(s1.compareTo(s2)>0) return -1;
if(s1.compareTo(s2)<0) return 1;
return 0;
}
}
6.映射组件类型集合
若照片包含照片名、文件名、长和宽等信息,可以专门定义一个Image组件类来表示照片。
Customer类和Image类之间是一对多的组成关系,Image类是值类型,没有OID。由于Image对象存放在Java集合中,为保证Java集合正常工作,应该在Image类中实现equals()和hashCode()方法。
数据库表:
create table CUSTOMERS (
ID bigint not null,
NAME varchar(15),
AGE int,
primary key (ID)
);
create table IMAGES(
CUSTOMER_ID bigint not null,
IMAGE_NAME varchar(15) not null,
FILENAME varchar(15) not null,
SIZEX int not null,
SIZEY int not null,
primary key (CUSTOMER_ID,IMAGE_NAME,FILENAME,SIZEX,SIZEY),
foreign key (CUSTOMER_ID) references CUSTOMERS(ID)
);
Image.java:
public class Image implements Serializable {
private String name;
private String filename;
private int sizeX;
private int sizeY;
private Customer customer;
Constructor()...; getter()...; setter()...;
public boolean equals(Object o){
if(this==o) return true;
if(! (o instanceof Image)) return false;
final Image other=(Image)o;
if(this.name.equals(other.getName())
&& this.filename.equals(other.getFilename())
&& this.sizeX==other.getSizeX()
&& this.sizeY==other.getSizeY())
return true;
else
return false;
}
public int hashCode() {
int result;
result = name.hashCode();
result = 29 * result + filename.hashCode()+sizeX+sizeY;
return result;
}
}
Customer.java:
public class Customer implements Serializable {
private Long id;
private String name;
private int age;
private Set images=new HashSet();
Constructor()...; getter()...; setter()...;
}
Customer.hbm.xml:
<hibernate-mapping >
<class name="mypack.Customer" table="CUSTOMERS" >
<id name="id" type="long" column="ID">
<generator class="increment"/>
</id>
<property name="name" type="string" >
<column name="NAME" length="15" />
</property>
<property name="age" type="int" >
<column name="AGE" />
</property>
<set name="images" table="IMAGES" lazy="true" order-by="IMAGE_NAME asc">
<key column="CUSTOMER_ID" />
<composite-element class="mypack.Image">
<parent name="customer" />
<property name="name" column="IMAGE_NAME" not-null="true" />
<property name="filename" column="FILENAME" not-null="true" />
<property name="sizeX" column="SIZEX" not-null="true" />
<property name="sizeY" column="SIZEY" not-null="true" />
</composite-element>
</set>
</class>
</hibernate-mapping><hibernate-mapping >
<class name="mypack.Customer" table="CUSTOMERS" >
<id name="id" type="long" column="ID">
<generator class="increment"/>
</id>
<property name="name" type="string" >
<column name="NAME" length="15" />
</property>
<property name="age" type="int" >
<column name="AGE" />
</property>
<set name="images" table="IMAGES" lazy="true" order-by="IMAGE_NAME asc">
<key column="CUSTOMER_ID" />
<composite-element class="mypack.Image">
<parent name="customer" />
<property name="name" column="IMAGE_NAME" not-null="true" />
<property name="filename" column="FILENAME" not-null="true" />
<property name="sizeX" column="SIZEX" not-null="true" />
<property name="sizeY" column="SIZEY" not-null="true" />
</composite-element>
</set>
</class>
</hibernate-mapping>
<composite-element>子元素用于映射Image组件类, <parent>子元素用于映射Image类的customer属性, <property>子元素用于映射Image类的name、filename、sizeX和sizeY属性。