一、单向关系之:一对多映射
一对多映射关系由one方(Department)来维护
- many方:Employee
package com.revanwang.one2many;
import lombok.Data;
@Data
public class Employee {
private Long id;
private String name;
}
- one方:Department
package com.revanwang.one2many;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class Department {
private Long id;
private String name;
private List<Employee> employees = new ArrayList<>();
}
- test
package com.revanwang.one2many;
import com.revanwang.util.HibernateUtil;
import org.hibernate.Session;
import org.junit.Test;
public class APP {
@Test
public void one2manyTest() {
Employee e1 = new Employee();
e1.setName("W");
Employee e2 = new Employee();
e2.setName("Y");
Department d = new Department();
d.setName("市场部");
d.getEmployees().add(e1);
d.getEmployees().add(e2);
Session session = HibernateUtil.getHibernateSession();
session.beginTransaction();
session.persist(d);
session.persist(e1);
session.persist(e2);
session.getTransaction().commit();
}
}
- One2Many.hbm.xml
<?xml version="1.0"?>
<!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.revanwang.one2many">
<!--many方-->
<class name="Employee" table="t_employee">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"/>
</class>
<!--one方-->
<class name="Department" table="t_department">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name"/>
<!-- Bag集合设置 -->
<bag name="employees">
<key column="depart_id"></key>
<one-to-many class="Employee"/>
</bag>
</class>
</hibernate-mapping>
小结:
- save方法:由Department来维护外键关系,所以得发额外的2条SQL
- 在保存对象的时候,因为对象的关系有one方维护,所以在保存many的时候,不会去修改外键的值;只能在one方保存完成之后,由one方发送额外的update语句去修改many方的外键的值
二、单向关系之:多对一映射
多对一映射关系由many方(Employee)来维护
- many方:Employee
package com.revanwang.many2one;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class Employee {
private Long id;
private String name;
private Department department;
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", department=" + department +
'}';
}
}
- one方:Department
package com.revanwang.many2one;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class Department {
private Long id;
private String name;
@Override
public String toString() {
return "Department{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
- many2oneTest
public void many2oneTest() {
Department d = new Department();
d.setName("研发部");
Employee e1 = new Employee();
e1.setName("S");
e1.setDepartment(d);
Employee e2 = new Employee();
e2.setName("s");
e2.setDepartment(d);
Session session = HibernateUtil.getHibernateSession();
session.beginTransaction();
session.persist(d);
session.persist(e1);
session.persist(e2);
session.getTransaction().commit();
}
- Many2One.hbm.xml
<?xml version="1.0"?>
<!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.revanwang.many2one">
<!--many方-->
<class name="Employee">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"/>
<many-to-one name="department" column="depart_id"/>
</class>
<!--one方-->
<class name="Department">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
小结:
要先保存one方,在保存many方,否则会造成额外的SQL语句
三、双向关系之:多对一
- one方(Department)
package com.revanwang.one2manyandmany2one;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Setter
@Getter
public class Department {
private Long id;
private String name;
private List<Employee> employees = new ArrayList<>();
}
- many方(Employee)
package com.revanwang.one2manyandmany2one;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class Employee {
private Long id;
private String name;
private Department department;
}
- one2manyandmany2one.hbm.xml
<?xml version="1.0"?>
<!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.revanwang.one2manyandmany2one">
<!--many方-->
<class name="Employee">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"/>
<many-to-one name="department" column="depart_id"/>
</class>
<!--one方-->
<class name="Department">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name"/>
<!-- Bag集合设置 -->
<bag name="employees">
<key column="department_id"></key>
<one-to-many class="Employee"/>
</bag>
</class>
</hibernate-mapping>
- test
public void appTest() {
Department d = new Department();
d.setName("销售部");
Employee e1 = new Employee();
e1.setName("H");
e1.setDepartment(d);
Employee e2 = new Employee();
e2.setName("h");
e2.setDepartment(d);
d.getEmployees().add(e1);
d.getEmployees().add(e2);
Session session = HibernateUtil.getHibernateSession();
session.beginTransaction();
session.persist(d);
session.persist(e1);
session.persist(e2);
session.getTransaction().commit();
}
- db 双向关系DB.png
-
SQL
Hibernate:
insert
into
Department
(name)
values
(?)
Hibernate:
insert
into
Employee
(name, depart_id)
values
(?, ?)
Hibernate:
insert
into
Employee
(name, depart_id)
values
(?, ?)
Hibernate:
update
Employee
set
department_id=?
where
id=?
Hibernate:
update
Employee
set
department_id=?
where
id=?
小结:因为先保存one方再保存many方,所以many方的外键(one方的主键)其实是已经确定的,所以不应该在发送update来更新Employee的外键,造成这样现象的原因是因为one方和many方都在维护双向的关系的原因,所以需要一方放弃维护关系,那么该由哪一方来放弃维护关系?由于外键是在many方显示的,所以many方来维护关系是最恰当的,所以one方来放弃关系的维护
- 控制反转(inverse):inverse=true:表示己方不维护关系
<?xml version="1.0"?>
<!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.revanwang.one2manyandmany2one">
<!--many方-->
<class name="Employee">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"/>
<many-to-one name="department" column="depart_id"/>
</class>
<!--one方-->
<class name="Department">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name"/>
<!-- Bag集合设置 -->
<bag name="employees" inverse="true">
<key column="department_id"></key>
<one-to-many class="Employee"/>
</bag>
</class>
</hibernate-mapping>
- inverse=true时DB inverseDB.png
- SQL
Hibernate:
insert
into
Department
(name)
values
(?)
Hibernate:
insert
into
Employee
(name, depart_id)
values
(?, ?)
Hibernate:
insert
into
Employee
(name, depart_id)
values
(?, ?)
小结:
- 1、one方inverse=true时,发送SQL时在没有执行update了,减少了SQL语句发送
- 2、在映射一对多的双向关联关系时,应该在one方把<set>/<list>/<bag>元素的inverse属性设置为true,提高性感
- 3、在建立两个对象的双向关联时,应该同时修改两端对象的相应属性
- 4、当删除双向的关联关系时,也应该修改两端对象的相应属性
四、关系映射之:多对多关系映射
- Student
package com.revanwang.many2many;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Setter
@Getter
public class Student {
private Long id;
private String name;
private List<Teacher> teachers = new ArrayList<>();
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
- Teacher
package com.revanwang.many2many;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Setter
@Getter
public class Teacher {
private Long id;
private String name;
private List<Student> students = new ArrayList<>();
@Override
public String toString() {
return "Teacher{" +
"id=" + id +
", name='" + name + '\'' +
", students=" + students +
'}';
}
}
- APPTest
package com.revanwang.many2many;
import com.revanwang.util.HibernateUtil;
import org.hibernate.Session;
import org.junit.Before;
import org.junit.Test;
public class APP {
@Before
public void APPTest() {
Student s1 = new Student();
s1.setName("s1");
Student s2 = new Student();
s2.setName("s2");
Teacher t1 = new Teacher();
t1.setName("t1");
Teacher t2 = new Teacher();
t2.setName("t2");
s1.getTeachers().add(t1);
s1.getTeachers().add(t2);
s2.getTeachers().add(t1);
t1.getStudents().add(s1);
t1.getStudents().add(s2);
Session session = HibernateUtil.getHibernateSession();
session.beginTransaction();
session.persist(s1);
session.persist(s2);
session.persist(t1);
session.persist(t2);
session.getTransaction().commit();
}
@Test
public void testGet() {
Session session = HibernateUtil.getHibernateSession();
session.beginTransaction();
Teacher teacher = (Teacher) session.load(Teacher.class, 1L);
session.getTransaction().commit();
System.out.println(teacher);
}
}
- many2many.xml
<?xml version="1.0"?>
<!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.revanwang.many2many">
<!--student-->
<class name="Student">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"/>
<!--
在配置集合时:
1、table:中间表的名称
2、key:在中间表中关联我的主键(Student表的主键)
3、many-to-many:表示对象的关系
4、class:对象类型
5、column:在中间表中哪个列作为外键关联到对方的主键(Teacher表的主键)
-->
<bag name="teachers" table="teacher_student">
<key column="s_id"></key>
<many-to-many class="Teacher" column="t_id"/>
</bag>
</class>
<!--Teacher-->
<class name="Teacher">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"/>
<bag name="students" table="teacher_student" inverse="true">
<key column="t_id"/>
<many-to-many class="Student" column="s_id"/>
</bag>
</class>
</hibernate-mapping>
小结:
- 1、如果使用set集合,中间表会为两个外键创建一个复合主键;
- 2、必须让一边放弃关系的维护,哪边在负责维护这个关系,就让对方放弃关系的管理(设计到页面上,哪个模块在添加另外一个对象,另外一个对象就放弃关系的管理)
五、关系映射之:组件关系映射
- Address
package com.revanwang.component;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class Address {
private String province;
private String city;
private String street;
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", street='" + street + '\'' +
'}';
}
}
- Company
package com.revanwang.component;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class Company {
private Long id;
private String name;
private Address address;
private Address regAddress;
@Override
public String toString() {
return "Company{" +
"id=" + id +
", name='" + name + '\'' +
", address=" + address +
", regAddress=" + regAddress +
'}';
}
}
- component.xml
<?xml version="1.0"?>
<!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.revanwang.component">
<class name="Company">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"/>
<!--组件映射-->
<component name="address" class="Address">
<property name="province"/>
<property name="city"/>
<property name="street"/>
</component>
<component name="regAddress" class="Address">
<property name="province" column="regProvince"/>
<property name="city" column="regCity"/>
<property name="street" column="regStreet"/>
</component>
</class>
</hibernate-mapping>
- APPTest
package com.revanwang.component;
import com.revanwang.util.HibernateUtil;
import org.hibernate.Session;
import org.junit.Test;
public class APP {
@Test
public void APPTest() {
Address address = new Address();
address.setCity("杭州");
address.setProvince("余杭区");
address.setStreet("仓前");
Address regAddress = new Address();
regAddress.setCity("杭州_reg");
regAddress.setProvince("余杭区_reg");
regAddress.setStreet("仓前_reg");
Company company = new Company();
company.setName("Revan");
company.setAddress(address);
company.setRegAddress(regAddress);
Session session = HibernateUtil.getHibernateSession();
session.beginTransaction();
session.persist(company);
session.getTransaction().commit();
}
}
-
component_DB
component.png
六、继承映射
- User
package com.revanwang.extend;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class User {
private Long id;
private String name;
}
- Employee
package com.revanwang.extend;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
@Setter
@Getter
public class Employee extends User {
private BigDecimal salary;
}
- Customer
package com.revanwang.extend;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class Customer extends User {
private String address;
}
6.1:继承关系生成一张表
- extend_single.xml
<?xml version="1.0"?>
<!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.revanwang.extend">
<class name="User" discriminator-value="0">
<id name="id">
<generator class="native"></generator>
</id>
<!--
设置用户类型的识别列:
type=0:一般用户
type=1:员工
type=2:客户
-->
<discriminator column="type" type="int"/>
<property name="name"/>
<subclass name="Employee" discriminator-value="1">
<property name="salary"/>
</subclass>
<subclass name="Customer" discriminator-value="2">
<property name="address"/>
</subclass>
</class>
</hibernate-mapping>
- APPTest
public void APPTest() {
User user = new User();
user.setName("WRW");
Employee employee = new Employee();
employee.setName("Emp");
employee.setSalary(new BigDecimal("10086"));
Customer customer = new Customer();
customer.setName("Customer");
customer.setAddress("浙江省杭州市");
Session session = HibernateUtil.getHibernateSession();
session.beginTransaction();
session.persist(user);
session.persist(employee);
session.persist(customer);
session.getTransaction().commit();
}
}
-
db
extend_single.png
小结:
- 由于继承关系生成一张表时,所以需要使用一个标识来区别每个不同类型
- load方法
public void getTest() {
Session session = HibernateUtil.getHibernateSession();
session.beginTransaction();
User user = (User) session.load(Employee.class, 2L);
session.getTransaction().commit();
System.out.println(user);
}
- SQL
select
employee0_.id as id1_0_0_,
employee0_.name as name3_0_0_,
employee0_.salary as salary4_0_0_
from
User employee0_
where
employee0_.id=?
and employee0_.type=1
发现SQL中where语句中多了一个type=1条件,如何判断type是什么(0、1、2),通过load(Employee.class, 2L)可知要查询的是Employee类型,在通过extend_single.xml文件可知,Employee类型的type值是1
6.2:每个具体类一张表
- extend_union.xml
<?xml version="1.0"?>
<!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.revanwang.extend">
<class name="User">
<id name="id">
<generator class="org.hibernate.id.enhanced.TableGenerator"/>
</id>
<property name="name"/>
<!--
每个子类一张表:
每个实体类属性都对应着一张表
此时因为继承体系中所有实体类必须使用同一个主键
因此,不能使用native生成策略
-->
<union-subclass name="Employee">
<property name="salary"/>
</union-subclass>
<union-subclass name="Customer">
<property name="address"/>
</union-subclass>
</class>
</hibernate-mapping>
6.3:每个子类一张表
- extend_joined.xml
<?xml version="1.0"?>
<!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.revanwang.extend">
<class name="User">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<!--
每个具体类一张表:
将子类和父类相同的属性保存在父类表中,而子类增加的属性,只保存在子类表中
<key column="/>:共享主键(即是主键又是外键)
-->
<joined-subclass name="Employee">
<key column="employee_id"/>
<property name="salary"/>
</joined-subclass>
<joined-subclass name="Customer">
<key column="customer_id"/>
<property name="address"/>
</joined-subclass>
</class>
</hibernate-mapping>
小结:
-
1、继承关系生成一张表
- 优点:支持多条查询,查询继承关系中的父类和子类数据时只需要一次简单的查询即可查询相应的数据。性能最高
- 缺点:需要添加额外列来区分子类,表中有很多列为null,不能为所有子类属性对应字段,设置为NOT NULL约束
-
2、每个子类一张表
- 优点:符合关系数据模型设计规则
- 缺点:新增、查询需要操作多张表,性能不如继承关系一张表
-
3、每个具体类一张表
- 优点:实现继承体系最简单
- 缺点:多态查询性能最低
七、组合关系映射
聚合关系
一般是由2个模块分别管理整体和部分的对象
组合关系
是一种强聚合关系,强调了整体和部分不能分开,一般把整体部分称之为主对象(父对象),把部分称之为从对象(子对象)
比如:销售订单(SaleBill)和销售订单明细(SaleBillltem)
组合关系的两个对象都是在同一个模块中管理的,都是在主对象中做管理,所以只需要设计SaleBillDAO,不需要设计SaleBillltemDAO,因为订单明细的CRUD都是在销售订单中完成的。
操作分析:
增加:
1):保存销售订单信息.
2):保存销售订单明细信息.
删除:
1):删除销售订单明细信息.
2):删除销售订单信息.
修改:
1):更新销售订单信息.
2):保存新的销售订单明细.
3):更新被修改过的销售订单明细信息.
4):删除需要被删除的销售订单明细(打破主从关系).
查询:
1):查询出销售订单
2):查询所管理的销售订单明细.
- SaleBill
package com.revanwang.cascade;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class SaleBill {
private Long id;
private String sn;
private List<SaleBillItem> items = new ArrayList<>();
}
- SaleBillItem
package com.revanwang.cascade;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class SaleBillItem {
private Long id;
private String product;
private SaleBill bill;
@Override
public String toString() {
return "SaleBillItem{" +
"id=" + id +
", product='" + product + '\'' +
'}';
}
}
- salebill.xml
<?xml version="1.0"?>
<!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.revanwang.cascade">
<!--one方-->
<class name="SaleBill">
<id name="id">
<generator class="native"></generator>
</id>
<property name="sn"/>
<bag name="items" inverse="true" cascade="all-delete-orphan">
<key column="bill_id"></key>
<one-to-many class="SaleBillItem"/>
</bag>
</class>
<!--many方-->
<class name="SaleBillItem">
<id name="id">
<generator class="native"></generator>
</id>
<property name="product"/>
<many-to-one name="bill" column="bill_id"/>
</class>
</hibernate-mapping>
- APPTest
package com.revanwang.cascade;
import com.revanwang.util.HibernateUtil;
import org.hibernate.Session;
import org.junit.Before;
import org.junit.Test;
public class APP {
@Before
public void APPTest() {
SaleBill saleBill = new SaleBill();
saleBill.setSn("10086");
SaleBillItem item1 = new SaleBillItem();
item1.setProduct("显示器");
item1.setBill(saleBill);
SaleBillItem item2 = new SaleBillItem();
item2.setProduct("笔记本");
item2.setBill(saleBill);
SaleBillItem item3 = new SaleBillItem();
item3.setProduct("台式机");
item3.setBill(saleBill);
saleBill.getItems().add(item1);
saleBill.getItems().add(item2);
saleBill.getItems().add(item3);
Session session = HibernateUtil.getHibernateSession();
session.beginTransaction();
session.persist(saleBill);
session.persist(item1);
session.persist(item2);
session.persist(item3);
session.getTransaction().commit();
}
@Test
public void getTest() {
Session session = HibernateUtil.getHibernateSession();
session.beginTransaction();
SaleBill saleBill = (SaleBill) session.load(SaleBill.class, 1L);
session.getTransaction().commit();
System.out.println(saleBill);
}
}
组合:只可能由一个模块来同时管理整体和部分的对象.
级联:cascade:代表级联操作,把主对象的操作遍历的在每一个从对象上面执行相同的操作
级联选项:
- 1、none:缺省值,不做任何级联操作.
- 2、save-update:在主对象上面执行save/update的方法,级联的保存临时的从对象,更新游离的从对象
- 3、delete:在主对象上面执行delete方法,级联的删除所有的从对象(要能够级联删除,必须要求在当前主对象里有从对象)
- 4、all:save-update+delete
- 5、delete-orphan:删除和主对象打破了关系的明细对象(孤立)
- 6、all-delete-orphan:all+delete-orphan
网友评论