在开始学Hibernate之前,一直就有人说:Hibernate并不难,无非是对JDBC进一步封装。一句不难,难道是真的不难还是眼高手低?
如果只是停留在使用的层面上,我相信什么技术都不难,看看别人怎么做的,你也可以很快上手。
这难道是学习的最终目的吗?
不是,绝对不是。我们需要了解这门技术的基本概念,它到底是什么,它是干什么的,它有什么优缺点。下面我就带领大家一起回顾一下Hibernate:
什么是Hibernate?
Hibernate,翻译过来是冬眠的意思,正好现在已经进入秋季,世间万物开始准备冬眠了。其实对于对象来说就是持久化。
扫盲--------------------------------------------------------------------------------------------------------
持久化(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等。
持久化是将程序数据在持久状态和瞬时状态间转换的机制。
JDBC就是一种持久化机制。文件IO也是一种持久化机制。
日常持久化的方法:
1、将鲜肉冷藏,吃的时候再解冻的方法也是。
2、将水果做成罐头的方法也是。
结束----------------------------------------------------------------------------------------------------------
我们从三个角度理解一下Hibernate:
一、Hibernate是对JDBC进一步封装
原来没有使用Hiberante做持久层开发时,存在很多冗余,如:各种JDBC语句,connection的管理,所以出现了Hibernate把JDBC封装了一下,我们不用操作数据,直接操作它就行了。
二、我们再从分层的角度来看
我们知道非常典型的三层架构:表示层,业务层,还有持久层。Hiberante也是持久层的框架,而且持久层的框架还有很多,比如:IBatis,Nhibernate,JDO,OJB,EJB等等。
三、Hibernate是开源的一个ORM(对象关系映射)框架。
ORM,即Object-Relational Mapping,它的作用就是在关系型数据库和对象之间做了一个映射。从对象(Object)映射到关系(Relation),再从关系映射到对象。这样,我们在操作数据库的时候,不需要再去和复杂SQL打交道,只要像操作对象一样操作它就可以了(把关系数据库的字段在内存中映射成对象的属性)。
Hibernate的核心:
从上图中,我们可以看出Hibernate六大核心接口,两个主要配置文件,以及他们直接的关系。Hibernate的所有内容都在这了。那我们从上到下简单的认识一下,每个接口进行一句话总结。
1、Configuration接口:负责配置并启动Hibernate
2、SessionFactory接口:负责初始化Hibernate
3、Session接口:负责持久化对象的CRUD操作
4、Transaction接口:负责事务
5、Query接口和Criteria接口:负责执行各种数据库查询
注意:Configuration实例是一个启动期间的对象,一旦SessionFactory创建完成它就被丢弃了。
Hibernate的优/缺点:
优点:
1、更加对象化
以对象化的思维操作数据库,我们只需要操作对象就可以了,开发更加对象化。
2、移植性
因为Hibernate做了持久层的封装,你就不知道数据库,你写的所有的代码都具有可复用性。
3、Hibernate是一个没有侵入性的框架,没有侵入性的框架我们称为轻量级框架。
对比Struts的Action和ActionForm,都需要继承,离不开Struts。Hibernate不需要继承任何类,不需要实现任何接口。这样的对象叫POJO对象。
4、Hibernate代码测试方便。
5、提高效率,提高生产力。
缺点:
1、使用数据库特性的语句,将很难调优
2、对大批量数据更新存在问题
3、系统中存在大量的攻击查询功能
总结:
Hibernate可以使我们采用对象化的思维操作关系型数据库。这篇博文最主要的目的是从整体上认识Hibernate,内容可能会有点空,不是特别详细,但是后面会有更加针对性的博文进行讲解。敬请期待。
Hibernate是非常典型的持久层框架,持久化的思想是非常值得我们学习和研究的。这篇博文,我们主要以实例的形式学习Hibernate,不深究Hibernate的思想和原理,否则,一味追求,苦学思想和原理,到最后可能什么也学不会,从实践入手,熟能生巧,思想和原理自然而然领悟。
上篇博文:【SSH进阶之路】Hibernate基本原理,我们介绍了Hibernate的基本概念、Hibernate的核心以及Hibernate的执行原理,可以很好帮助我们认识Hibernate,再看这篇博客之前,请先回顾上篇博文内容。这篇博文,我们从实践角度,动手搭建一个简单的Hibernate实例。
一、开发环境
Win8 + jdk1.7 + MyEclipse + Tomcat5.0 + MySQL
说明:其实Hibernate是非常独立的框架,根本不需要MyEclipse,Eclipse,Tomcat,Log4J等,他们只不过是能满足我们其他的需求,才把他们引进来的。
二、下载文件
你需要Java SDK、 Hibernate包、和JDBC Driver。
1、Hibernate包下载地址:
http://prdownloads.sourceforge.net/hibernate/?sort_by=date&sort=desc
2、JDBC Driver根据你的数据库来定,一般database官网都有。Hibernate支持常用的数据库,比如 MySQL, Oracle等等。这两个数据库是现在比较常用的,都有JDBC Driver:
Oracle JDBC Driver下载地址(下载前必须同意Oracle协议书)
http://otn.oracle.com/software/htdocs/distlic.html?/software/tech/java/sqlj_jdbc/htdocs/jdbc9201.html
MySQL JDBC Driver下载地址
http://dev.mysql.com/downloads/connector/j/3.0.html
三、所需jar包
hibernate3.jar Hibernate的核心包
dom4j-1.6.1.jar dom4j读取xml文件包
mysql-connector-java-3.1.13-bin.jar MySQL的jdbc驱动包
Hibernate的作用:让我们以面向对象的方式或思维来考虑怎么向关系型数据库存取数据。它需要与相应的数据库打交道,所以需要相应的jdbc驱动。我们的database用的是MySQL,所以需要引入MySQL的jdbc驱动。
log4j-1.2.11.jar 记录日志框架
由于log4j的记录日志比jdk自带的记录日志功能更加美观,简单,易配置日志级别,便于调试,我们选择使用log4j。
必须要引入的jar:
commons-logging-1.0.4.jar 抽象的日志记录框架
本身并没有实现真正的写日志能力(看包结构即可知道)而是结合其它的日志系统如Log4j或者java本身的java.util.logging作为日志输出组件,来实现日志记录的功能。
commons-collections-2.1.1jar 各种集合类和集合工具类的封装
cglib-2.1.3.jar 动态代理,Hibernate用它来实现PO字节码的动态生成
asm.jar cglib需要依赖的jar,ASM字节码库
注:作为初学者不提倡这种做法,只需要将hibernate所要依赖的第三方jar包都引入即可,否则做其他实例时会报NoClassDefFoundError的错误,解决方案:只需将对应jar引入即可。由于这是一个简单实例,仅仅需要引入这些jar。
四、代码展示
1、在IDE中创建java项目(比较简单不再演示)
2、创建source folder,命名为Hibernate3,在Hibernate下载文件中找到我们所需要的三个配置文件和所有jar包,拷贝所需jar文件,构建依赖包
3、提供hibernate.cfg.xml文件,完成基本配置
4、写代码
(1)建立实体类User.java
- package com.liang.hibernate;
- import java.util.Date;
- public class User {
- private String id;
- private String name;
- private String password;
- private Date createTime;
- private Date expireTime;
- public String getId() {
- return id;
- }
- public void setId(String id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- public Date getCreateTime() {
- return createTime;
- }
- public void setCreateTime(Date createTime) {
- this.createTime = createTime;
- }
- public Date getExpireTime() {
- return expireTime;
- }
- public void setExpireTime(Date expireTime) {
- this.expireTime = expireTime;
- }
- }
(2)提供User.hbm.xml文件,完成实体类映射
- <span style="font-size:12px;"><?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>
- <!--生成默认为user的数据库表-->
- <class name="com.liang.hibernate.User">
- <id name="id">
- <!-- 算法的核心思想是结合机器的网卡、当地时间、一个随机数来生成GUID -->
- <generator class="uuid"></generator>
- </id>
- <property name="name"></property>
- <property name="password"></property>
- <property name="createTime" type="date"></property>
- <property name="expireTime" type="date"></property>
- </class>
- </hibernate-mapping></span>
- <!DOCTYPE hibernate-configuration PUBLIC
- "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
- <hibernate-configuration>
- <session-factory>
- <!-- 设置数据库驱动 -->
- <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
- <!-- 设置数据库URL -->
- <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_first</property>
- <!-- 数据库用户名 -->
- <property name="hibernate.connection.username">root</property>
- <!-- 数据库密码 -->
- <property name="hibernate.connection.password">123456</property>
- <!-- 指定对应数据库的方言,hibernate为了更好适配各种关系数据库,针对每种数据库都指定了一个方言dialect -->
- <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
- <!-- 映射文件 -->
- <mapping resource="com/liang/hibernate/User.hbm.xml"/>
- </session-factory>
- </hibernate-configuration>
(4)编写工具类ExportDB.java,将hbm生成ddl,也就是hbm2ddl
- package com.liang.hibernate;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.tool.hbm2ddl.SchemaExport;
- /**
- * 将hbm生成ddl
- * @author liang
- *
- */
- public class ExportDB{
- public static void main(String[]args){
- //默认读取hibernate.cfg.xml文件
- Configuration cfg = new Configuration().configure();
- ////生成并输出sql到文件(当前目录)和数据库
- SchemaExport export = new SchemaExport(cfg);
- export.create(true, true);
- }
- }
测试之前,要提前建立数据库hibernate_first,测试如下:
控制台打印的SQL语句:
- drop table if exists User
- create table User (id varchar(255) not null, name varchar(255), password varchar(255), createTime date, expireTime date, primary key (id))
数据库表结构:
(5)建立客户端类Client,添加用户数据到mySQL
- package com.liang.hibernate;
- import java.util.Date;
- import org.hibernate.Session;
- import org.hibernate.SessionFactory;
- import org.hibernate.cfg.Configuration;
- public class Client {
- public static void main(String[]args){
- //读取hibernate.cfg.xml文件
- Configuration cfg = new Configuration().configure();
- //建立SessionFactory
- SessionFactory factory =cfg.buildSessionFactory();
- //取得session
- Session session = null;
- try{
- //开启session
- session = factory.openSession();
- //开启事务
- session.beginTransaction();
- User user = new User();
- user.setName("jiuqiyuliang");
- user.setPassword("123456");
- user.setCreateTime(new Date());
- user.setExpireTime(new Date());
- //保存User对象
- session.save(user);
- //提交事务
- session.getTransaction().commit();
- }catch(Exception e){
- e.printStackTrace();
- //回滚事务
- session.getTransaction().rollback();
- }finally{
- if(session != null){
- if(session.isOpen()){
- //关闭session
- session.close();
- }
- }
- }
- }
- }
右键debug运行,测试完成之后,我们查询一下测试结果:
5、为了在调试过程中能观察到Hibernate的日志输出,最好加入log4j.properties配置文件、在CLASSPATH中新建log4j.properties配置文件或将该配置文件拷贝到src下,便于程序调试。
内容如下:
- <span style="font-size:12px;">### direct log messages to stdout ###
- log4j.appender.stdout=org.apache.log4j.ConsoleAppender
- log4j.appender.stdout.Target=System.out
- log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
- log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
- ### direct messages to file hibernate.log ###
- #log4j.appender.file=org.apache.log4j.FileAppender
- #log4j.appender.file.File=hibernate.log
- #log4j.appender.file.layout=org.apache.log4j.PatternLayout
- #log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
- ### set log levels - for more verbose logging change 'info' to 'debug' ###
- log4j.rootLogger=warn, stdout</span>
配置完成后,项目结构如下图所示:
五、最后
自己动手丰衣足食,实践出真理,纸上得来终觉浅,绝知此事要躬行。虽然这个实例非常简单,但是我们踏进了持久层框架的大门。
从上面的简单实例可以看到,我们只是使用Hibernate对User这一个实体进行了映射,比较简单,但是完全不符合实际。如何像关系型数据库一样表示多种关联关系,例如:一对一,一对多,多对多等等,我们还需要深入。下篇博文,我们介绍Hibernate的基本映射原理以及关联关系映射。【SSH进阶之路】Hibernate基本原理(一) ,小编介绍了Hibernate的基本原理以及它的核心,采用对象化的思维操作关系型数据库。
【SSH进阶之路】Hibernate搭建开发环境+简单实例(二),小编搭建了基本Hibernate的开发环境,并做了一个简单实例,对它的基本原理有了一个理性的认识。
这篇博客小编介绍Hibernate的经典内容:对象关系映射。主要介绍映射的基本概念,映射的分类,映射文件。
概念
ORM(Object Relational Mapping),即对象关系映射。它的作用就是在关系型数据库和对象之间做了一个映射。从对象(Object)映射到关系(Relation),再从关系映射到对象。相信很多人跟小编一个毛病,看到概念就头疼,下面小编画了一张图加深理解。
这张图特别简单:原来,没有Hibernate时,我们需要通过JDBC+手动写SQL语句来操作数据库,现在,有了Hibernate,它将JDBC+SQL进行了高度封装,我们不需要再去和复杂SQL打交道,只要像操作对象一样操作数据库就可以了。
ORM的实现思想就是将数据库中表的数据映射成对象,Hibernate可以使我们采用对象化的思维操作关系型数据库。
映射文件
Hibernate在实现ORM功能的时候主要用到的文件有:
1、 映射类(*.java):它是描述数据库表的结构,表中的字段在类中被描述成属性,将来就可以实现把表中的记录映射成为该类的对象了。
2、映射文件(*.hbm.xml):它是指定数据库表和映射类之间的关系,包括映射类和数据库表的对应关系、表字段和类属性类型的对应关系以及表字段和类属性名称的对应关系等。
3、 hibernate核心配置文件(*.properties/*.cfg.xml):它指定hibernate的一些核心配置,包含与数据库连接时需要的连接信息,比如连接哪种数据库、登录数据库的用户名、登录密码以及连接字符串等。映射文件的地址信息也放在这里。
分类
上面的内容看上去挺多,其实特别少,基本映射很简单,我们主要学习关联关系映射,其他几种映射一般不会用,只需要了解即可,用的时候看一下相关资料会做就好。
基本映射
上篇博文我们已经实现了一个基本映射,是使用XML方式配置映射,如下所示:
- <span style="font-size:12px;"><?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>
- <class name="com.liang.hibernate.User" >
- <id name="id">
- <!-- 算法的核心思想是结合机器的网卡、当地时间、一个随机数来生成GUID -->
- <generator class="uuid"></generator>
- </id>
- <property name="name"></property>
- <property name="password"></property>
- <property name="createTime" type="date"></property>
- <property name="expireTime" type="date"></property>
- </class>
- </hibernate-mapping></span>
除了XML方式配置映射外,还可以通过给类文件添加注解的方式配置映射,在上篇博文的基础之上,我们稍加修改。
1、加入hibernate annotion支持包
*hibernate-annotations.jar
*hibernate-commons-annotations.jar
*ejb3-persistence.jar
如图所示:
2、建立实体类User,采用注解完成映射
- package com.liang.hibernate;
- import java.util.Date;
- import javax.persistence.Column;
- import javax.persistence.Entity;
- import javax.persistence.GeneratedValue;
- import javax.persistence.GenerationType;
- import javax.persistence.Id;
- import javax.persistence.Temporal;
- import javax.persistence.TemporalType;
- @Entity //不写Table默认为user,@Table(name="t_user")
- public class User {
- @Id //主键
- @GeneratedValue(strategy=GenerationType.AUTO)//采用数据库自增方式生成主键
- //JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO.
- //TABLE:使用一个特定的数据库表格来保存主键。
- //SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
- //IDENTITY:主键由数据库自动生成(主要是自动增长型)
- //AUTO:主键由程序控制。
- private int id;
- private String name;
- private String password;
- @Temporal(TemporalType.DATE)//生成yyyy-MM-dd类型的日期
- private Date createTime;
- @Temporal(TemporalType.DATE)//生成yyyy-MM-dd类型的日期
- private Date expireTime;
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- @Column(name="name",unique=true,nullable=false) //字段为name,不允许为空,用户名唯一
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- public Date getCreateTime() {
- return createTime;
- }
- public void setCreateTime(Date createTime) {
- this.createTime = createTime;
- }
- public Date getExpireTime() {
- return expireTime;
- }
- public void setExpireTime(Date expireTime) {
- this.expireTime = expireTime;
- }
- }
注:由于主键改成了自增长,所以数据类型修改成了int类型
3、提供hibernate.cfg.xml文件,将实体类User加入到hibernate.cfg.xml配置文件中,完成基本配置
- <span style="font-size:12px;"><!DOCTYPE hibernate-configuration PUBLIC
- "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
- <hibernate-configuration>
- <session-factory>
- <!-- 驱动 -->
- <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
- <!-- 数据库URL -->
- <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_first</property>
- <!-- 数据库用户名 -->
- <property name="hibernate.connection.username">root</property>
- <!-- 数据库密码 -->
- <property name="hibernate.connection.password">123456</property>
- <!-- mysql的方言 -->
- <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
- <!-- 映射文件 -->
- <!-- <mapping resource="com/liang/hibernate/User.hbm.xml"/> -->
- <!-- 由原来的映射文件,改成实体类 -->
- <mapping class="com.liang.hibernate.User"/>
- </session-factory>
- </hibernate-configuration></span>
4、编写工具类ExportDB.java,注解生成ddl,必须采用AnnotationConfiguration类
- <span style="font-size:12px;">package com.liang.hibernate;
- import org.hibernate.cfg.AnnotationConfiguration;
- import org.hibernate.cfg.Configuration;
- import org.hibernate.tool.hbm2ddl.SchemaExport;
- /**
- * 将hbm生成ddl
- * @author liang
- *
- */
- public class ExportDB{
- public static void main(String[]args){
- //默认读取hibernate.cfg.xml文件
- Configuration cfg = new AnnotationConfiguration().configure();
- SchemaExport export = new SchemaExport(cfg);
- export.create(true, true);
- }
- }</span>
数据库生成表如图所示:
5、建立客户端类Client,添加用户数据到mysql
- package com.liang.hibernate;
- import java.util.Date;
- import org.hibernate.Session;
- import org.hibernate.SessionFactory;
- import org.hibernate.cfg.AnnotationConfiguration;
- import org.hibernate.cfg.Configuration;
- public class Client {
- public static void main(String[]args){
- //读取hibernate.cfg.xml文件
- Configuration cfg = new AnnotationConfiguration().configure();
- //建立SessionFactory
- SessionFactory factory =cfg.buildSessionFactory();
- //取得session
- Session session = null;
- try{
- //开启session
- session = factory.openSession();
- //开启事务
- session.beginTransaction();
- User user = new User();
- user.setName("jiuqiyuliang");
- user.setPassword("123456");
- user.setCreateTime(new Date());
- user.setExpireTime(new Date());
- //保存User对象
- session.save(user);
- //提交事务
- session.getTransaction().commit();
- }catch(Exception e){
- e.printStackTrace();
- //回滚事务
- session.getTransaction().rollback();
- }finally{
- if(session != null){
- if(session.isOpen()){
- //关闭session
- session.close();
- }
- }
- }
- }
- }
运行之后,数据库表生成的数据,如下图所示:
注解和xml文件的优缺点,网上有很多,有兴趣可以查一下,小编就不再累述了,但是小编认为,学习初期最好不要使用注解,不易于理解Hibernate的原理,并且注解对于程序的可扩展性而言,太差了。
【SSH进阶之路】Hibernate基本原理(一) ,小编介绍了Hibernate的基本原理以及它的核心,采用对象化的思维操作关系型数据库。
【SSH进阶之路】Hibernate搭建开发环境+简单实例(二),小编搭建了基本Hibernate的开发环境,并做了一个简单实例,对它的基本原理有了一个理性的认识。
【SSH进阶之路】Hibernate基本映射(三),我们介绍了Hibernate的基本映射(即对一个实体进行映射)的相关概念,并给大家实现相关实例,比较简单。
这篇博客,我们开始介绍基于基本映射的关联关系映射。
概念
基本映射是对一个实体进行映射,关联映射就是处理多个实体之间的关系,将关联关系映射到数据库中,所谓的关联关系在对象模型中有一个或多个引用。
分类
关联关系分为上述七种,但是由于相互之间有各种关系,可以简化,例如:多对一与一对多映射,只是侧重的角度不对而已。
映射技巧
映射技巧是小编写映射文件的过程,总结的经典内容,总共分为四步,咋看不是特别易懂,但是效果很好。下面我们以实例看技巧。
(1)写注释
格式为:?属性,表达的是本对象与?的?关系。
解释:在写映射文件之前先写注释,将问号的地方填上相应的内容。例如:<!-- group属性,表达的是本对象(User)与Group的多对一的关系-->
(2)写映射的框架(拷模版)
多对一 |
<many-to-one name=“” class=“” column=“”/> |
一对多 (Set) |
<set name=""> <key column=""></key> <one-to-many class=""/> </set> |
多对多 (Set) |
<set name="" table=""> <key column=""></key> <many-to-many class="" column=""/> </set> |
(3)填空
填空,就是将映射的框架信息,填写完成,完成映射文件。
• name属性:属性名(注释中的第1问号)
• class属性:关联的实体类型(注释中的第2个问号)
• column属性:
○ <many-to-one column="..">:一般可以写成属性名加Id后缀,如属性为group,则column值写成groupId。
○ 一对多中的<key column="..">:从关联的对方(对方是多对一)映射中把column值拷贝过来。
○ 多对多中的<key column=“..”>:一般可以写成本对象的名加Id后缀,如本对象名为User,则写为userId。
○ 多对多中的<many-to-many column=“..”>:一般可以写为关联对象的名称加Id后缀。
(4)完成
将映射文件添加到hibernate.hbm.xml中,这个相信大家都知道为什么。
我们后面所有关联映射的博文都使用此映射技巧来写映射文件,明白之后,速度会非常快。
多对一关联映射
映射原理
多的一端维护关联关系,在“多”的一端加入一个外键,指向“一”的一端。多的一端持有一的一端的引用,即在“多”的一端加外键,指向“一”的一端。
实例
比如,多个用户属于同一组,我们从对象模型和关系模型两个角度来分析一下这个例子,如下:
从上图可以看出,对象模型具有方向性,通过用户(User)可以看到组(Group),但是不能反过来。用户和组各对应一张数据库表,聚合关系需要一个外键(groupid)来表示,最后生成的表如下所示:
作用:
当我拿到用户时直接就可以拿到用户的组,hibernate在访问多的一端时,可以自动的加载关联对象。对于用户(User)来说,它的关联对象是组(group)。
上面都是多对一关联映射的基本原理,以及相应的实例,下面我们看一下代码:
代码
User类
- public class User {
- private int id;
- private String name;
- private Group group;
- 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;
- }
- public Group getGroup() {
- return group;
- }
- public void setGroup(Group group) {
- this.group = group;
- }
- }
Group类
- public class Group {
- 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;
- }
User.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.liang.hibernate">
- <class name="User" table="t_user">
- <id name="id">
- <generator class="native"></generator>
- </id>
- <property name="name"></property>
- <!-- group属性,表达的是本对象与Group的多对一的关系-->
- <many-to-one name="group" class="Group" column="groupid"></many-to-one>
- <!-- 解释:
- 多对一关系标签:
- <many-to-one name=“” class=“” column=“”/>
- 1、第1个问号:group是User类的属性,对应于name属性名
- 2、第2个问号:class表达的是本对象(User)与Group的关系
- 3、第3个问号:column是属性名+Id
- 当我们写完注释之后,我们直接拷贝问号的对应关系即可。
- -->
- </class>
- </hibernate-mapping>
Group.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>
- <class name="com.liang.hibernate.Group" table="t_group">
- <id name="id">
- <generator class="native"></generator>
- </id>
- <property name="name"></property>
- </class>
- </hibernate-mapping>
生成的表结构,与上面的分析的关系模型一样:
【SSH进阶之路】Hibernate基本原理(一) ,小编介绍了Hibernate的基本原理以及它的核心,采用对象化的思维操作关系型数据库。
【SSH进阶之路】Hibernate搭建开发环境+简单实例(二),小编搭建了基本Hibernate的开发环境,并做了一个简单实例,对它的基本原理有了一个理性的认识。
【SSH进阶之路】Hibernate基本映射(三),我们介绍了Hibernate的基本映射(即对一个实体进行映射)的相关概念,并给大家实现相关实例,比较简单。
【SSH进阶之路】Hibernate映射——多对一单向关联映射(四),我们介绍了多对一的关联映射,并详细了解说了,映射技巧,这篇我们依旧使用此映射技巧,帮助大家更好的理解。
下面开始今天的愉快之旅。
映射原理
两个实体对象之间是一对一的关联映射,即一个对象只能与另外唯一的一个对象相对应。例如:一个人(Person)只有一张身份证(IdCard)。我们看一下这个例子的对象模型,如下图所示:
对象模型
从上图中可以看出:
1、一个人只有一张身份证,唯一的一个身份证号,对象之间是一对一的关系;
2、人(Person)持有身份证(IdCard)的引用,所以,两个对象关系维护由person端决定。
从对象模型映射成关系模型,有两种方式:主键关联和唯一外键关联,我们继续看下面的内容。
分类:
主键关联:
1、两个实体对象的主键一样,以表明它们之间的一一对应关系;
2、不需要多余的外键字段来维护关系,仅通过主键来关联,即Person的主键要依赖IdCard的主键,他们共用一个主键值。
以上两点恰与唯一外键关联相反。
主键关联的关系模型
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.liang.hibernate">
- <class name="Person" table="t_person">
- <id name="id">
- <!-- 采用foreign生成策略,forgeign会取得另外一个关联对象的标识 -->
- <generator class="foreign" >
- <param name="property">idCard</param>
- </generator>
- </id>
- <property name="name"></property>
- <!--
- one-to-one指示hibernate如何加载其关联对象,默认根据主键加载
- 也就是拿到关系字段值,根据对端的主键来加载关联对象
- constrained="true"表示约束,当前主键(person的主键)还是一个外键
- 参照了对端的主键(IdCard的主键),也就是会生成外键约束语句
- -->
- <!-- idCard属性,表达的是本对象与IdCard的一对一关系。 -->
- <one-to-one name="idCard" class="IdCard" constrained="true"></one-to-one>
- </class>
- </hibernate-mapping>
生成的表结构以及测试数据:
唯一外键关联:
1、两个实体对象用一个外键来关联,以表表明对象之间的关系。
2、其实它是多对一关联映射的特例,多的一端加上唯一的限制之后,用来表示一对一的关联关系。
所以它采用多对一的标签来映射,如下所示:
- <!-- 采用<mang-to-one>标签来映射,指定多的一端unique为true,
- 这样就限制了多的一端的多重性为一,就是这样来映射的。
- -->
- <many-to-one name="" unique="true"></many-to-one>
唯一外键的关系模型
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.liang.hibernate">
- <class name="Person" table="t_person">
- <id name="id">
- <generator class="native" ></generator>
- </id>
- <property name="name"></property>
- <!-- 由于它是一对一的唯一外键关联,它是多对一关联的特例,注释可以直接写成多对一关联-->
- <!-- idCard属性,表达的是本对象与IdCard的多对一关系。 -->
- <many-to-one name="idCard" class="IdCard" column="idCardId" unique="true"></many-to-one>
- </class>
- </hibernate-mapping>
生成的表结构以及测试数据:
其他相同代码如下:
Person
- public class Person {
- private int id;
- private String name;
- private IdCard idCard;
- public IdCard getIdCard() {
- return idCard;
- }
- public void setIdCard(IdCard idCard) {
- this.idCard = idCard;
- }
- 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;
- }
- }
IdCard
- public class IdCard {
- private int id;
- private String cardNo;
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getCardNo() {
- return cardNo;
- }
- public void setCardNo(String cardNo) {
- this.cardNo = cardNo;
- }
- }
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.liang.hibernate">
- <class name="IdCard" table="t_idCard">
- <id name="id">
- <generator class="native"></generator>
- </id>
- <property name="cardNo"></property>
- </class>
- </hibernate-mapping>
建议
由于一对一主键关联映射具有以下两个缺点:
1、灵活性差,没有办法改成多对一关联映射,不能应变多变的需求;
2、必须先保存关联对象IdCard,之后才能保持Person;
所以,在映射一对一单向关联映射时,我们采用唯一外键关联映射。
下篇博文,我们介绍一对一双向关联映射,谢谢关注。
上篇博文【SSH进阶之路】Hibernate映射——一对一单向关联映射(五),我们介绍了一对一的单向关联映射,单向是指只能从人(Person)这端加载身份证端(IdCard),但是反过来,不能从身份证端加载人得信息。如图所示:
关键原因在于对象模型具有方向性:
单向:一端只能加载另一端,不能反过来。
双向:两端都可以加载另一端。
问题来了:如何我们想从身份证端(IdCard)加载人(Person),怎么办呢?
下面我们开始介绍一对一的双向关联映射。
映射原理
双向关联映射与单向关联映射的原理是一样的,双向关联映射并不影响存储,只影响加载。所以,双向关联映射和单向关联映射的关系模型是一样的即数据库的表结构是一样的,只是IdCard的实体类和配置文件(IdCard.hbm.xml)发生了一点点变化。
对象模型
从上图中可以看出:
1、一个人只有一张身份证,唯一的一个身份证号,对象之间是一对一的关系;
2、两个对象得关系维护还是由person端决定(因为关系只能由一端维护主键,否则关系就乱了)。
根据上面的对象模型,我们可以看到Person端没有变化,但是要在IdCard端加上Person的引用,例如Person和IdCard实体类如下。
Person
- package com.liang.hibernate;
- public class Person {
- private int id;
- private String name;
- private IdCard idCard;
- public IdCard getIdCard() {
- return idCard;
- }
- public void setIdCard(IdCard idCard) {
- this.idCard = idCard;
- }
- 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;
- }
- }
IdCard
- package com.liang.hibernate;
- public class IdCard {
- private int id;
- private String cardNo;
- private Person person;
- public Person getPerson() {
- return person;
- }
- public void setPerson(Person person) {
- this.person = person;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getCardNo() {
- return cardNo;
- }
- public void setCardNo(String cardNo) {
- this.cardNo = cardNo;
- }
- }
无论是单向关联映射还是双向关联映射,他们都属于一对一关联映射,只是他们主键的生成策略不同,分为主键关联映射和唯一外键关联映射。
由于它们都属于一对一关联映射,所以,Hibernate封装双向关联映射时,主键关键映射和唯一外键关联映射的加载策略一样,都采用的是一对一<one-to-one name=""></one-to-one>,只是属性设置不一致,所以,下面我们分开来看IdCard的配置文件。
分类:
主键关联映射
同一对一单向关联映射类似,主键关联即利用主键进行关联,关联主键的值相同。下面我们看一下映射文件:
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.liang.hibernate">
- <class name="IdCard" table="t_idCard">
- <id name="id">
- <generator class="native"></generator>
- </id>
- <property name="cardNo"></property>
- <!-- 怎么加载对象,抓取策略:join联合查询(默认),select:一条条的查询 -->
- <one-to-one name="person" class="Person" fetch="join"></one-to-one>
- </class>
- </hibernate-mapping>
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.liang.hibernate">
- <class name="Person" table="t_person">
- <id name="id">
- <!-- 采用foreign生成策略,forgeign会取得关联对象的标识 -->
- <generator class="foreign" >
- <param name="property">idCard</param>
- </generator>
- </id>
- <property name="name"></property>
- <!--
- one-to-one指示hibernate如何加载其关联对象,默认根据主键加载
- 也就是拿到关系字段值,根据对端的主键来加载关联对象
- constrained="true"表示,当前主键(person的主键)还是一个外键
- 参照了对端的主键(IdCard的主键),也就是会生成外键约束语句
- -->
- <one-to-one name="idCard" class="IdCard" constrained="true"></one-to-one>
- </class>
- </hibernate-mapping>
生成的表结构
唯一外键关联映射
一对一双向关联映射的外键关联映射也与一对一单向关联映射的外键关联映射类似,在其一对一的指向端(Person)存在一个唯一外键,该唯一外键与被指向端(IdCard)相关联,关联主键的值相同。下面我们看一下映射文件:
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.liang.hibernate">
- <class name="IdCard" table="t_idCard">
- <id name="id">
- <generator class="native"></generator>
- </id>
- <property name="cardNo"></property>
- <!-- 一对一唯一外键关联双向采用<one-to-one>标签来映射,必须指定<one-to-one>
- 标签中的property-ref属性为关系字段的名称
- -->
- <one-to-one name="person" class="Person" property-ref="idCard"></one-to-one>
- </class>
- </hibernate-mapping>
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.liang.hibernate">
- <class name="Person" table="t_person">
- <id name="id">
- <generator class="native" ></generator>
- </id>
- <property name="name"></property>
- <!-- 由于它是一对一的唯一外键关联,它是多对一关联的特例,注释可以直接写成多对一关联-->
- <!-- idCard属性,表达的是本对象与IdCard的多对一关系。 -->
- <many-to-one name="idCard" class="IdCard" column="idCardId" unique="true"></many-to-one>
- </class>
- </hibernate-mapping>
生成的表结构
上上篇博文【SSH进阶之路】Hibernate映射——一对一单向关联映射(五),我们介绍了一对一的单向关联映射,单向是指只能从人(Person)这端加载身份证端(IdCard),但是反过来,不能从身份证端加载人得信息。
上篇博文【SSH进阶之路】Hibernate映射——一对一双向关联映射(六),双向关联映射解决了单向关联映射只能从一端加载信息的缺陷,当然,双向关联映射并不影响存储,只影响加载。下面我们开始今天的内容:
一对多关联映射
映射原理
一对多关联映射和多对一关联映射的映射原理是一致的,都是在多的一端加入一个外键,指向一的一端。关联关系都是由多端维护,只是在写映射时发生了变化。
多对一和一对多的区别
多对一和一对多的区别在于维护的关系不同:
(1)多对一:多端维护一端的关系,在加载多端时,可以将一端加载上来。
(2)一对多:一端维护多端的关系,在加载一端时,可以将多端加载上来。
分类
一对多单向关联映射
对象模型
从对象模型中,我们可以看出,Group持有User的一个引用。由于是单向关联,所以数据在加载Group时,会把User加载上来,但是User并不知道Group的存在。
我们先看一下Group和User的实体,以及映射文件。
Group
- package com.liang.hibernate;
- import java.util.Set;
- public class Group {
- private int id;
- private String name;
- private Set users;
- 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;
- }
- public Set getUsers() {
- return users;
- }
- public void setUsers(Set users) {
- this.users = users;
- }
- }
User
- package com.liang.hibernate;
- public class User {
- 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;
- }
- }
User.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>
- <class name="com.liang.hibernate.User" table="t_user">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- </class>
- </hibernate-mapping>
Group.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>
- <class name="com.liang.hibernate.Group" table="t_group">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <!-- users属性,表达的是本对象与User的一对多的关系 -->
- <set name="users">
- <!--当前表(Group)的主键-->
- <key column="groupid"/>
- <one-to-many class="com.liang.hibernate.User"/>
- </set>
- </class>
- </hibernate-mapping>
生成的表结构和测试数据
缺点
1)因为多端User不知道Group的存在(也就是User不维护与Group的关系),所以在保存User时,关系字段groupId为null,如果该字段设置为非空,则将无法保存数据。
2)因为User不维护关系,而Group维护关系,Group就会发出多余的update语句,保证Group和User有关系,这样加载Group时才把该Users对应的用户加载上来。
一对多双向关联映射
对象模型
双向关联映射对比单向关联映射,对象的加载方向由单向变成了双向。
我们看一下Group和User的实体,映射文件
Group
- package com.liang.hibernate;
- import java.util.Set;
- public class Group {
- private int id;
- private String name;
- private Set users;
- 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;
- }
- public Set getUsers() {
- return users;
- }
- public void setUsers(Set users) {
- this.users = users;
- }
- }
User
- package com.liang.hibernate;
- public class User {
- private int id;
- private String name;
- private Group groups;
- 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;
- }
- public Group getGroups() {
- return groups;
- }
- public void setGroups(Group groups) {
- this.groups = groups;
- }
- }
Group.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.liang.hibernate">
- <class name="Group" table="t_group">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <!-- 影响控制反转:inverse="false",多的一端维护关系,让一的一端失效 -->
- <set name="users" inverse="true">
- <key column="groupid" not-null="true"/>
- <one-to-many class="User"/>
- </set>
- </class>
- </hibernate-mapping>
User.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.liang.hibernate">
- <class name="User" table="t_user">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <!-- groups属性,表达的是本对象与Group的多对一的关系 -->
- <many-to-one name="groups" class="Group" column="groupid"/>
- </class>
- </hibernate-mapping>
生成的表和测试数据
一对多双向关联的映射方式:
1)在一的一端的集合上采用<key>标签,在多的一端加入一个外键
2)在多的一端采用<many-to-one>标签
注意:<key>标签和<many-to-one>标签加入的字段保持一直,否则会产生数据混乱。
inverse属性:
inverse属性可以用在一对多和多对多双向关联上,inverse属性默认为false,为false表示本端维护关系,如果inverse为true,则本端不能维护关系,会交给另一端维护关系,本端失效。所以一对多关联映射我们通常在多的一端维护关系,让一的一端失效,所以设置为inverse为true。
注意:inverse属性,只影响数据的存储,也就是持久化。
目的
一对多双向关联映射的目的主要是为了解决一对多单向关联的缺陷而不是需求驱动的。
总结
一对多关联映射还是很简单的,下篇博文我们介绍多对多关联映射。谢谢关注。
对比
一对一单向和双向关联映射的区别正是对象模型和关系模型的区别之一。
对象模型:有方向性。它到底是单向还是双向是由对象模型决定的即配置文件决定。
关系模型:没有方向性或者说是双向的。从任何一端都可以加载另一端。
下载
以上内容,只证明了一对一双向关联映射不影响存储即没有改变表结构,但不能证明关联是双向的,需要写相应的测试用例,我们以源码的形式给大家。源码下载
总结
一对一双向关联映射并不是必须的,是由需求下决定的。如果没有这样的需求,用户也没有要求,系统也不需要,就没有必要建立双向的关联映射。
下篇博文,我们介绍多对多单向关联映射,谢谢关注。
项目中,多对一关联映射是最常见的映射,但它是Hibernate的关联映射中最简单的一种映射关系。下篇博文,我们介绍一对一单向关联映射,谢谢关注。
上篇博文【SSH进阶之路】Hibernate映射——一对多关联映射(七),我们介绍了一对多关联映射,它是多对多关联映射的基础。
多对多映射是现实生活中最常见的映射,也是最容易理解的映射。废话少说,直接开始。
映射原理
不论是单向关联还是双向关联都是通过第三张表,将两个表中的主键放到第三张做一个关联。用第三张表来解决可能会造成数据冗余的问题。
举例
一个用户(User)对多个角色(Role),一个角色对多个用户。
分类
单向的多对多关联映射(单向User--->Role)
对象模型
关系模型
实例
下面我们看一下实体类和映射文件的代码。
User
- package com.liang.hibernate;
- import java.util.Set;
- public class User {
- private int id;
- private String name;
- private Set roles;
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public Set getRoles() {
- return roles;
- }
- public void setRoles(Set roles) {
- this.roles = roles;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
Role
- package com.liang.hibernate;
- public class Role {
- 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;
- }
- }
User.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.liang.hibernate">
- <class name="User" table="t_user">
- <id name="id">
- <generator class="native"></generator>
- </id>
- <property name="name"></property>
- <!-- roles属性,表达的是本对象(User)与Role的多对多的关系 -->
- <set name="roles" table="t_user_role">
- <!--当前表(User)的主键-->
- <key column="user_id"></key>
- <many-to-many class="Role" column="role_id"></many-to-many>
- </set>
- </class>
- </hibernate-mapping>
Role.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.liang.hibernate">
- <class name="Role" table="t_role">
- <id name="id">
- <generator class="native"></generator>
- </id>
- <property name="name"></property>
- </class>
- </hibernate-mapping>
生成的表结构和测试数据
多对多关联映射,在实体类中,跟一对多关联映射一样,也是用集合来表示的。<set>标签中用table属性重命名中间表名称,<key>标签定义当前表的主键,用<many-to-many>标签来关联另一张表。
双向的多对多关联映射(双向User<--->Role)
对象模型
关系模型
同上
实例
下面我们看一下实体类和映射文件的代码。
User
- package com.liang.hibernate;
- import java.util.Set;
- public class User {
- private int id;
- private String name;
- private Set roles;
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public Set getRoles() {
- return roles;
- }
- public void setRoles(Set roles) {
- this.roles = roles;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
Role
- package com.liang.hibernate;
- import java.util.Set;
- public class Role {
- private int id;
- private String name;
- private Set users;
- public Set getUsers() {
- return users;
- }
- public void setUsers(Set users) {
- this.users = users;
- }
- 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;
- }
- }
User.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.liang.hibernate">
- <class name="User" table="t_user">
- <id name="id">
- <generator class="native"></generator>
- </id>
- <property name="name"></property>
- <!-- roles属性,表达的是本对象(User)与Role的多对多的关系 -->
- <set name="roles" table="t_user_role">
- <!--当前表(User)的主键-->
- <key column="user_id"></key>
- <many-to-many class="Role" column="role_id"></many-to-many>
- </set>
- </class>
- </hibernate-mapping>
Role.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.liang.hibernate">
- <class name="Role" table="t_role">
- <id name="id">
- <generator class="native"></generator>
- </id>
- <property name="name"></property>
- <!-- users属性,表达的是本对象(Role)与User的多对多的关系 -->
- <set name="users" table="t_user_role">
- <!--当前表(Role)的主键-->
- <key column="role_id"></key>
- <many-to-many class="User" column="user_id"></many-to-many>
- </set>
- </class>
- </hibernate-mapping>
生成的表结构和测试数据
多对多双向关系中,User和Role的映射文件相同,值得注意的是生成的中间表名称必须一样,生成中间表的字段必须一样。
总结
多对多关联映射到此就结束了,经过对一对多关联映射的学习,相对而言,多对多关联映射变得非常的简单了,非常像一对多关联映射的变体。
Hibernate的总结篇马上杀到,谢谢关注。
这篇博文是Hibernate系列的最后一篇,既然是最后一篇,我们就应该进行一下从头到尾,整体上的总结,将这个系列的内容融会贯通。
概念
Hibernate是一个对象关系映射框架,当然从分层的角度看,我们也说它是数据持久层的框架。
我们从上一句话可以看出Hibernate的核心:面向对象、关系映射以及数据持久化。前面两个概念很容易理解,而对于“数据持久化”,就是将数据或者某物体,永久的保存起来。现实生活中的例子有很多,例如:鲜肉冷藏,水果做成罐头,而对于编程而言就是将数据保存在文件或磁盘以及数据库中。下面我们再看一下Hibernate的核心对象:
核心对象
上面这幅图反馈给我们的信息有很多,能否读懂这幅图可以从侧面反映你对Hibernate理解的水平。下面我们说几条:
1、Hibernate有俩个主要的配置文件:(Hibernate.cfg.xml和xxx.hbm.xml)
2、Hibernate有五个或六个接口:Configuration、SessionFactory、Session、Transaction、Query和Criteria。
3、Hibernate的执行原理,看上图的箭头即可。
4、Session对象是通过SessionFactory构建的,这是Hibernate创建Session的两种方式之一。
。。。。。。
由于内容太多,我们暂时就列这么多,就不再一一列举了。下面我们再详细的介绍映射的分类。
分类
在Hibernate系列的起初,我将关系映射分为了以上四种,现在来看关系映射其实就两种,甚至一种。
1、从对象的加载方向上分为单向和双向两种。
单向和双向只影响数据的加载,并不影响数据的存储。不论是一对一,一对多还是多对多,单向和双向生成的数据库表是一样,单向和双向的不同是由对象模型决定的。
2、从对象的映射关系上分为一对多和多对一两种,它们又是从不同角度说的,所以也可以说是一种。
一对一关联映射是多对一关联映射的特例,只是在“多”的一端加上唯一的限制之后,用来表示一对一的关联关系。
多对多关联映射是一对多关联映射的特例,它们呢都是使用集合来表示多的关系,用<key>标签定义当前表的主键。
当然它们既有联系也有区别,区别就不再一一列举了,系列博文中已经详细的介绍了各自的联系与区别。
系列列表
【SSH进阶之路】Hibernate搭建开发环境+简单实例(二)
【SSH进阶之路】Hibernate映射——多对一单向关联映射(四)
【SSH进阶之路】Hibernate映射——一对一单向关联映射(五)
【SSH进阶之路】Hibernate映射——一对一双向关联映射(六)
【SSH进阶之路】Hibernate映射——一对多关联映射(七)
【SSH进阶之路】Hibernate映射——多对多关联映射(八)
总结
对于Hibernate的系列博文到今天为止,暂时宣布告一段落,但是并没有结束,因为对于Hibernate的内容还有很多,例如懒加载以及Hibernate的性能调优,这些内容我会在后面的项目中,为大家介绍。
后面博客,我们开始Spring的系列博文,谢谢关注。