集合属性大致有两种:一种是单纯的集合属性,例如List、Set或数组等集合属性;另一种是Map结构的集合属性,每个属性值都有对应的key映射。不管哪种类型的集合属性,都统一用@ElementCollection注解进行映射。使用@ElementCollection注解时可指定如下的属性。
Hibernate使用标准的@CollectionTable注解映射保存集合属性的表,使用该注解时可指定如下的属性。
@JoinClumn注解专门用于定义外键列,使用@JoinColumn时可指定如下的属性。
1. List集合
List是有序集合,因此持久化到数据库时也必须增加一个列来表示集合元素的次序。看下面的持久化类,该Person类有一个集合属性:schools,该属性对应多个学校。
@Entity
@Table(name="person_inf")
public class Person
{
@Id @Column(name="perosn_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
// 标识属性
private Integer id;
private String name;
private int age;
// 集合属性,保留该对象关联的学校
@ElementCollection(targetClass=String.class)
// 映射保存集合属性的表
@CollectionTable(name="school_inf", // 指定表名为school_inf
joinColumns=@JoinColumn(name="person_id" , nullable=false))
// 指定保存集合元素的列为 school_name
@Column(name="school_name")
// 映射集合元素索引的列
@OrderColumn(name="list_order")
private List<String> schools
= new ArrayList<>();
// id的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
// name的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
// age的setter和getter方法
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
// schools的setter和getter方法
public void setSchools(List<String> schools)
{
this.schools = schools;
}
public List<String> getSchools()
{
return this.schools;
}
}
上面的代码依次使用的注解:
1) ElementCollection:用于映射集合属性。
2) CollectionTable:用于映射集合属性表。其中name属性指定集合属性表的表名,joinColumns属性用于映射外键列。
3) @Column:用于映射保存集合元素的数据列
4) @OrderColumn:用于映射List集合的索引列。
开发了上面的持久类(POJO + 持久化注解),该类可以用于支持持久化访问。用于完成持久化访问的主程序片段如下。
public class PersonManager
{
public static void main(String[] args)
{
PersonManager mgr = new PersonManager();
mgr.createAndStorePerson();
HibernateUtil.sessionFactory.close();
}
// 创建并保存Person对象
private void createAndStorePerson()
{
// 打开线程安全的session对象
Session session = HibernateUtil.currentSession();
// 打开事务
Transaction tx = session.beginTransaction();
// 创建Person对象
Person person = new Person();
//为Person对象设置属性
person.setAge(20);
person.setName("owen.org");
// 向person的schools属性中添加2个元素
person.getSchools().add("小学");
person.getSchools().add("中学");
session.save(person);
tx.commit();
HibernateUtil.closeSession();
}
}
结果对应的数据表school_inf:
person_inf表:
2. 数组属性
Hibernate对数组和List的处理方式非常相似,它们用法区别是:List的长度可能变化,而数组长度不可以变化。
下面笔者定义一个关于使用数组的Person类。
@Entity
@Table(name="person_inf")
public class Person
{
@Id @Column(name="person_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
// 标识属性
private Integer id;
private String name;
private int age;
// 集合属性,保留该对象关联的学校
@ElementCollection(targetClass=String.class)
// 映射保存集合属性的表
@CollectionTable(name="school_inf", // 指定表名为school_inf
joinColumns=@JoinColumn(name="person_id" , nullable=false))
// 指定保存集合元素的列为 school_name
@Column(name="school_name")
// 映射集合元素索引的列
@OrderColumn(name="array_order")
private String[] schools;
// id的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
// name的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
// age的setter和getter方法
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
// schools的setter和getter方法
public void setSchools(String[] schools)
{
this.schools = schools;
}
public String[] getSchools()
{
return this.schools;
}
}
执行持久化的类与上面相同。运行的数组也是相同的,所以这里就不给出结果图。
3. Set集合属性
Set集合属性的映射与List有点不同,因为Set是无序、不可重复的集合,因此Set集合属性无须使用@OrderColumn注解映射集合元素的索引。下面我们定义了一个Set的List集合属性的Pserson类。
@Entity
@Table(name="person_inf")
public class Person
{
@Id @Column(name="person_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
// 标识属性
private Integer id;
private String name;
private int age;
// 集合属性,保留该对象关联的学校
@ElementCollection(targetClass=String.class)
// 映射保存集合属性的表
@CollectionTable(name="school_inf", // 指定表名为school_inf
joinColumns=@JoinColumn(name="person_id" , nullable=false))
// 指定保存集合元素的列为 school_name,nullable=false增加非空约束
@Column(name="school_name" , nullable=false)
private Set<String> schools
= new HashSet<>();
// id的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
// name的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
// age的setter和getter方法
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
// schools的setter和getter方法
public void setSchools(Set<String> schools)
{
this.schools = schools;
}
public Set<String> getSchools()
{
return this.schools;
}
}
下面主程序来保存持久化实例。
public class PersonManager
{
public static void main(String[] args)
{
PersonManager mgr = new PersonManager();
mgr.createAndStorePerson();
HibernateUtil.sessionFactory.close();
}
private void createAndStorePerson()
{
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
// 创建Person对象
Person person = new Person();
person.setAge(20);
person.setName("owen.org");
// 向person的schools集合属性中添加两个字符串元素
person.getSchools().add("小学");
person.getSchools().add("中学");
session.save(person);
tx.commit();
HibernateUtil.closeSession();
}
}
执行的数据为结果:
4. Map集合属性
Map集合属性同样需要使用@ElementCollection映射集合属性,使用@CollectionTable映射保存集合属性的数据表,如果Map的value是基本类型及其包装类、String或Date类型,同样也可使用@Column映射保存Map Value的数据列。除此之外,程序需要使用@MapKeyColumn映射保存Map Key的数据列。
下面我们定义了一个Map类型的集合属性。
@Entity
@Table(name="person_inf")
public class Person
{
@Id @Column(name="person_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
// 标识属性
private Integer id;
private String name;
private int age;
// 集合属性,保留该对象关联的考试成绩
@ElementCollection(targetClass=Float.class)
// 映射保存集合属性的表
@CollectionTable(name="score_inf", // 指定表名为score_inf
joinColumns=@JoinColumn(name="person_id" , nullable=false))
@MapKeyColumn(name="subject_name")
// 指定Map key的类型为String类型
@MapKeyClass(String.class)
// 映射保存Map value的数据列
@Column(name="mark")
private Map<String , Float> scores
= new HashMap<>();
// id的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
// name的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
// age的setter和getter方法
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
// scores的setter和getter方法
public void setScores(Map<String , Float> age)
{
this.scores = scores;
}
public Map<String , Float> getScores()
{
return this.scores;
}
}
执行持久化的类如下。
public class PersonManager
{
public static void main(String[] args)
{
PersonManager mgr = new PersonManager();
mgr.createAndStorePerson();
HibernateUtil.sessionFactory.close();
}
private void createAndStorePerson()
{
// 打开线程安全的Session对象
Session session = HibernateUtil.currentSession();
// 打开事务
Transaction tx = session.beginTransaction();
// 创建Person对象
Person person = new Person();
person.setAge(20);
person.setName("owen.org");
// 向person的Map集合属性中添加key-value对
person.getScores().put("语文" , 67f);
person.getScores().put("英文" , 45f);
session.save(person);
tx.commit();
HibernateUtil.closeSession();
}
}
执行的结果。
5. 集合的有序
Hibernate还支持使用SortedSet和SortedMap两个有序集合,当需要映射这种有序集合时,只有使用Hibernate本身提供的@SortNatural或@SortComparator注解,其中当前者表明对集合采用自然排序,后者表明对集合元素采用定制排序,因此使用@SortComparator时必须指定vaLue属性,该属性值为Comparator实现类。
…..
// 有序集合属性
@ElementCollection(targetClass=String.class)
// 映射保存集合元素的表
@CollectionTable(name="training_inf",
joinColumns=@JoinColumn(name="person_id" , nullable=false))
// 定义保存集合元素的数据列
@Column(name="training_name" , nullable=false)
// 使用@SortNatural指定使用自然排序
@SortNatural
private SortedSet<String> trainings
= new TreeSet<>();
……