ORM(对象/关系数据库映射)
对象关系映射(Object Relational Mapping,简称ORM)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。它完成了面向对象的编程语言到关系型数据库的映射㈣。ORM框架可以看成应用程序和数据库之间的桥梁,通过它,我们可以以面向对象的方式建模、操作,易用、易理解,同时也可以利用关系型数据库系统对数据库访问操作的一些优势。以面向对象的方式操作持久化对象,而ORM框架负责转换成对应的SQL(结构化查询语言)操作。
Hibernate概述
Hibernate是轻量级Java EE应用的持久层解决方案,是流行的ORM框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以面向对象的方式来操纵数据库。Hibernate不仅管理Java类到数据库表的映射,还提供数据查询和获取数据的操作,可以减少人工使用SQL和JDBC处理数据的时间。
Hibernate体系结构
Hibernate实际上是一个提供数据库服务的中间件。Hibernate的持久化解决方案将用户从繁琐的JDBC访问中解脱出来,底层数据库连接获取,数据访问的实现、事务控制都无须用户关心,这种体系结构,将应用层从底层的JDBC/JTA API中抽象出来。Hibernate体系结构如图(注:该图来自于Hibernate官方参考文档)所示。
Hibernate有5个核心的接口、类,如下:
(1)SessionFactory接口:它是单个数据库映射关系经过编译后的内存镜像。所以,SessionFactory的初始化过程比较复杂,同时也耗费大量的资源。为了解决这个问题,Hibernate的设计人员对Hibernate采用了线程安全可以并发调用,实现SessionFactory的实例,多个线程可以并发调用,实现Hibernate实例共享。此外,它还是生成Session的工厂。
(2)Session接口:应用程序与持久储存层之间交互操作的一个单线程对象。Session接口对象实例中提供了持久化操作相关的“增、删、查、改"(CRUD)方面的操作。所有的持久化操作都是在Session的基础上完成的。通常将每一个Session对象实例和一个数据库事务绑定。
(3)事务Transaction接口:通过Transaction接口来达到允许应用系统通过一组一致的事务API来控制事务边界,从而可以封装系统底层的事务操作和访问的接口。将应用系统中涉及事务功能实现代码从底层的事务具体实现的技术细节中抽象出来,最终可以达到应用系统在不同的运行环境平台和Java EE容器之间方便的移植。
(4)Query接口:Query接口能让开发者方便的对数据库表中的数据及与之对应的持久化对象进行查询操作,利用它可以以面向对象的方式来实现对数据库的各种查询操作。
(5)Configuration:通过Configuration获取目前的配置(如数据源、数据库的URL、连接用户名及密码、数据库方言等)并将这些配置加载到内存中,并预启动Hibernate框架,最终达到创建SessionFactory对象的目的。
Hibernate Api
1 |-- Configuration 配置管理类对象 2 3 config.configure(); 加载主配置文件的方法(hibernate.cfg.xml) 4 5 默认加载src/hibernate.cfg.xml 6 7 config.configure(“cn/config/hibernate.cfg.xml”); 加载指定路径下指定名称的主配置文件 8 9 config.buildSessionFactory(); 创建session的工厂对象 10 11 12 13 |-- SessionFactory session的工厂(或者说代表了这个hibernate.cfg.xml配置文件) 14 15 sf.openSession(); 创建一个sesison对象 16 17 sf.getCurrentSession(); 创建session或取出session对象 18 19 20 21 |--Session session对象维护了一个连接(Connection), 代表了与数据库连接的会话。 22 23 Hibernate最重要的对象: 只用使用hibernate与数据库操作,都用到这个对象 24 25 session.beginTransaction(); 开启一个事务; hibernate要求所有的与数据库的操作必须有事务的环境,否则报错! 26 27 28 29 |-- Transaction hibernate事务对象
更新:
session.save(obj); 保存一个对象 session.update(emp); 更新一个对象 session.saveOrUpdate(emp); 保存或者更新的方法: 没有设置主键,执行保存;有设置主键,执行更新操作;如果设置主键不存在报错!
主键查询: session.get(Employee.class, 1); 主键查询,及时加载,只要调用get方法立刻向数据库查询。
session.load(Employee.class, 1); 主键查询,默认使用懒加载,当用到数据的时候才向数据库查询。
在这里,我们顺便谈谈懒加载(延迟加载)是什么,
懒加载(lazy):当用到数据的时候才向数据库查询,这就是hibernate的懒加载特性。
目的:提供程序执行效率!
Hibernate跟数据库打交道,那么查询数据是必不可少的。Hibernate提供了几种查询方式。
HQL查询:
HQL: hibernate query language 即hibernate提供的面向对象的查询语言
查询的是对象以及对象的属性。
区分大小写。
Criteria查询:
完全面向对象的查询。
本地SQL查询:
复杂的查询,就要使用原生态的sql查询,也可以,就是本地sql查询的支持!
(缺点: 不能跨数据库平台!)
为什么要用Hibernate:
1.对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
2.Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他能很大程度的简化DAO层的编码工作
3.hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。
4.hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。
Hibernate运行过程:
1.通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件
2.由hibernate.cfg.xml中的<mappingresource="com/xx/User.hbm.xml"/>读取并解析映射信息
3.通过config.buildSessionFactory();//创建SessionFactory
4.sessionFactory.openSession();//打开Sesssion
5.session.beginTransaction();//创建事务Transation
6.persistent operate持久化操作 //一般指Save这个方法
7.session.getTransaction().commit();//提交事务
8.关闭Session
9.关闭SesstionFactory
说完这个工作原理,我们来个Demo试下。
开发工具:myeclipse2014
操作系统:win10
数据库:MySQL5.5
首先,在myeclipse2014下新建web项目。
导入hibernate jar包。步骤,右击项目,选择“Myeclipse”,再选择Project Facets 【Capabilities】 ,再点击“Install Hibernate Facet”。
进入该窗口。
点击Next。
把红色的勾去掉,再点击Next。
填写数据库配置信息。填写完再Finish即可。
新建实体类User
public class User { private int id; private String username; private String password; //省略get,set方法 }
User映射文件
<?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.sun.bean"> <!--类名,表名,数据库名 --> <class name="User" table="user" catalog="mydb"> <id name="id" column="id"> <generator class="native" /> </id> <property name="username" column="username"/> <property name="password" column="password"/> </class> </hibernate-mapping>
Demo类
public class Demo { public static void main(String[] args) { // 生成Configuration对象 Configuration cfg = new Configuration(); // 读取配置文件 cfg.configure(); // 通过cf对象生成SessionFactory对象 SessionFactory sf = cfg.buildSessionFactory(); // 通过SessionFactory得到session Session session = sf.openSession(); // 开启事务 Transaction tx = session.beginTransaction(); // 生成user对象,并通过set方法赋值 User user1 = new User(); user1.setUsername("system"); user1.setPassword("111"); User user2 = new User(); user2.setUsername("java"); user2.setPassword("2222"); // 保存数据 session.save(user1); session.save(user2); // 提交事务 tx.commit(); } }
在hibernate.cfg.xml中我们可以配置数据库连接参数
下面,我们讲讲hibernate.cfg.xml中关于数据库表的配置参数;
#hibernate.hbm2ddl.auto create-drop 每次在创建sessionFactory时候执行创建表;
当调用sesisonFactory的close方法的时候,删除表!
#hibernate.hbm2ddl.auto create 每次都重新建表; 如果表已经存在就先删除再创建
#hibernate.hbm2ddl.auto update 如果表不存在就创建; 表存在就不创建;
#hibernate.hbm2ddl.auto validate (生成环境时候) 执行验证: 当映射文件的内容与数据库表结构不一样的时候就报错!
在hibernate中,提供SchemaExport 类,让我们可以使用它根据hibernate.cfg.xml的配置来自动建表,代码如下;
public class App_ddl { // 自动建表 @Test public void testCreate() throws Exception { // 创建配置管理类对象 Configuration config = new Configuration(); // 加载主配置文件 config.configure(); // 创建工具类对象 SchemaExport export = new SchemaExport(config); // 建表 // 第一个参数: 是否在控制台打印建表语句 // 第二个参数: 是否执行脚本 export.create(true, true); } }
接下来我们,谈谈实体类映射文件XX.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"> <!-- 映射文件: 映射一个实体类对象; 描述一个对象最终实现可以直接保存对象数据到数据库中。 --> <!-- package: 要映射的对象所在的包(可选,如果不指定,此文件所有的类都要指定全路径) auto-import 默认为true, 在写hql的时候自动导入包名 如果指定为false, 再写hql的时候必须要写上类的全名; 如:session.createQuery("from cn.itcast.c_hbm_config.Employee").list(); --> <hibernate-mapping package="com.sun.bean" auto-import="true"> <!-- class 映射某一个对象的(一般情况,一个对象写一个映射文件,即一个class节点) name 指定要映射的对象的名称 table 指定对象对应的表; 如果没有指定表名,默认与对象名称一样 --> <class name="Employee" table="employee"> <!-- 主键 ,映射--> <id name="empId" column="id"> <!-- 主键的生成策略 identity 自增长(mysql,db2) sequence 自增长(序列), oracle中自增长是以序列方法实现 native 自增长【会根据底层数据库自增长的方式选择identity或sequence】 如果是mysql数据库, 采用的自增长方式是identity 如果是oracle数据库, 使用sequence序列的方式实现自增长 increment 自增长(会有并发访问的问题,一般在服务器集群环境使用会存在问题。) assigned 指定主键生成策略为手动指定主键的值 uuid 指定uuid随机生成的唯一的值 foreign (外键的方式, one-to-one讲) --> <generator class="native"/> </id> <!-- 普通字段映射 property name 指定对象的属性名称 column 指定对象属性对应的表的字段名称,如果不写默认与对象属性一致。 length 指定字符的长度, 默认为255 type 指定映射表的字段的类型,如果不指定会匹配属性的类型 java类型: 必须写全名 hibernate类型: 直接写类型,都是小写 --> <property name="empName" column="empName" type="java.lang.String" length="20"></property> <property name="workDate" type="java.util.Date"></property> <!-- 如果列名称为数据库关键字,需要用反引号或改列名。 --> <property name="desc" column="`desc`" type="java.lang.String"></property> </class> </hibernate-mapping>
复合主键类
public class CompositeKeys implements Serializable{ private String userName; private String address; // 省略get/set方法 } public class User { // 名字跟地址,不会重复 private CompositeKeys keys; private int age; }
User.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.sun.bean" auto-import="true"> <class name="User"> <!-- 复合主键映射 --> <composite-id name="keys"> <key-property name="userName" type="string"></key-property> <key-property name="address" type="string"></key-property> </composite-id> <property name="age" type="int"></property> </class> </hibernate-mapping>
测试类Test
public class Test{ private static SessionFactory sf; static { // 创建sf对象 sf = new Configuration() .configure() .addClass(User.class) //(测试) 会自动加载映射文件:Employee.hbm.xml .buildSessionFactory(); } //1. 保存对象 @Test public void testSave() throws Exception { Session session = sf.openSession(); Transaction tx = session.beginTransaction(); // 对象 CompositeKeys keys = new CompositeKeys(); keys.setAddress("广州东"); keys.setUserName("Jack"); User user = new User(); user.setAge(20); user.setKeys(keys); // 保存 session.save(user); tx.commit(); session.close(); } @Test public void testGet() throws Exception { Session session = sf.openSession(); Transaction tx = session.beginTransaction(); //构建主键再查询 CompositeKeys keys = new CompositeKeys(); keys.setAddress("广州东"); keys.setUserName("Jack"); // 主键查询 User user = (User) session.get(User.class, keys); // 测试输出 if (user != null){ System.out.println(user.getKeys().getUserName()); System.out.println(user.getKeys().getAddress()); System.out.println(user.getAge()); } tx.commit(); session.close(); } }
集合映射
user 类
/ javabean设计 public class User { private int userId; private String userName; // 一个用户,对应的多个地址 private Set<String> address; private List<String> addressList = new ArrayList<String>(); //private String[] addressArray; // 映射方式和list一样 <array name=""></array> private Map<String,String> addressMap = new HashMap<String, String>(); //省略get,set方法 }
User.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.sun.bean"> <class name="User" table="t_user"> <id name="userId" column="id"> <generator class="native"></generator> </id> <property name="userName"></property> <!-- set集合属性的映射 name 指定要映射的set集合的属性/ table 集合属性要映射到的表 key 指定集合表(t_address)的外键字段 element 指定集合表的其他字段 type 元素类型,一定要指定 --> <set name="address" table="t_address"> <key column="uid"></key> <element column="address" type="string"></element> </set> <!-- list集合映射 list-index 指定的是排序列的名称 (因为要保证list集合的有序) --> <list name="addressList" table="t_addressList"> <key column="uid"></key> <list-index column="idx"></list-index> <element column="address" type="string"></element> </list> <!-- map集合的映射 key 指定外键字段 map-key 指定map的key element 指定map的value --> <map name="addressMap" table="t_addressMap"> <key column="uid"></key> <map-key column="shortName" type="string" ></map-key> <element column="address" type="string" ></element> </map> </class> </hibernate-mapping>
测试
@Test public void testSaveSet() throws Exception { Session session = sf.openSession(); session.beginTransaction(); //-- 保存 Set<String> addressSet = new HashSet<String>(); addressSet.add("广州"); addressSet.add("深圳"); // 用户对象 User user = new User(); user.setUserName("Jack"); user.setAddress(addressSet); // 保存 session.save(user); session.getTransaction().commit(); session.close(); } // 保存list/map @Test public void testSaveList() throws Exception { Session session = sf.openSession(); session.beginTransaction(); User user = new User(); user.setUserName("Tom"); // // 用户对象 -- list // user.getAddressList().add("广州"); // user.getAddressList().add("深圳"); // // 保存 // session.save(user); // 用户对象 -- Map user.getAddressMap().put("A0001", "广州"); user.getAddressMap().put("A0002", "深圳"); // 保存 session.save(user); session.getTransaction().commit(); session.close(); }
关联映射
需求:部门与员工
public class Dept { private int deptId; private String deptName; // 【一对多】 部门对应的多个员工 private Set<Employee> emps = new HashSet<Employee>(); }
public class Employee { private int empId; private String empName; private double salary; // 【多对一】员工与部门 private Dept dept; }
映射文件
Dept.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="cn.itcast.b_one2Many"> <class name="Dept" table="t_dept"> <id name="deptId"> <generator class="native"></generator> </id> <property name="deptName" length="20"></property> <!-- 一对多关联映射配置 (通过部门管理到员工) Dept 映射关键点: 1. 指定 映射的集合属性: "emps" 2. 集合属性对应的集合表: "t_employee" 3. 集合表的外键字段 "t_employee. dept_id" 4. 集合元素的类型 一对多外键在多的一方 --> <set name="emps"> <!-- table="t_employee" --> <key column="dept_id"></key> <one-to-many class="Employee"/> </set> </class> </hibernate-mapping>
Employee.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="cn.itcast.b_one2Many"> <class name="Employee" table="t_employee"> <id name="empId"> <generator class="native"></generator> </id> <property name="empName" length="20"></property> <property name="salary" type="double"></property> <!-- 多对一映射配置 Employee 映射关键点: 1. 映射的部门属性 : dept 2. 映射的部门属性,对应的外键字段: dept_id 3. 部门的类型 --> <many-to-one name="dept" column="dept_id" class="Dept"></many-to-one> </class> </hibernate-mapping>
测试类
public class App { private static SessionFactory sf; static { sf = new Configuration() .configure() .addClass(Dept.class) .addClass(Employee.class) // 测试时候使用 .buildSessionFactory(); } // 保存, 部门方 【一的一方法操作】 @Test public void save() { Session session = sf.openSession(); session.beginTransaction(); // 部门对象 Dept dept = new Dept(); dept.setDeptName("应用开发部"); // 员工对象 Employee emp_zs = new Employee(); emp_zs.setEmpName("张三"); Employee emp_ls = new Employee(); emp_ls.setEmpName("李四"); // 关系 dept.getEmps().add(emp_zs); dept.getEmps().add(emp_ls); // 保存 session.save(emp_zs); session.save(emp_ls); session.save(dept); // 保存部门,部门下所有的员工 session.getTransaction().commit(); session.close(); /* * 结果 * Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?) Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?) Hibernate: insert into t_dept (deptName) values (?) Hibernate: update t_employee set deptId=? where empId=? 维护员工引用的部门的id Hibernate: update t_employee set deptId=? where empId=? */ } // 【推荐】 保存, 部员方 【多的一方法操作】 @Test public void save2() { Session session = sf.openSession(); session.beginTransaction(); // 部门对象 Dept dept = new Dept(); dept.setDeptName("综合部"); // 员工对象 Employee emp_zs = new Employee(); emp_zs.setEmpName("张三"); Employee emp_ls = new Employee(); emp_ls.setEmpName("李四"); // 关系 emp_zs.setDept(dept); emp_ls.setDept(dept); // 保存 session.save(dept); // 先保存一的方法 session.save(emp_zs); session.save(emp_ls);// 再保存多的一方,关系回自动维护(映射配置完) session.getTransaction().commit(); session.close(); /* * 结果 * Hibernate: insert into t_dept (deptName) values (?) Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?) Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?) 少生成2条update sql */ } }
总结:
在一对多与多对一的关联关系中,保存数据最好的通过多的一方来维护关系,这样可以减少update语句的生成,从而提高hibernate的执行效率!
多对多映射
需求:项目与开发人员
Project Developer
Developer 类
/** * */ public class Developer { private int d_id; private String d_name; // 开发人员,参数的多个项目 private Set<Project> projects = new HashSet<Project>(); }
Project 类
/** * * */ public class Project { private int prj_id; private String prj_name; // 项目下的多个员工 private Set<Developer> developers = new HashSet<Developer>();
映射文件
Project.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="cn.itcast.c_many2many"> <class name="Project" table="t_project"> <id name="prj_id"> <generator class="native"></generator> </id> <property name="prj_name" length="20"></property> <!-- 多对多映射: 1. 映射的集合属性: “developers” 2. 集合属性,对应的中间表: “t_relation” 3. 外键字段: prjId 4. 外键字段,对应的中间表字段: did 5. 集合属性元素的类型 --> <set name="developers" table="t_relation" cascade="save-update"> <key column="prjId"></key> <many-to-many column="did" class="Developer"></many-to-many> </set> </class> </hibernate-mapping>
Developer.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="cn.itcast.c_many2many"> <class name="Developer" table="t_developer"> <id name="d_id"> <generator class="native"></generator> </id> <property name="d_name" length="20"></property> <!-- 多对多映射配置: 员工方 name 指定映射的集合属性 table 集合属性对应的中间表 key 指定中间表的外键字段(引用当前表t_developer主键的外键字段) many-to-many column 指定外键字段对应的项目字段 class 集合元素的类型 --> <set name="projects" table="t_relation"> <key column="did"></key> <many-to-many column="prjId" class="Project"></many-to-many> </set> </class> </hibernate-mapping>
一对一映射,可分为基于主键映射和基于外键映射。外键映射的原理其实就是特殊的多对一。
我们先看基于主键映射的吧。
需求: 人和身份证号;
Person类
public class Person { private int id; private String name; }
IDCard 类
public class IDCard { private int id; private String cardNo; // private Person person; }
Person.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.sun.bean"> <!--类名,表名,数据库名 --> <class name="Person" table="person" catalog="mydb"> <id name="id" column="id"> <generator class="native" /> </id> <property name="name" column="name" /> </class> </hibernate-mapping>
IDCard.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.sun.bean"> <!--类名,表名,数据库名 --> <class name="IDCard" table="idcard" catalog="mydb"> <!-主鍵,主鍵要- --> <id name="id" column="id"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <property name="cardNo" column="cardNo" /> <!-- 对person一对一--> <one-to-one name="person" /> </class> </hibernate-mapping>
当我们写好po后,我们利用工具SchemaExport 来自动建表。
person表
idcard 表
通过观察这两张表,我们会发现,person表的id是自动递增的,而idcard表的id却没有。
于是我们得出了小结论,idcard表的id其实是跟着person表的id而变化的。
测试类
public class TestOneToOne { public static void main(String[] args) { // 生成Configuration对象 Configuration cfg = new Configuration(); // 读取配置文件 cfg.configure(); // 通过cf对象生成SessionFactory对象 SessionFactory sf = cfg.buildSessionFactory(); // 通过SessionFactory得到session Session session = sf.openSession(); Transaction tx = session.beginTransaction(); Person p1 = new Person(); p1.setName("小张"); Person p2 = new Person(); p2.setName("小张"); IDCard card1 = new IDCard(); card1.setCardNo("545454545454"); IDCard card2 = new IDCard(); card2.setCardNo("8888888888888"); card1.setPerson(p1); card2.setPerson(p2); session.save(p1); session.save(p2); session.save(card1); session.save(card2); tx.commit(); session.close(); sf.close(); } }
基于外键映射
原理就是特殊的多对一。单向一对一必须通过实体类生成数据库表的方式,会自动生成相关的约束,直接创建表无效。
Person 类
public class Person { private int id; private String name; }
IDCard 类
public class IDCard { private int id; private String cardNo; // private Person person; }
映射文件
IDCard.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.sun.bean"> <!--类名,表名,数据库名 --> <class name="IDCard" table="idcard" catalog="mydb"> <id name="id" column="id"> <generator class="native"> </generator> </id> <property name="cardNo" column="cardNo" /> <!--对person一对一,需加上属性 unique--> <many-to-one name="person" column="person_id" unique="true"/> </class> </hibernate-mapping>
Person.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.sun.bean"> <!--类名,表名,数据库名 --> <class name="Person" table="person" catalog="mydb"> <id name="id" column="id"> <generator class="native" /> </id> <property name="name" column="name" /> </class> </hibernate-mapping>
组件映射
组合关系:一个类中包含了另外一个类。这2个类中就是组合关系。
需求: 汽车与车轮
Car类
//车public class Car { private int id; private String name; // 车轮 private Wheel wheel; }
车轮类
// 车轮 public class Wheel { private int count; private int size; }
映射文件
<hibernate-mapping package="cn.itcast.d_component"> <class name="Car" table="t_car"> <id name="id"> <generator class="native"></generator> </id> <property name="name" length="20"></property> <!-- 组件映射 --> <component name="wheel"> <property name="size"></property> <property name="count"></property> </component> </class> </hibernate-mapping>
继承映射
需求:猪、鸟、动物。
继承映射有三种实现方式。
(1)所有子类映射到一张表 (1张表)
(2)每个类映射一张表(3张表)
(3)每个子类映射一张表, 父类不对应表(2张表)
(1)所有子类映射到一张表 (1张表)
Animal类
public class Animal { private int id; private String name; private String gender; //省略get,set方法 }
Pig 类
public class Pig extends Animal{ private int weight; }
Bird 类
public class Bird extends Animal{ private int height; }
映射文件
<hibernate-mapping package="com.sun.bean"> <class name="Animal" table="t_animal" lazy="false"> <id name="id"> <generator class="native"/> </id> <discriminator column="type" type="string"/> <property name="name"/> <property name="sex"/> <subclass name="Pig" discriminator-value="P"> <property name="weight"/> </subclass> <subclass name="Bird" discriminator-value="B"> <property name="height"/> </subclass> </class> </hibernate-mapping>
总结:
写法较为简单:所有子类用一个映射文件,且映射到一张表!
那么怎么区分哪个子类的信息在那张表呢?
答案是在表中添加一个字段,用这个字段的值来进行区分。
在父类中定义一个discriminator,即指定这个区分的字段的名称和类型
如: <discriminator column="type" type="string"/>
(2)每个类映射一张表(3张表)
映射文件:
<hibernate-mapping package="com.sun.bean"> <class name="Animal" table="t_animal"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="sex"/> <joined-subclass name="Pig" table="t_pig"> <key column="pid"/> <property name="weight"/> </joined-subclass> <joined-subclass name="Bird" table="t_bird"> <key column="bid"/> <property name="height"/> </joined-subclass> </class> </hibernate-mapping>
总结:在父类对应的数据库表中,实际上会存储所有的记录,包括父类和子类的记录;在子类对应的数据库表中,这个表只定义了子类中所特有的属性映射的字段。子类与父类,通过相同的主键值来关联。
(3)每个子类映射一张表, 父类不对应表(2张表)
<hibernate-mapping package="com.sun.bean"> <class name="Animal" table="t_animal" abstract="true"> <id name="id"> <generator class="assigned"/> </id> <property name="name"/> <property name="sex"/> <union-subclass name="Pig" table="t_pig"> <property name="weight"/> </union-subclass> <union-subclass name="Bird" table="t_bird"> <property name="height"/> </union-subclass> </class> </hibernate-mapping> <!--注意:主键不能是自增长!在保存对象的时候id不能重复,可使用uuid-->
总结:
所有的子类都写到一个映射文件;
父类不对应表; 每个子类对应一张表,而且这个表的信息是完备的。
未完待续,还有下文~如有错误,请指正~