前言
前面的我们使用的是一个表的操作, 但我们实际的开发中不可能只使用一个表的... 因此, 本博文主要讲解关联映射
集合映射
需求分析: 当用户购买商品, 用户可能有多个地址
数据库表
我们一般如下图一样设计数据库表, 一般我们不会在 User 表设计多个列来保存地址的因为每个用户的地址个数都不一的, 会造成数据冗余
创建两张数据表, 一张保存着用户的信息, 一张保存着地址的信息地址表使用外键来引用用户表
实体
由于地址只是使用 String 类型来保存着, 那么我们直接使用一个 User 对象就可以了
- public class User {
- private String id;
- private String username;
- private String password;
- private Set<String> address;
- // 各种 setter 和 getter
映射文件
- <?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">
- <!-- 在 domain 包下 -->
- <hibernate-mapping package="zhongfucheng.domain">
- <class name="User" table="user">
- <!-- 主键映射 -->
- <id name="id" column="id" >
- <generator class="native"/>
- </id>
- <!-- 普通字段映射 -->
- <property name="username" column="username"></property>
- <property name="password" column="password"></property>
- <!--
- Set:
- name: 映射集合的名称
- table: 集合的属性要映射到哪张表(address)
- key:
- column: 指定要映射的表 (address) 中的外键列
- element: 要映射的表的其他字段
- 类型一定要指定!
- -->
- <set name="address" table="address">
- <key column="user_id"></key>
- <element column="addr" type="string"></element>
- </set>
- </class>
- </hibernate-mapping>
测试:
- package zhongfucheng.domain;
- import org.hibernate.SessionFactory;
- import org.hibernate.Transaction;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.classic.Session;
- /**
- * Created by ozc on 2017/5/6.
- */
- public class App {
- public static void main(String[] args) {
- // 创建对象
- User user = new User();
- user.setUsername("123");
- user.setPassword("1234");
- user.getAddress().add("广州");
- // 获取加载配置管理类
- Configuration configuration = new Configuration();
- // 加载 User 的映射文件!
- configuration.configure().addClass(User.class);
- // 创建 Session 工厂对象
- SessionFactory factory = configuration.buildSessionFactory();
- // 得到 Session 对象
- Session session = factory.openSession();
- // 使用 Hibernate 操作数据库, 都要开启事务, 得到事务对象
- Transaction transaction = session.getTransaction();
- // 开启事务
- transaction.begin();
- session.save(user);
- // 提交事务
- transaction.commit();
- // 关闭 Session
- session.close();
- }
- }
List 集合映射配置
既然我们现在已经会了如何配置 Set 集合了, List 集合又怎么配置呢??
想一下, List 集合和 Set 集合有什么区别...List 集合是有序的, 因此要多配置一个列来维护数据的有序性!
- <list name="address" table="address">
- <key column="user_id"></key>
- <!--index 是关键字, 不能使用!!!!-->
- <list-index column="indexNum"></list-index>
- <element column="addr" type="string"></element>
- </list>
Map 集合映射配置
Map 集合和 Collection 集合的区别就是键值对模型, 那么在配置的时候多一个 key 即可!
- <map name="address" table="address">
- <key column="user_id" ></key>
- <map-key type="string" column="short"></map-key>
- <element type="string" column="addr"></element>
- </map>
一对多和多对一
上面我们讲解了集合映射是怎么配置的, 那集合装载的元素有没有可能是对象呢?? 而不是简单的 String 类型.. 那个就太多了! 一般地, 我们集合装载的都是对象, 而不是简单的 String, 如果我们的装载在集合的数据有很多类型, 那么 String 就不能用了!...
需求: 部门与员工之间的关系
一个部门有多个员工; 一对多
多个员工, 属于一个部门 多对一
设计数据库表
员工表应该使用一个外键来记住部门表这样才可以维护员工和部门之间的关系
设计实体
部门实体要使用一个集合来记住所有的员工, 员工要使用一个对象引用着部门
- Dept.java
- package zhongfucheng.domain;
- import java.util.HashSet;
- import java.util.Set;
- /**
- * Created by ozc on 2017/5/6.
- */
- public class Dept {
- private int id ;
- private Set<Employee> set = new HashSet<>();
- private String deptName;
- public String getDeptName() {
- return deptName;
- }
- public void setDeptName(String deptName) {
- this.deptName = deptName;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public Set<Employee> getSet() {
- return set;
- }
- public void setSet(Set<Employee> set) {
- this.set = set;
- }
- }
- Employee.java
- package zhongfucheng.domain;
- /**
- * Created by ozc on 2017/5/6.
- */
- public class Employee {
- private int id;
- private String empName;
- private double salary;
- private Dept dept;
- public Dept getDept() {
- return dept;
- }
- public void setDept(Dept dept) {
- this.dept = dept;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getEmpName() {
- return empName;
- }
- public void setEmpName(String empName) {
- this.empName = empName;
- }
- public double getSalary() {
- return salary;
- }
- public void setSalary(double salary) {
- this.salary = salary;
- }
- }
映射分析
我们在写映射配置文件之前, 分析一下怎么写以部门映射配置文件为例...
现在使用了一个 Set 集合来维护与员工的关系, Set 集合的类型是员工对象... 因此在映射文件中需要以下几点
映射集合属性的名称(employees)
映射集合对应的数据表(employee)
对应的数据表的外键字段(dept_id)
集合中的元素类型 (Employee) 通过这个类型, Hibernate 就可以找到对应类型的映射文件, 从而得到对应的信息!
部门映射配置文件
- <?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">
- <!-- 在 domain 包下 -->
- <hibernate-mapping package="zhongfucheng.domain">
- <class name="Dept" table="dept">
- <id column="id" name="id">
- <generator class="native">
- </generator>
- </id>
- <!-- 普通字段映射 -->
- <property name="deptName" column="deptname">
- </property>
- <!-- 维护关系的是 Set 集合, 对应 employee 表 -->
- <set cascade="save-update" name="set" table="employee">
- <!--employee 的外键列是 dept_no-->
- <key column="dept_no">
- </key>
- <!-- 一个部门对应多个员工, 集合的类型是 Employee-->
- <one-to-many class="Employee">
- </one-to-many>
- </set>
- </class>
- </hibernate-mapping>
员工映射配置文件
- <?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">
- <!-- 在 domain 包下 -->
- <hibernate-mapping package="zhongfucheng.domain">
- <class name="Employee" table="employee">
- <id column="id" name="id">
- <generator class="native">
- </generator>
- </id>
- <!-- 普通字段数据 -->
- <property name="empName" column="empName"></property>
- <property name="salary" column="salary"></property>
- <!--Hibernate 这个标签可看成在当前表中设置一个外键 dept_no-->
- <many-to-one name="dept" class="Dept" column="dept_no"></many-to-one>
- </class>
- </hibernate-mapping>
在一的一方测试
- package zhongfucheng.domain;
- import org.hibernate.SessionFactory;
- import org.hibernate.Transaction;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.classic.Session;
- /**
- * Created by ozc on 2017/5/6.
- */
- public class App {
- public static void main(String[] args) {
- // 创建对象
- Dept dept = new Dept();
- dept.setDeptName("开发部");
- Employee zs = new Employee();
- zs.setEmpName("张珊");
- zs.setSalary(1111);
- Employee ls = new Employee();
- ls.setEmpName("李四");
- ls.setSalary(2222);
- // 添加关系
- dept.getSet().add(zs);
- dept.getSet().add(ls);
- // 获取加载配置管理类
- Configuration configuration = new Configuration();
- // 加载 User 的映射文件!
- configuration.configure().addClass(Dept.class).addClass(Employee.class);
- // 创建 Session 工厂对象
- SessionFactory factory = configuration.buildSessionFactory();
- // 得到 Session 对象
- Session session = factory.openSession();
- // 使用 Hibernate 操作数据库, 都要开启事务, 得到事务对象
- Transaction transaction = session.getTransaction();
- // 开启事务
- transaction.begin();
- session.save(dept);
- session.save(zs);
- session.save(ls);
- // 提交事务
- transaction.commit();
- // 关闭 Session
- session.close();
- }
- }
Hibernate 执行了 5 条 SQL 语句
在多的一方测试
- package zhongfucheng.domain;
- import org.hibernate.SessionFactory;
- import org.hibernate.Transaction;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.classic.Session;
- /**
- * Created by ozc on 2017/5/6.
- */
- public class App {
- public static void main(String[] args) {
- // 创建对象
- Dept dept = new Dept();
- dept.setDeptName("开发部");
- Employee zs = new Employee();
- zs.setEmpName("张珊");
- zs.setSalary(1111);
- Employee ls = new Employee();
- ls.setEmpName("李四");
- ls.setSalary(2222);
- // 维护关系
- zs.setDept(dept);
- ls.setDept(dept);
- // 获取加载配置管理类
- Configuration configuration = new Configuration();
- // 加载 User 的映射文件!
- configuration.configure().addClass(Dept.class).addClass(Employee.class);
- // 创建 Session 工厂对象
- SessionFactory factory = configuration.buildSessionFactory();
- // 得到 Session 对象
- Session session = factory.openSession();
- // 使用 Hibernate 操作数据库, 都要开启事务, 得到事务对象
- Transaction transaction = session.getTransaction();
- // 开启事务
- transaction.begin();
- session.save(dept);
- session.save(zs);
- session.save(ls);
- // 提交事务
- transaction.commit();
- // 关闭 Session
- session.close();
- }
- }
Hibernate 执行了 3 条 SQL
一对多和多对一总结
在一对多与多对一的关联关系中, 保存数据最好的通过多的一方来维护关系, 这样可以减少 update 语句的生成, 从而提高 hibernate 的执行效率!
配置一对多与多对一, 这种叫双向关联
只配置一对多, 叫单项一对多
只配置多对一, 叫单项多对一
值得注意是: 配置了哪一方, 哪一方才有维护关联关系的权限!
当我在部门中不配置员工的关联关系了, 那么在操作部门的时候就不能得到员工的数据了也就是: 在保存部门时, 不能同时保存员工的数据
多对多映射
需求: 一个项目由多个员工开发, 一个员工开发多个项目
设计数据库表
一般地, 如果是多对多的映射, 我们都会使用一张中间表来保存它们的关联关系....
设计实体
我们在设计实体的时候, 一般是核心数据表对应一个 JavaBean 实体中间表并不是核心数据表, 那么我们将会设计两个 JavaBean 对象
- project.java
- package zhongfucheng.many2many;
- import java.util.HashSet;
- import java.util.Set;
- /**
- * Created by ozc on 2017/5/7.
- */
- public class Project {
- private int projectId;
- private String projectName;
- // 使用 Set 集合与 developer 实体维护关系
- private Set<Developer> developers = new HashSet<>();
- public int getProjectId() {
- return projectId;
- }
- public void setProjectId(int projectId) {
- this.projectId = projectId;
- }
- public String getProjectName() {
- return projectName;
- }
- public void setProjectName(String projectName) {
- this.projectName = projectName;
- }
- public Set<Developer> getDevelopers() {
- return developers;
- }
- public void setDevelopers(Set<Developer> developers) {
- this.developers = developers;
- }
- }
- developer.java
- package zhongfucheng.many2many;
- import java.util.HashSet;
- import java.util.Set;
- /**
- * Created by ozc on 2017/5/7.
- */
- public class Developer {
- private int developerId;
- private String developerName;
- // 使用 Set 集合来维护与 Project 关系
- private Set<Project> projects = new HashSet<>();
- public int getDeveloperId() {
- return developerId;
- }
- public void setDeveloperId(int developerId) {
- this.developerId = developerId;
- }
- public String getDeveloperName() {
- return developerName;
- }
- public void setDeveloperName(String developerName) {
- this.developerName = developerName;
- }
- public Set<Project> getProjects() {
- return projects;
- }
- public void setProjects(Set<Project> projects) {
- this.projects = projects;
- }
- }
映射配置文件
以项目映射文件为例: 我们不急着写, 首先来分析一下关键点...... 想要在多对多映射中产生正确的关联关系, 下面几步必不可少:
配置映射集合的属性(developers)
映射集合对应的中间表(developer_project)
中间表的外键字段(project_id)
集合元素的类型(Developer)
中间表另外的外键字段(developer_id)
Project 和 Developer 的映射文件都需要这几个关键步骤
Project 映射文件
- <?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">
- <!-- 在 domain 包下 -->
- <hibernate-mapping package="zhongfucheng.many2many">
- <class name="Project" table="Project">
- <!-- 映射主键 -->
- <id name="projectId" column="projectId">
- <generator class="native"></generator>
- </id>
- <!-- 映射普通字段 -->
- <property name="projectName" column="projectName"></property>
- <!-- 映射多对多的关系 -->
- <!--Set 的属性名称为 developers, 对应 developer_project 表 -->
- <set name="developers" table="developer_project">
- <!-- 对应 developer_project 表的外键列 -->
- <key column="project_id"></key>
- <!-- 集合的类型和 developer_project 表的另一个外键列 -->
- <many-to-many column="developer_id" class="Developer"></many-to-many>
- </set>
- </class>
- </hibernate-mapping>
Developer 映射文件
- <?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">
- <!-- 在 domain 包下 -->
- <hibernate-mapping package="zhongfucheng.many2many">
- <class name="Developer" table="Developer">
- <!-- 映射主键 -->
- <id name="developerId" column="developerId">
- <generator class="native"></generator>
- </id>
- <!-- 映射普通字段 -->
- <property name="developerName" column="developerName"></property>
- <!-- 映射多对多的关系 -->
- <!--Set 的属性名称为 developers, 对应 developer_project 表 -->
- <set name="projects" table="developer_project">
- <!-- 对应 developer_project 表的外键列 -->
- <key column="developer_id"></key>
- <!-- 集合的类型和 developer_project 表的另一个外键列 -->
- <many-to-many column="project_id" class="Project"></many-to-many>
- </set>
- </class>
- </hibernate-mapping>
测试
- package zhongfucheng.many2many;
- import org.hibernate.SessionFactory;
- import org.hibernate.Transaction;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.classic.Session;
- /**
- * Created by ozc on 2017/5/6.
- */
- public class App2 {
- public static void main(String[] args) {
- /*
- *
- *
- * Project Developer
- 电商系统
- 曹吉
- 王春
- OA 系统
- 王春
- 老张
- */
- // 创建对象
- Developer cj = new Developer();
- Developer wc = new Developer();
- Developer lz = new Developer();
- Project ds = new Project();
- Project oa = new Project();
- // 设置对象的数据
- cj.setDeveloperName("曹吉");
- wc.setDeveloperName("王春");
- lz.setDeveloperName("老张");
- oa.setProjectName("OA 系统");
- ds.setProjectName("电商系统");
- // 使用 Project 来关联数据在多对多中, 一样的
- oa.getDevelopers().add(wc);
- oa.getDevelopers().add(lz);
- ds.getDevelopers().add(cj);
- ds.getDevelopers().add(wc);
- // 获取加载配置管理类
- Configuration configuration = new Configuration();
- // 加载 User 的映射文件!
- configuration.configure().addClass(Developer.class).addClass(Project.class);
- // 创建 Session 工厂对象
- SessionFactory factory = configuration.buildSessionFactory();
- // 得到 Session 对象
- Session session = factory.openSession();
- // 使用 Hibernate 操作数据库, 都要开启事务, 得到事务对象
- Transaction transaction = session.getTransaction();
- // 开启事务
- transaction.begin();
- // 在 Project 映射文件中设置级联保存了
- session.save(oa);
- session.save(ds);
- // 提交事务
- transaction.commit();
- // 关闭 Session
- session.close();
- }
- }
执行了 9 条 SQL 语句, 数据库中的记录也是正确的
一对一的映射
需求: 用户与身份证信息.. 一个用户对应一个身份证
数据库表设计
对于数据库表设计我们有两种方式
第一种: 在身份证的数据表中设置一个外键来维护用户的关系, 这个外键也应该是唯一的一个用户对应一张身份证
第二种: 在身份证的数据表中使用主键 + 外键的方式来维护用户的关系
设计实体
- idCard.java
- package zhongfucheng.one2one;
- /**
- * Created by ozc on 2017/5/7.
- */
- public class IdCard {
- private int idCardId;
- private String idCardName;
- // 维护与用户之间的关系
- private User user ;
- public int getIdCardId() {
- return idCardId;
- }
- public void setIdCardId(int idCardId) {
- this.idCardId = idCardId;
- }
- public String getIdCardName() {
- return idCardName;
- }
- public void setIdCardName(String idCardName) {
- this.idCardName = idCardName;
- }
- public User getUser() {
- return user;
- }
- public void setUser(User user) {
- this.user = user;
- }
- }
- User.java
- package zhongfucheng.one2one;
- /**
- * Created by ozc on 2017/5/7.
- */
- public class User {
- private int userId;
- private String userName;
- // 维护与身份证一对一的关系
- private IdCard idCard ;
- public int getUserId() {
- return userId;
- }
- public void setUserId(int userId) {
- this.userId = userId;
- }
- public String getUserName() {
- return userName;
- }
- public void setUserName(String userName) {
- this.userName = userName;
- }
- public IdCard getIdCard() {
- return idCard;
- }
- public void setIdCard(IdCard idCard) {
- this.idCard = idCard;
- }
- }
第一种方式映射文件
我们有两种方式来设计数据库中的表实现一对一的关系, 首先我们来挑比较熟悉的外键方式来写映射文件
user 映射文件
- <?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="zhongfucheng.one2one">
- <class name="User" table="User">
- <!-- 映射主键 -->
- <id name="userId" column="userId">
- <generator class="native"></generator>
- </id>
- <!-- 映射普通字段 -->
- <property name="userName" column="userName"></property>
- <!--
- User 是没有外键字段的表
- 一对一的关系的属性名称 name 是 idCard
- 类型是 IdCard
- -->
- <one-to-one name="idCard" class="IdCard"></one-to-one>
- </class>
- </hibernate-mapping>
idCard 映射文件
- <?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="zhongfucheng.one2one">
- <class name="IdCard" table="IdCard">
- <!-- 映射主键 -->
- <id name="idCardId" column="idCardId">
- <generator class="native"></generator>
- </id>
- <!-- 映射普通字段 -->
- <property name="idCardName" column="idCardName"></property>
- <!--idCart 是有外键的表, 要把字段映射成外键, 用的是 manyToOne-->
- <!--
- 外键的属性 name 是 user
- 对应表的字段是 userId
- 属性的类型是 User
- 该字段需要唯一性 unique
- -->
- <many-to-one name="user" column="user_id" class="User" unique="true" cascade="save-update"></many-to-one>
- </class>
- </hibernate-mapping>
测试
要使用 IdCart 来维护 User 的关联关系
如果使用 User 来维护 idCart 的关联关系, idCart 的外键列是为 NULL 的, 因为重头到尾我们都没有给它赋值
而使用 IdCart 来维护 User, 是外键值是根据 User 的主键 id 来生成的
- package zhongfucheng.one2one;
- import org.hibernate.SessionFactory;
- import org.hibernate.Transaction;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.classic.Session;
- /**
- * Created by ozc on 2017/5/6.
- */
- public class App3 {
- public static void main(String[] args) {
- // 创建对象
- User user = new User();
- IdCard idCard = new IdCard();
- // 设置对象的数据
- user.setUserName("你好");
- idCard.setIdCardName("身份证 001");
- // 一对一关联数据
- idCard.setUser(user);
- // 获取加载配置管理类
- Configuration configuration = new Configuration();
- // 加载 User 的映射文件!
- configuration.configure().addClass(User.class).addClass(IdCard.class);
- // 创建 Session 工厂对象
- SessionFactory factory = configuration.buildSessionFactory();
- // 得到 Session 对象
- Session session = factory.openSession();
- // 使用 Hibernate 操作数据库, 都要开启事务, 得到事务对象
- Transaction transaction = session.getTransaction();
- // 开启事务
- transaction.begin();
- // 保存对象的数据, idCard 配置文件使用级联保存
- session.save(idCard);
- // 提交事务
- transaction.commit();
- // 关闭 Session
- session.close();
- }
- }
第二种方式映射文件
因为 IdCart 使用 userId 作为了主键, 因此需要在 JavaBean 中配置多一个属性 UserId... 其他的都不用变
- private int userId;
- public int getUserId() {
- return userId;
- }
- public void setUserId(int userId) {
- this.userId = userId;
- }
在 Hibernate 入门篇讲解配置的时候, 在 generator 节点下还有一个属性没有讲解, 也就是 foreign 属性... 现在来填坑了..
idCard 映射文件
idCart 的映射文件主要在于: 将主键也映射成外键来使用, 这就需要用到 foreign 属性值了
使用 < one-to-one > 标签来配置基于主键的映射
- <?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="zhongfucheng.one2one2.one2one">
- <class name="IdCard" table="IdCard">
- <!-- 映射主键 -->
- <id name="userId" column="userId">
- <!--
- 做主键的同时也做外键
- 外键的类型名称为 user
- -->
- <generator class="foreign">
- <param name="property">user</param>
- </generator>
- </id>
- <!-- 映射普通字段 -->
- <property name="idCardName" column="idCardName"></property>
- <property name="idCardId" column="idCartId"></property>
- <!--
- 有外键的一方:
- 基于主键映射, 使用 oneToOne
- constrained="true" 指定在主键上添加外键约束
- -->
- <one-to-one name="user" class="User" constrained="true"></one-to-one>
- </class>
- </hibernate-mapping>
user 映射文件
- <?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="zhongfucheng.one2one2.one2one">
- <class name="User" table="User">
- <!-- 映射主键 -->
- <id name="userId" column="userId">
- <generator class="native"></generator>
- </id>
- <!-- 映射普通字段 -->
- <property name="userName" column="userName"></property>
- <!--
- User 是没有外键字段的表
- 一对一的关系的属性名称 name 是 idCard
- 类型是 IdCard
- -->
- <one-to-one name="idCard" class="IdCard"></one-to-one>
- </class>
- </hibernate-mapping>
测试
- package zhongfucheng.one2one2.one2one;
- import org.hibernate.SessionFactory;
- import org.hibernate.Transaction;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.classic.Session;
- /**
- * Created by ozc on 2017/5/6.
- */
- public class App4 {
- public static void main(String[] args) {
- // 创建对象
- User user = new User();
- IdCard idCard = new IdCard();
- // 设置对象的数据
- user.setUserName("你好 3");
- idCard.setIdCardName("身份证 003");
- idCard.setIdCardId(4235);
- // 一对一关联数据
- idCard.setUser(user);
- // 获取加载配置管理类
- Configuration configuration = new Configuration();
- // 加载 User 的映射文件!
- configuration.configure().addClass(User.class).addClass(IdCard.class);
- // 创建 Session 工厂对象
- SessionFactory factory = configuration.buildSessionFactory();
- // 得到 Session 对象
- Session session = factory.openSession();
- // 使用 Hibernate 操作数据库, 都要开启事务, 得到事务对象
- Transaction transaction = session.getTransaction();
- // 开启事务
- transaction.begin();
- // 保存对象的数据, idCard 配置文件使用级联保存
- session.save(idCard);
- // 提交事务
- transaction.commit();
- // 关闭 Session
- session.close();
- }
- }
组件映射
Java 主要的类主要有两种方式
组合关系, 组合关系对应的就是组件映射
继承关系, 继承关系对应的就是继承映射
组件映射实际上就是将组合关系的数据映射成一张表, 组件类和被包含的组件类映射成一张表
有的时候, 两个类的关系明显不是继承关系, 但两个类的亲密程度很高, 在一个类里边需要用到另外一个类... 那么就在类中定义一个变量来维护另一个类的关系, 这种就叫组合关系!
需求: 汽车和轮子汽车需要用到轮子, 但是轮子的爸爸不可能是汽车吧?
设计数据库
设计实体
- Wheel.java
- public class Wheel {
- private int count;
- private int size;
- public int getCount() {
- return count;
- }
- public void setCount(int count) {
- this.count = count;
- }
- public int getSize() {
- return size;
- }
- public void setSize(int size) {
- this.size = size;
- }
- }
Car.java, 使用变量维护 Wheel
- package zhongfucheng.aa;
- /**
- * Created by ozc on 2017/5/7.
- */
- public class Car {
- private int id;
- private String name;
- private Wheel wheel;
- public Wheel getWheel() {
- return wheel;
- }
- public void setWheel(Wheel wheel) {
- this.wheel = wheel;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
映射表
使用了一个新标签 < component>, 组件映射标签
- <?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="zhongfucheng.aa">
- <class name="Car" table="Car">
- <!-- 映射主键 -->
- <id name="id" column="id">
- <generator class="native">
- </generator>
- </id>
- <!-- 映射普通字段 -->
- <property name="name" column="name">
- </property>
- <!-- 映射组件字段 -->
- <component name="wheel">
- <property name="count">
- </property>
- <property name="size">
- </property>
- </component>
- </class>
- </hibernate-mapping>
测试
- package zhongfucheng.aa;
- import org.hibernate.SessionFactory;
- import org.hibernate.Transaction;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.classic.Session;
- /**
- * Created by ozc on 2017/5/6.
- */
- public class App5 {
- public static void main(String[] args) {
- // 创建对象
- Wheel wheel = new Wheel();
- Car car = new Car();
- // 设置属性
- wheel.setCount(43);
- wheel.setSize(22);
- car.setName("宝马");
- // 维护关系
- car.setWheel(wheel);
- // 获取加载配置管理类
- Configuration configuration = new Configuration();
- configuration.configure().addClass(Car.class);
- // 创建 Session 工厂对象
- SessionFactory factory = configuration.buildSessionFactory();
- // 得到 Session 对象
- Session session = factory.openSession();
- // 使用 Hibernate 操作数据库, 都要开启事务, 得到事务对象
- Transaction transaction = session.getTransaction();
- // 开启事务
- transaction.begin();
- session.save(car);
- // 提交事务
- transaction.commit();
- // 关闭 Session
- session.close();
- }
- }
传统方式继承
需求: 动物猫猴子猫继承着动物
传统方式继承的特点就是: 有多少个子类就写多少个配置文件.
表结构
我们的表应该是这样的: id 和 name 从 Animal 中继承, catchMouse 是子类的具体行为
实体
- Animal.java
- package zhongfucheng.aa;
- // 动物类
- public abstract class Animal {
- private int id;
- private String name;
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
Cat.java 继承着 Animail
- package zhongfucheng.aa;
- public class Cat extends Animal{
- // 抓老鼠
- private String catchMouse;
- public String getCatchMouse() {
- return catchMouse;
- }
- public void setCatchMouse(String catchMouse) {
- this.catchMouse = catchMouse;
- }
- }
映射文件
简单继承的映射文件很好写, 在属性上, 直接写父类的属性就可以了
但是也有致命的缺点: 如果子类有很多, 就需要写很多的配置文件
- <?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="zhongfucheng.aa" >
- <class name="Cat" table="cat" >
- <!-- 映射主键 -->
- <id name="id" column="id">
- <generator class="native"></generator>
- </id>
- <!--
- 映射普通字段
- 父类的属性直接引用就行了, 比如 name 属性, 直接写就行了!
- -->
- <property name="name" column="name" ></property>
- <property name="catchMouse" column="catchMouse" ></property>
- </class>
- </hibernate-mapping>
测试
- package zhongfucheng.aa;
- import org.hibernate.SessionFactory;
- import org.hibernate.Transaction;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.classic.Session;
- public class App5 {
- public static void main(String[] args) {
- // 创建对象
- Cat cat = new Cat();
- // 设置属性
- cat.setName("大花猫");
- cat.setCatchMouse("捉大老鼠");
- // 获取加载配置管理类
- Configuration configuration = new Configuration();
- configuration.configure().addClass(Cat.class);
- // 创建 Session 工厂对象
- SessionFactory factory = configuration.buildSessionFactory();
- // 得到 Session 对象
- Session session = factory.openSession();
- // 使用 Hibernate 操作数据库, 都要开启事务, 得到事务对象
- Transaction transaction = session.getTransaction();
- // 开启事务
- transaction.begin();
- session.save(cat);
- // 如果取数据时候 Animal 父类接收的话, 需要给出 Anmail 的全名
- // 提交事务
- transaction.commit();
- // 关闭 Session
- session.close();
- }
- }
把所有子类映射成一张表
前面我们采用的是: 每个子类都需要写成一个配置文件, 映射成一张表...
如果子类的结构很简单, 只比父类多几个属性就像上面的例子... 我们可以将所有的子类都映射成一张表中
但是呢, 这样是不符合数据库设计规范的..... 因为表中的数据 ** 可能是猫, 可能是猴子...** 这明显是不合适的...
由于表中可能存在猫, 存在猴子, 为了区分是什么类型的我们需要使用鉴别器
我们了解一下...
数据表
实体
实体和上面雷同, 只多了一个猴子的实体表
- Monkey.java
- public class Monkey extends Animal {
- // 吃香蕉
- private String eatBanana;
- public String getEatBanana() {
- return eatBanana;
- }
- public void setEatBanana(String eatBanana) {
- this.eatBanana = eatBanana;
- }
- }
映射文件
使用了 subClass 这个节点和鉴别器
- <?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="cn.itcast.e_extends2">
- <class name="Animal" table="t_animal">
- <id name="id">
- <generator class="native"></generator>
- </id>
- <!-- 指定鉴别器字段(区分不同的子类) -->
- <discriminator column="type_"></discriminator>
- <property name="name"></property>
- <!--
- 子类: 猫
- 每个子类都用 subclass 节点映射
- 注意: 一定要指定鉴别器字段, 否则报错!
- 鉴别器字段: 作用是在数据库中区别每一个子类的信息, 就是一个列
- discriminator-value="cat_"
- 指定鉴别器字段, 即 type_字段的值
- 如果不指定, 默认为当前子类的全名
- -->
- <subclass name="Cat" discriminator-value="cat_">
- <property name="catchMouse"></property>
- </subclass>
- <!--
- 子类: 猴子
- -->
- <subclass name="Monkey" discriminator-value="monkey_">
- <property name="eatBanana"></property>
- </subclass>
- </class>
- </hibernate-mapping>
测试
加载的是 Animal 父类的映射文件保存的是 cat 和 monkey
- package zhongfucheng.aa;
- import org.hibernate.SessionFactory;
- import org.hibernate.Transaction;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.classic.Session;
- public class App5 {
- public static void main(String[] args) {
- // 创建对象
- Cat cat = new Cat();
- Monkey monkey = new Monkey();
- // 设置属性
- cat.setName("大花猫");
- cat.setCatchMouse("小老鼠");
- monkey.setEatBanana("吃香蕉");
- monkey.setName("大猴子");
- // 获取加载配置管理类
- Configuration configuration = new Configuration();
- // 加载 Animal 的映射文件!
- configuration.configure().addClass(Animal.class);
- // 创建 Session 工厂对象
- SessionFactory factory = configuration.buildSessionFactory();
- // 得到 Session 对象
- Session session = factory.openSession();
- // 使用 Hibernate 操作数据库, 都要开启事务, 得到事务对象
- Transaction transaction = session.getTransaction();
- // 开启事务
- transaction.begin();
- session.save(cat);
- session.save(monkey);
- // 提交事务
- transaction.commit();
- // 关闭 Session
- session.close();
- }
- }
每个类映射一张表(3 张表)
父类和子类都各对应一张表那么就有三张表了
这种结构看起来是完全面向对象, 但是表之间的结构会很复杂, 插入一条子类的信息, 需要两条 SQL
数据表设计
映射文件
使用到了 < joined-subclass >这个节点
- <?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="zhongfucheng.aa">
- <class name="Animal" table="t_animal">
- <id name="id">
- <generator class="native"></generator>
- </id>
- <property name="name"></property>
- <!--
- Animal 下的子类映射成一张表
- 指定子类的类型, 对应的表
- 指定子类的外键字段需要对应 Animal
- 指定子类的普通属性
- -->
- <joined-subclass name="Cat" table="cat_">
- <!--key 对应的是外键字段 -->
- <key column="animal_id"></key>
- <property name="catchMouse"></property>
- </joined-subclass>
- <joined-subclass name="Monkey" table="monkey_">
- <!--key 对应的是外键字段 -->
- <key column="animal_id"></key>
- <property name="eatBanana"></property>
- </joined-subclass>
- </class>
- </hibernate-mapping>
测试
- package zhongfucheng.aa;
- import org.hibernate.SessionFactory;
- import org.hibernate.Transaction;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.classic.Session;
- public class App5 {
- public static void main(String[] args) {
- // 创建对象
- Cat cat = new Cat();
- Monkey monkey = new Monkey();
- // 设置属性
- cat.setName("大花猫");
- cat.setCatchMouse("小老鼠");
- monkey.setEatBanana("吃香蕉");
- monkey.setName("大猴子");
- // 获取加载配置管理类
- Configuration configuration = new Configuration();
- // 加载类对应的映射文件!
- configuration.configure().addClass(Animal.class);
- // 创建 Session 工厂对象
- SessionFactory factory = configuration.buildSessionFactory();
- // 得到 Session 对象
- Session session = factory.openSession();
- // 使用 Hibernate 操作数据库, 都要开启事务, 得到事务对象
- Transaction transaction = session.getTransaction();
- // 开启事务
- transaction.begin();
- session.save(cat);
- session.save(monkey);
- // 提交事务
- transaction.commit();
- // 关闭 Session
- session.close();
- }
- }
每保存一个子类对象需要两条 SQL 语句!
(推荐)每个子类映射一张表, 父类不对应表(2 张表)
使用过了一张表保存所有子类的数据, 这不符合数据库设计规范
每个子类父类都拥有一张表.. 表结构太过于繁琐.. 添加信息时, 过多的 SQL
我们即将使用的是: 每个子类映射成一张表, 父类不对应表... 这和我们传统方式继承是一样的只不过在 hbm.xml 文件中使用了 < union-subclass > 这个节点, 由于有了这个节点, 我们就不需要每个子类都写一个配置文件了
数据库表设计
映射文件
想要父类不映射成数据库表, 只要在 class 中配置为 abstract 即可
使用了 union-subclass 节点, 主键就不能采用自动增长策略了我们改成 UUID 即可当然啦, 对应的实体 id 类型要改成 String
- <?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="zhongfucheng.aa">
- <!--
- 想要父类不映射成表, 设置成 abstract
- -->
- <class name="Animal" abstract="true">
- <!--
- 如果使用了 union-subclass 节点, 那么主键生成策略不能是自增长, 我们改成 uuid 即可
- -->
- <id name="id">
- <generator class="uuid"></generator>
- </id>
- <property name="name"></property>
- <!--
- 将子类的信息都映射成一张表
- 给出属性的名称
- 属性对应的数据库表
- 普通字段
- -->
- <union-subclass name="Cat" table="cat_">
- <property name="catchMouse"></property>
- </union-subclass>
- <union-subclass name="Monkey" table="monkey_">
- <property name="eatBanana"></property>
- </union-subclass>
- </class>
- </hibernate-mapping>
测试
- package zhongfucheng.aa;
- import org.hibernate.SessionFactory;
- import org.hibernate.Transaction;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.classic.Session;
- public class App5 {
- public static void main(String[] args) {
- // 创建对象
- Cat cat = new Cat();
- Monkey monkey = new Monkey();
- // 设置属性
- cat.setName("大花猫");
- cat.setCatchMouse("小老鼠");
- monkey.setEatBanana("吃香蕉");
- monkey.setName("大猴子");
- // 获取加载配置管理类
- Configuration configuration = new Configuration();
- // 加载类对应的映射文件!
- configuration.configure().addClass(Animal.class);
- // 创建 Session 工厂对象
- SessionFactory factory = configuration.buildSessionFactory();
- // 得到 Session 对象
- Session session = factory.openSession();
- // 使用 Hibernate 操作数据库, 都要开启事务, 得到事务对象
- Transaction transaction = session.getTransaction();
- // 开启事务
- transaction.begin();
- session.save(cat);
- session.save(monkey);
- // 提交事务
- transaction.commit();
- // 关闭 Session
- session.close();
- }
- }
组件映射和继承映射总结
由于我们的传统继承映射每个子类都对应一个配置文件, 这样十分麻烦因此 **.hbm.xml 就给出了几个节点供我们使用 **, 分别有以下的情况:
子类父类共有一张表 subclass
不符合数据库设计规范
需要使用鉴别器
子类父类都有自己的表 joined-subclass, 那么就是三张表
表的结构太过繁琐
插入数据时要生成 SQL 至少就要两条
子类拥有自己的表父类不对应表推荐 union-subclass
父类不对应表要使用 abstract 来修饰
主键的 id 不能使用自增长策略, 修改成 UUID 就好了对应的 JavaBean 的 id 设置成 String 就好
来源: https://juejin.im/post/5aa0bcdcf265da239e4da443