Hibernate集合映射
- Set
- List
- Map
- Array
- Bag
Set集合
set元素不允许重复,常见的有HashSet、TreeSet、LinkedHashSet,HashSet中的元素是没有顺序的,TreeSet和LinkedHashSet中的元素是有序的
实体类User
package com.hml.domain; import java.util.Set; public class User { private int id; private String name; private Set<String> address;// set集合存放地址 public int getId() { return id; } public Set<String> getAddress() { return address; } public void setAddress(Set<String> address) { this.address = address; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
User.hbm.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.hml.domain"> <class name="User" table="t_user"> <id name="id" column="id"> <generator class="native"></generator> </id> <property name="name" column="name"></property> <!-- name: set集合对象 table:集合对象数据存储的表 key:外键的名称,对应的数据是User的主键 element:set集合中存储的数据 --> <set name="address" table="t_user_address"> <key column="userId"></key> <element column="address" type="string"></element> </set> </class> </hibernate-mapping>
注意:hibernate中的集合在实体类中声明时应该声明为接口类型,因为Hibernate内部对集合类型进行了特殊处理。
Set集合hbm的配置文件如上,如果需要排序可以在set节点添加sort属性或者order-by属性指定排序,但是此时要求的Set集合是有序的集合,如:TreeSet。sort是内存排序,order-by是数据库排序,建议使用order-by。
List集合
List集合是有序的集合
实体类User
package com.hml.domain; import java.util.List; public class User { private int id; private String name; private List<String> address; // List集合 public int getId() { return id; } public List<String> getAddress() { return address; } public void setAddress(List<String> address) { this.address = address; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
User.hbm.xml文件配置
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.hml.domain"> <class name="User" table="t_user"> <id name="id" column="id"> <generator class="native"></generator> </id> <property name="name" column="name"></property> <!-- name: set集合对象 table:集合对象数据存储的表 key:外键的名称,对应的数据是User的主键 element:set集合中存储的数据 list-index:指定集合元素的顺序 --> <list name="address" table="t_user_address"> <key column="userId"></key> <list-index column="idx"></list-index> <element column="address" type="string"></element> </list> </class> </hibernate-mapping>
List集合的配置和Set类似,只是多了一个指定元素顺序的节点list-index
集合Map
Map集合存储key value 值对
User实体类
package com.hml.domain; import java.util.Map; public class User { private int id; private String name; private Map<String, String> address; public int getId() { return id; } public Map<String, String> getAddress() { return address; } public void setAddress(Map<String, String> address) { this.address = address; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
User.hbm.xml文件的配置
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.hml.domain"> <class name="User" table="t_user"> <id name="id" column="id"> <generator class="native"></generator> </id> <property name="name" column="name"></property> <!-- name: Map集合对象 table:集合对象数据存储的表 key:外键的名称,对应的数据是User的主键 element:Map集合中存储的数据 map-key:Map集合中key的映射 --> <map name="address" table="t_user_address"> <key column="userid"></key> <map-key column="key_" type="string"></map-key> <element column="address" type="string"></element> </map> </class> </hibernate-mapping>
Array数组的使用和List集合差不多只是使用的节点是array而已,Bag存储的元素可重复,但是是无序的,配置使用的是bag节点,实体中使用的是list集合。
多对一/一对多关系
这里用员工和部门举例,部门和员工的关系是一对多的关系,员工和部门的关系就是多对一的关系。
类图
Employee实体
package com.hml.domain; public class Employee { private Integer id; private String name; private Department department; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Department getDepartment() { return department; } public void setDepartment(Department department) { this.department = department; } }
Department实体
package com.hml.domain; import java.util.HashSet; import java.util.Set; public class Department { private Integer id; private String name; private Set<Employee> employees = new HashSet<Employee>(); public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Employee> getEmployees() { return employees; } public void setEmployees(Set<Employee> employees) { this.employees = employees; } }
Employee.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.hml.domain"> <class name="Employee" table="t_employee"> <id name="id" column="id"> <generator class="native"></generator> </id> <property name="name" column="name"></property> <!-- many-to-one:表示多对一的关系 column:指定外键的名称,可以省略,如果省略,则外键名称和name一致 class:指定一的一方是哪个类,可以省略,系统可以自动推断 --> <many-to-one name="department" column="departmentid" class="Department"></many-to-one> </class> </hibernate-mapping>
Department.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.hml.domain"> <class name="Department" table="t_department"> <id name="id" column="id"> <generator class="native"></generator> </id> <property name="name" column="name"></property> <!-- 这里和普通集合的配置是一样的,只不过这里的key的column是多的一方的many-to-one column的值, element元素变成了one-to-many,class的值是多的一方的全限定名称 --> <set name="employees" > <key column="departmentid"></key> <one-to-many class="Employee"/> </set> </class> </hibernate-mapping>
上面的例子是 多对一/一对多 的双向映射关系,也就是说两遍都可以维护关系。一般情况下为了效率,会让一的一方放弃维护关系,这个时候我们可以使用如下的配置
Department.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.hml.domain"> <class name="Department" table="t_department"> <id name="id" column="id"> <generator class="native"></generator> </id> <property name="name" column="name"></property> <!-- 这里和普通集合的配置是一样的,只不过这里的key的column是多的一方的many-to-one column的值, element元素变成了one-to-many,class的值是多的一方的全限定名称 inverse: 默认为false,代表维护关系,如果为true则表示放弃维护关系,这时关系由另外一方维护 --> <set name="employees" inverse="true"> <key column="departmentid"></key> <one-to-many class="Employee"/> </set> </class> </hibernate-mapping>
inverse:如果一的一方放弃维护关联关系,也就是inverse="true",那么对于一的一方进行删除,如果多的一方有关联的数据,那么会抛出异常,对于数据的加载不会存在影响
一对多和多对一可分为,单向和双向关系,单向是指有一方不能查询到对方,双向是指,彼此都可以查到对方。如果我们把Department employees属性去掉,那么对应关系就是一对多单向关联关系。
多对多的对应关系
Student实体类
package com.hml.domain; import java.util.HashSet; import java.util.Set; public class Student { private Integer id; private String name; private Set<Teacher> teachers = new HashSet<Teacher>();; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Teacher> getTeachers() { return teachers; } public void setTeachers(Set<Teacher> teachers) { this.teachers = teachers; } }
Teacher实体类
package com.hml.domain; import java.util.HashSet; import java.util.Set; public class Teacher { private Integer id; private String name; private Set<Student> students = new HashSet<Student>(); public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } }
Student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.hml.domain"> <class name="Student" table="t_student"> <id name="id" column="id"> <generator class="native"></generator> </id> <property name="name" column="name"></property> <!-- table:中间表的名称 key:中间表的外键,对应本对象的id many-to-many:说明多对多的关系,class指明关联对象,column中间表中相对于关联对象的外键 --> <set name="teachers" table="t_student_tearcher" inverse="false"> <key column="studentId"></key> <many-to-many class="Teacher" column="teacherId"></many-to-many> </set> </class> </hibernate-mapping>
Teacher.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.hml.domain"> <class name="Teacher" table="t_teacher"> <id name="id" column="id"> <generator class="native"></generator> </id> <property name="name" column="name"></property> <!-- table:中间表的名称 key:中间表的外键,对应本对象的id many-to-many:说明多对多的关系,class指明关联对象,column中间表中相对于关联对象的外键 inverse: true表示不维护关联关系,多对多的映射建议一方放弃维护关联关系 cascade:指定级联关系,这里设置为delete是级联删除的意思,也就是说删除teacher的同时级联删除该老师的学生, 可以避免由于不能维护关系而删除对象报异常,cascade有多种取值,实际使用的时候按照需求设置 --> <set name="students" table="t_student_tearcher" inverse="true" cascade="delete"> <key column="teacherId"></key> <many-to-many class="Student" column="studentId"></many-to-many> </set> </class> </hibernate-mapping>
多对多的对应关系也分为单向和双向,单向就是通过一方查询不到另外一方。
一对一映射关系
基于主键的一对一映射关系:有一张表的主键就是外键
基于外键的一对一映射关系:在一张表中存在一个外键,这个外键是唯一的
基于外键的一对一映射关系
表结构:
Person表:
IdCard表:
IdCard表中有一个外键personid对应了person表中的主键。
Person类:
package com.hml.domain; public class Person { private Integer id; private String name; private IdCard idCard; // 关联的身份证 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public IdCard getIdCard() { return idCard; } public void setIdCard(IdCard idCard) { this.idCard = idCard; } @Override public String toString() { return "[Person:id=" + id + ", name=" + name + "]"; } }
IdCard类
package com.hml.domain; public class IdCard { private Integer id; private String number; private Person person; // 关联的公民 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } @Override public String toString() { return "[IdCard:id=" + id + ", number=" + number + "]"; } }
Person.hbm.xml映射文件:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.hml.domain"> <class name="Person" table="person"> <id name="id"> <generator class="native"></generator> </id> <property name="name"/> <!-- idCard属性,IdCard类型。 表达的是本类与IdCard的一对一。 采用基于外键的一对一映射方式,本方无外键方。 property-ref属性: 写的是对方映射中外键列对应的属性名。 --> <one-to-one name="idCard" class="IdCard" property-ref="person"/> </class> </hibernate-mapping>
IdCard.hbm.xml映射文件:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.hml.domain"> <class name="IdCard" table="idCard"> <id name="id"> <generator class="native"></generator> </id> <property name="number"/> <!-- person属性,Person类型。 表达的是本类与Person的一对一。 采用基于外键的一对一映射方式,本方有外键方。 --> <many-to-one name="person" class="Person" column="personId" unique="true"></many-to-one> </class> </hibernate-mapping>
基于外键的一对一映射其中一张表中会存在一个外键,这个外键是另外一张表的主键。在没有外键的映射文件中我们使用标签one-to-one来映射,name属性是映射的java对象的属性, class指定了映射的类, property-ref指定另外一方java类中映射为外键的属性。在有主键的一方通过many-to-one标签进行映射,该映射需要注意的是必须加上unique="true"。
基于主键的一对一映射:
Person.hbm.xml配置文件:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.hml.domain"> <class name="Person" table="person"> <id name="id"> <generator class="native"></generator> </id> <property name="name"/> <!-- idCard属性,IdCard类型。 表达的是本类与IdCard的一对一。 采用基于主键的一对一映射方式,本方无外键方。 --> <one-to-one name="idCard" class="IdCard"></one-to-one> </class> </hibernate-mapping>
IdCard.hbm.xml配置文件
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.hml.domain"> <class name="IdCard" table="idCard"> <id name="id"> <!-- 当使用基于主键的一对一映射时, 有外键方的主键生成策略一定要是foreign。
参数property: 生成主键值时所根据的属性对象。 --> <generator class="foreign"> <param name="property">person</param> </generator> </id> <property name="number" /> <!-- person属性,Person类型。 表达的是本类与Person的一对一。
采用基于主键的一对一映射方式,本方有外键方。 constrained="true"强制加上外键约束--> <one-to-one name="person" class="Person" constrained="true"></one-to-one> </class> </hibernate-mapping>
基于主键的映射两边属性都使用one-to-one进行映射,不过在有外键的一方有些不同,有外键的一方主键生成策略一定是foreign,还有就是one-to-one标签中,constrained="true",代表必须加上外键约束。基于主键的映射在插入数据的时候,先插入没有外键的一方的数据,然后把主键当成另外一方的主键进行数据插入:
Hibernate: insert into person (name) values (?) Hibernate: insert into idCard (number, id) values (?, ?)