Hibernate--从认识到细化了解

Posted progor

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate--从认识到细化了解相关的知识,希望对你有一定的参考价值。


本文内容

  • Hibernate的介绍与执行流程
  • Hibernate运行环境搭建
  • Hibernate的基础示例
  • 持久类
  • 核心文件配置
  • 映射文件配置
  • 建立与数据库的连接
  • 增删查改CRUD
  • 缓存
  • 事务管理
  • 关系映射
  • 日志

 

首发日期:2018-07-31


Hibernate的介绍与执行流程

  • 是一个开源的ORM框架,是一个持久层(通常都是将数据保存到数据库中)的框架。
  • 根据ORM框架的特性,它可以根据对象的创建和保存(对于表的插入、修改),类的更改(对应表结构更改)等操作自动生成对应的SQL语句,而不需要像JDBC那样需要自己去拼接SQL语句,可以说操作对象就是操作表。

 

题外话:什么是ORM(对象关系映射)?

ORM 将数据库中的表与面向对象语言中的类建立了一种对应关系,【ORM可以说是参照映射来处理数据的模型,比如说:需要创建一个表,可以定义一个类,而这个类存在与表相映射的属性,那么可以通过操作这个类来创建一个表】

 

运行流程:

  1. 当运行时,Hibernate先去读取一个核心配置文件,读取Hibernate的核心配置(数据库连接信息、日志管理、数据表映射关系等)【在读取的过程中,会将读取的配置信息逐一配置给对应的组件(连接池、日志等等)。】;
  2. 载入核心配置文件的过程的最后是读取核心配置文件中标注的“映射关系文件”,载入“映射关系文件”,建立持久化类与数据表之间的映射(同时是否有对应的数据表,如果没有表可能会进行创建,表建立之后,再建立映射);
  3. 在核心配置和映射关系都读取完毕之后。Hibernate的基本运行配置就配置完毕了。在前面的加载配置中,返回的是一个Configuration对象,Configuration对象主要是用来获取配置文件的。
  4. 通过Configuration对象获取一个sessionFactory对象,sessionFactory对象真正负责解析配置文件中的配置选项并配置给各个组件,在很多时候,sessionFactory对象相当于一个连接池,它解析了hibernate的配置并负责分发会话连接。
  5. 通过sessionFactory对象来获取一个Session对象,有了Session对象就相当于有了JDBC中的Connection对象,它相当于与数据库的会话连接。它可以帮助我们操作数据库。
  6. 对于增删改操作,我们需要事务管理的协助。通过Session对象获取一个Transaction对象,Transaction对象负责数据库的事务管理。
  7. 通过Session对象操作数据库。

 

 


Hibernate运行环境搭建

下面的运行环境基于的是Hibernate5.3.0

 

1.在官网下载Hibernate5

 

2.解压下载的压缩包

技术分享图片

3.在解压出来的文件夹中,lib存放着Hibernate运行所需要的jar包。

技术分享图片

 

 

4.因为这里仅仅做最基础的运行,所以仅仅将required中的11个jar包导入到工程中。

技术分享图片

如果你还需要其他功能,按照前面图中标注不同功能的文件夹来选择添加。比如想要使用C3P0,可以将在optional文件夹中c3p0文件夹下的包都导入。

 

5.导入Hibernate之外的包:

  • 数据库驱动包:
    • 技术分享图片【版本可以比较随意】
  • 日志包:
    • 技术分享图片【这是hibernate的日志接口,如果要扩展日志功能,需要这个接口】【这个可以在libspatial中找到】
    • 技术分享图片我们这里要使用log4j来实现日志功能,但slf4j不能直接到log4j,它需要转换器,这个就是转换器】【这个需要你自己下载】
    • 技术分享图片【这个也需要你自己下载】

 

 

6.导入包之后,运行环境就基本搭建完毕了。后面都是具体需求具体配置了。

 


Hibernate的基础示例

 

这个基础示例作用是让你了解Hibernate的使用需要做什么?先让你了解需要做什么,后面再讨论每个里面能具体怎么做。

 

1.运行环境搭建,导入需要的依赖包。

技术分享图片

2.创建持久类

public class Person {
    
    private Long pid;
    private String username;
    private Integer age;
    private Integer address;
    public Long getPid() {
        return pid;
    }
    public void setPid(Long pid) {
        this.pid = pid;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Integer getAddress() {
        return address;
    }
    public void setAddress(Integer address) {
        this.address = address;
    }
    
    
}

3.创建映射文件Person.hbm.xml

技术分享图片

 

【这里还有个可选操作:创建数据表,数据表Hibernate也可以帮你创建(十分建议提前创建,不然你可能会遇到其他问题),这里就省略了。当然,数据库应该是已经创建好了的。但我已经踩过坑了,所以我这里就懒了。】

 

4.创建核心配置文件:hibernate.cfg.xml

技术分享图片

 

5.读取核心配置文件

技术分享图片

6.与数据库交互

技术分享图片

 

 


持久类

 

 

  • 持久类是用于跟数据表产生映射关系的类。 一个没有映射关系的类不是持久化类。
  • 持久类的定义与普通的javabean没有什么区别,如果你了解javabean,那么你应该知道javabean有三个标准:
    • 1.属性私有化
    • 2.有无参构造函数 (hibernate同样需要利用反射)
    • 3.提供共有的属性getter和setter函数。
  • 除了以上与javabean相同的要求之外,还需要一个唯一标识属性。它相当于数据表中的主键,hibernate利用唯一标识属性来特异性地识别每个对象。
public class Person {
    private Long pid;//唯一标识属性,用来映射表中的主键
    private String username;
    private Integer age;
    private Integer address;
    public Long getPid() {
        return pid;
    }
    public void setPid(Long pid) {
        this.pid = pid;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Integer getAddress() {
        return address;
    }
    public void setAddress(Integer address) {
        this.address = address;
    }
}

 

几个考虑遵守的规则:

1.持久化类的属性尽量使用包装类类型。【因为基础数据类型通常存在默认值并且通常不为null,null在数据表中是空的意思,但如果是0的话,意思就是不明确了--比如某个字段用0,1代表某种状态,那么默认值为0的话就不好知道是否是忘记设置状态了。】

2.持久化类不要使用final修饰,不然无法使用延迟加载功能(这个东西将会在后面的CRUD中的查询中讲),延迟加载功能依赖于代理对象,使用final修饰之后就无法产生代理对象了。

 

 

补充:

  • 持久类还有三种状态需要认识,因为涉及到与数据库的交互和缓存区,所以留到后面缓存那里再讲。

核心文件配置

 

核心配置文件的配置方式有使用properties和xml这两种,我们主要使用xml版,因为xml版可以导入映射文件,而使用properties版的就需要自己在代码中引入映射文件。

 

 

使用XML配置核心文件:

  • 命名要求:hibernate.cfg.xml 【保存路径:src目录下,否则需要在.configure()的时候传入它的路径】
  • dtd文件去哪里找:依赖包技术分享图片下的技术分享图片下的技术分享图片中。
  • 配置选项:
    • 基础配置:这些配置是必需的。
      • 数据驱动
        • 格式:<property name="hibernate.connection.driver_class" >数据驱动</property>
        • 例如:<property name="hibernate.connection.driver_class" >com.mysql.jdbc.Driver</property>
      • url
        • 格式:<property name="hibernate.connection.url">url</property>
        • 例如:<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate</property>
      • 用户名 hibernate.connection.username
      • 密码 hibernate.connection.password
      • SQL方言:定义了怎么转换SQL语句,就好像使用谷歌翻译需要确定转成哪种语言
        • 格式:<property name="hibernate.dialect">方言</property>
        • 例如:<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
      • 映射关系:【注意,映射关系要放在最下面!】
        • 格式:<mapping resource="hbm文件的路径"/>
        • 例如:<mapping resource="work/domain/Person.hbm.xml"/>
    • 可选配置:下面的配置是可选的
      • 显示对应的sql语句:使用hibernate操作对象,会转成对应的sql语句,可以设置是否显示。
        • 格式:<property name="hibernate.show_sql">true或false</property>【true是显示,false是不显示】
        • 例如:<property name="hibernate.show_sql">true</property>
      • 格式化显示的sql语句 :当选择显示sql语句后,默认的是一行显示的,使用格式化后,是多行的。
        • 格式:<property name="hibernate.format_sql">true或false</property>
        • 例如:<property name="hibernate.format_sql">true</property>
      • 是否自动建表hibernate.hbm2ddl.auto :
        • 格式:<property name="hibernate.hbm2ddl.auto">create或creat-drop或update或validate</property>
          • 不配的时候:不使用hibernate的建表功能
          • create:不论有没有表,每次都重新创建表。
          • update:如果没有表,就创建;如果有了,就用已有;如果定义的表结构跟现有的不符合,那么修改成定义的表结构。
          • create-drop:不论有没有表,每次都重新创建表,并且在用完后就会删除。
          • validate:不会创建表,只会使用已经有了的表,所以如果结构不符合会报错。
        • 例如:<property name="hibernate.hbm2ddl.auto">update</property>

技术分享图片

 

 

那么,去哪里看能配什么,配哪些值呢?

在解压包的project的etc下面有一个hibernate.properties,里面有hibernate的核心配置信息。想使用什么功能,ctrl+f查找一下即可。

技术分享图片

 

 

使用properties配置核心配置:

  • 命名要求:hibernate.properties
  • 配置选项:在xml的配置方法中,相信你已经了解了有哪些选项可以配置了。xml版的和properties版的配置选项是一样的。
  • 格式:类似hibernate.connection.driver_class=com.mysql.jdbc.Driver

 

 

核心配置文件的加载:

  • xml版本:

技术分享图片

  • properties版本:

技术分享图片

 

 

c3p0连接池的配置:

首先需要配置hibernate的连接提供者:

技术分享图片

然后配置c3p0的选项:

技术分享图片

<!-- 配置C3P0连接池 -->
<property  name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!-- 最少可用连接数 -->
<property name="hibernate.c3p0.min_size">5</property>
<!-- 最大可用连接数 -->
<property name="hibernate.c3p0.max_size">20</property>
<!--设定数据库连接的过期时间,以秒为单位 -->
<property name="hibernate.c3p0.timeout">120</property>

最后,要记得导入c3p0的依赖包,它在hibernate文件夹的optional的c3p0中。

 

 

补充:

  • 选项hibernate.XXX前面的hibernate.是可以省去的。
  • 事务管理也需要核心配置文件的配置,这部分将在事务管理那里再讲。
  • 还有很多的配置,但都相对少用,有需要再去properties查找一下即可。

 


映射文件配置

 

映射文件的结构大概如下:

技术分享图片

怎么配置映射文件:

1. 映射文件名要求:类名.hbm.xml

2. 标准xml首部:<?xml version="1.0" encoding="UTF-8"?>

3. dtd约束:对于映射文件,它的dtd可以在技术分享图片技术分享图片中获取。打开文件,你能看到dtd写在了注释里面。hibernate5与hibernate3的相似,通常使用的都是3.0版本的。

4.创建标签hibernate-mapping

后续的内容都在hibernate-mapping中配置,下面讲hibernate-mapping怎么配置:

  • hibernate-mapping
    • 常用子标签:class标签
  • class标签:用来建立类与表的映射关系
    • 属性:
      • name:对应的持久类的全路径
      • table:对应的数据表的名字
      • catalog:数据库名【比较少用】
    • 常用子标签:id,property,set(涉及一对多,多对多),many-to-one(涉及一对多)

 

  • id标签:用来建立类中的属性与表中的主键的对应关系
    • 属性:
      • name:对应的持久类的唯一标识属性的名字
      • column:对于表中的主键名【如果表中的主键与持久类中的唯一标识属性同名,那么可以省去column】
      • length:定义数据类型的长度
      • type:字段的数据类型
    • 常用子标签:generator
  • generator标签:定义主键生成策略。这里暂时不讲,这是一个知识点,留到主键生成策略那里再讲。
  • property标签:用来建立类中的普通属性与表的字段的对应关系
    • 属性:
      • name:对应的持久类的属性名
      • column:对于表中的字段名【如果表中的字段名与持久类中的属性同名,那么可以省去column】
      • length:定义数据类型的长度
      • type:字段的数据类型【type和length主要是用来定义字段的属性的,如果你不关心字段的属性,只需要映射关系,那么可以省去。】【定义的时候,会对应去更改表结构;没有表的时候,会根据这些属性来创建新表;如果没表也没有这些关于字段的属性的属性,那么会使用默认值。比如varchar长度会达到255】
      • not-null:字段是否有非空属性。【同样的,也是用来定义字段的属性的,不关系可以不用】
      • unique:字段是否有唯一属性。【同样的,也是用来定义字段的属性的,不关系可以不用】

 

 

 

主键生成策略:

id标签用来配置主键,generator标签是用来配置主键生成策略的(主键的值的策略),generator的class的值常用的有如下几个

技术分享图片

  • increment:使用Hibernate自增长机制来生成主键。使用这个属性之后,使用自动建表,表会有主键属性。但它是线程不安全的,它的原理是获取数据库中主键当前最大值来确定新主键的值。所以它只能用于单线程。
  • identity: 使用数据库自带的自增长机制,适用于DB2、SQL Server、MySQL、Sybase。
  • sequence:使用数据库自带的自增长机制,适用于oralce、DB、SAP DB、PostgerSQL。
  • uuid:随机生成字符串作为主键的值。
  • native:使用数据库自带的自增长机制,会根据不同的数据库来采用identity或sequence。
  • assigned:放弃管理,由自己调用setter函数给主键设置值。
  • foreign:使用关联表的主键值作为主键的值。主要用于一对一关系中。【这个这里不讲】

 

补充:

  • 映射文件的配置中还有一个重头戏-- 一对多、多对多关系的配置,因为这是一个重点内容,所以分到下面的关系映射中来讲。

 


建立与数据库的连接

 

上面已经讲述了配置核心配置文件,配置映射文件,下面就是与数据库的交互了。要与数据库交互要了解以下几个类

 

Configuration:

  • 作用:它负责加载核心配置
  • 使用:
    • 对于xml版的核心配置文件:只需要Configuration cfg = new Configuration().configure() 返回的是读取了hibernate.cfg.xml配置的Configuration对象
    • 对于properties版的核心配置文件:由于properties版的无法导入映射文件,所以需要加载配置:Configuration cfg = new Configuration(),然后再导入映射文件:configuration.addResource("com/itheima/hibernate/demo1/Customer.hbm.xml");【这里为了帮助了解使用,直接给了一个参照路径】

 

SessionFactory:

  • 作用:加载了核心配置文件之后,就需要进行hibernate与数据库的连接了,SessionFactory对象在很多时候都仅仅相当于一个连接池对象。
  • 使用:SessionFactory factory = configure.buildSessionFactory()

 

 

Session:

  • 作用:SessionFactory对象相当于一个连接池对象,那么可以从它那里获取连接,返回的是一个Session对象,Session对象就相当于jdbc中的Connection对象,我们可以使用Session对象来操作数据库。
  • 使用:Session session = factory.openSession();
    • 如果使用session来操作数据库,我们下面再讲。这里主要讲连接。

 

 

Transaction:

  • 作用:Hibernate的增删改操作需要事务管理,Transaction是事务管理对象,它可以通过Session对象来获取。
  • 使用:
    • 获取:Transaction transaction = session.beginTransaction();
    • 提交事务:transaction.commit();
    • 回滚事务:transaction.rollback();

 

 

了解上面对象的作用与使用之后,我们就可以得出以下与数据库建立连接并交互的步骤:

技术分享图片

 

 


增删查改CRUD

 

了解了怎么建立与数据库的连接之后,下面就是CRUD的内容了。

 

新增:

1.创建持久类对象

2.session对象调用save(Object obj)来保存到数据库

技术分享图片

 

 

查询:

查询是一个很重要的内容,考虑到这篇博文已经很长了的,所以我留到另外一篇博文去讲述了。

详情点击下面超链接。

Hibernate:查询

 

修改:

  • 修改有两种方法:
    • 创建一个对象,给对象的唯一标识属性赋予一个值,给各个属性赋值,然后session.update(这个对象)【问题是需要给各个属性赋值】
    • 先查询,再修改【推荐操作】:先查询出对象,修改好对象的指定属性,然后session.update(这个对象)

【这里给的例子使用的查询是OID查询,更多查询方法请查看"查询"那里的用法】

public void test1() {
        Configuration cfg = new Configuration().configure();
        SessionFactory factory = cfg.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        Person person = session.get(Person.class, 2L);
        person.setUsername("葫芦娃");
        session.update(person);
        transaction.commit();
    }

 

删除:

  • 删除有两种方法:
    • 创建一个对象,给对象的唯一标识属性赋予一个值,然后session.delete(这个对象)
    • 先查询,再删除【推荐操作】:先查询出对象,然后session.delete(这个对象)

【这里给的例子使用的查询是OID查询,更多查询方法请查看"查询"那里的用法】

public void test2() {
        Configuration cfg = new Configuration().configure();
        SessionFactory factory = cfg.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        Person person = session.get(Person.class, 2L);
        session.delete(person);
        transaction.commit();
    }

 


缓存

 

缓存:是一种优化的方式,将数据存入到内存中,使用的时候直接从缓存中获取,不用通过存储源。

 

  • session管理着Hibernate的一级缓存。
  • 缓存区中有一块特殊的区域--快照区,缓存区存储着本地对象,快照区存储着"查询到的对象"。
    • 缓存区是用来存储被session管理的持久类对象的(查询得来的、新建并通过session保存的对象都会保存到缓存区),通常来说都是处于“持久态”的对象;而快照区存储的是查询来的对象,主要用于校验本地对象的数据跟数据表是否一致。
    • 一个新建的对象是不会存储到缓存区的,只有它被session管理了,才会存储到缓存区中,并且不会存储到快照区中。

 

 

缓存对效率的影响:

  • 查询一个持久类对象时,如果这个持久类对象缓存区已经有了,那么不会再发送SQL语句。
  • 修改一个持久类对象后,如果缓存区的对象与快照区的对象的数据不一样,那么会自动更新到数据表中。

 

 

持久化类的三种状态:

 

瞬时态:新建的持久类,唯一标识属性没有值的时候,没有被session管理的时候。

持久态:唯一标识属性有值,被session管理的时候(要么是用session从数据库中查询出来的;要么是已经使用过session保存到数据库中,并且还没有被删除的。)。持久态的对象会存储到缓存区中,用来提高效率。

脱管态:唯一标识属性有值,不被session管理的时候。

持久类状态转换图:

技术分享图片


事务管理

 

事务的开启、提交与回滚:

开始事务:Transaction transaction = session.beginTransaction();

事务提交:transaction.commit();

事务回滚:transaction.rollback();

 

 

事务隔离级别配置:

事务隔离级别配置要在核心配置文件中配置:

<property name="hibernate.connection.isolation">4</property>

值可以为:

1:Read Uncommitted
2:Read Committed
4:Repeatable Read
8:Serializable

 

 

不同函数中的事务管理:

     使用Hibernate之后,操作数据库转成了操作一个个对象,当在一个函数中操作对象中,事务管理可以直接在函数中使用Transaction来管理。

     但在使用Hibernate来实现DAO层的功能的时候,如果你把功能细化了,比如加钱是一个函数,减钱是一个函数。当需要考虑到事务管理的时候,你可能需要把一个session传入到这两个函数中才能实现事务管理。你可能会想到把session作为形参传进去,这是一个解决方法。但Hibernate提供了一个更好的解决方案。

     这时候我们可以使用getCurrentSession来获取session,getCurrentSession是通过当前线程来获取一个session,同一个线程获取的session是同一个,所以同一个线程执行不同的函数依然可以很好的进行事务管理。

所以很多时候,factory.getCurrentSession()取代了Session session = factory.openSession()。

getCurrentSession的正常使用需要核心配置文件加上一个配置选项:

<property name="hibernate.current_session_context_class">thread</property>

 


关系映射:

 

在上面的基础示例中,演示的是一个单表的例子。而事实上,项目中的表与表之间是有关系的。并且我们经常需要利用这些关系来获取数据。

 

一对多关系:

【为了帮助理解,步骤与演示一起讲述,引用框里面不是引用,是例子演示。以班级与学生之间的一对多关系(假设一个班级可以有多个学生,一个学生只能有一个班级)为例】

1.创建“多”方的类,在普通的单表的基础上要多出一个“多”一方的类的对象,这个类的对象是用来映射成外键字段的,所以这个类不需要另外创建映射到外键的“属性”(一对多都是基于外键,不使用Hibernate的时候,可能需要手动去创建外键外键和手动去给外键指定值;但这里使用了Hibernate之后,它会自动映射成外键。)。

建立学生类Student,添加一个班级类Grade的对象--用来映射学生所对应的班级;

技术分享图片

2.创建“一”方的类,在普通的单表的基础上要多出一个“一”一方的对象组成的集合(这里最好提前new一下,免得后面再去赋值,new了之后,后面就可以直接get来add了)。这个集合是用来帮助Hibernate处理它们之间的关系的,不会在表中生成新的字段。

建立班级类Grade,添加一个set集合,用来映射班级所对应的多个学生

技术分享图片

3.建立"多"一方的hbm,像单表配置一样配置完“多”一方的属性(除去那个“一”一方的对象);配置剩下的那个“一”一方的对象需要使用many-to-one标签来配置,name属性是那个“一”一方的对象的变量名,class属性是“一”一方的类的全路径,column属性是“一”一方的对象要映射成的外键的字段名(这个字段名会出现在“多”一方的表中)。

建立Student.hbm.xml:

技术分享图片

4.建立“一”一方的hbm,像单表配置一样配置完“一”一方的属性(除去那个“多”一方的集合对象);配置剩下的那个“多”一方的集合对象需要使用set标签来配置,name属性是那个“多”一方的集合对象的变量名,key中的column属性是”多“一方的外键字段名。one-to-many的class属性是“多”一方的类的全路径。

Grade.hbm.xml

技术分享图片

5.在hibernate.cfg.xml中导入映射文件

导入映射文件

技术分享图片

 

6.编写测试类,将”一“的一方的对象和”多“的一方的对象互相建立上关系(把一一方的对象设置到多的一方的对象的属性中,把多的一方的对象添加到一的一方的集合中),然后保存起来。

创建两个学生(李白、杜甫)、一个班级(唐朝);给学生的grade属性设置值,将两个学生加入到班级的学生集合中;把三个对象都保存起来。

public void test3() {
        Configuration configure = new Configuration().configure();
        SessionFactory factory = configure.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        //创建两个学生一个班级
        Student s1=new Student();
        Student s2=new Student();
        s1.setName("李白");
        s2.setName("杜甫");
        Grade g=new Grade();
        g.setCname("唐朝");
        //设置关系
        s1.setGrade(g);
        s2.setGrade(g);
        g.getStudents().add(s1);
        g.getStudents().add(s2);
        //保存
        session.save(s1);
        session.save(s2);
        session.save(g);
        transaction.commit();
    }

 

结果:技术分享图片技术分享图片

 

 

 

多对多关系:

(假设一方为A,一方为B)

1.创建A的持久类,里面有一个B类对象组成的集合【例子演示以一个商品可以属于多个购物车,一个购物车可以有多个商品为例。】

创建购物车类ShopCar:

技术分享图片

 

2.创建B的持久类,里面有一个集合,

创建产品类Product:

技术分享图片

 

3.创建A的hbm,使用property配置好基本属性,使用set配置配置集合:set中的name的值是自身存储的对方对象的集合的变量名,table是中间表的名称(自动创建);key中的column的值是自身在中间表映射的外交字段名;many-to-many中class的值是对方类的全路径,column的值是对方类在中间表映射的外键字段名。

创建ShopCar.hbm.xml:

技术分享图片

4.创建B的hbm,像第三步一样配置好

创建Product.hbm.xml:

技术分享图片

5.在hernate.cfg.xml中导入映射文件

导入Product.hbm.xml和ShopCar.hbm.xml

技术分享图片

6.编写测试类,将一个A类对象与多个B类对象建立关联,将一个B类对象与多个A类对象建立关联,把所有A和B的对象保存,查看中间表的数据。

编写测试类,建立两个Product,建立两个ShopCar,让他们之间相互都建立起关联。

public void test4() {
        Configuration configure = new Configuration().configure();
        SessionFactory factory = configure.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        //新建两个订单,两个产品
        ShopCar o1=new ShopCar();
        ShopCar o2=new ShopCar();
        o1.setCustomer("马云");
        o2.setCustomer("马化腾");
        Product p1=new Product();
        Product p2=new Product();
        p1.setProduct_name("聚宝盆");
        p2.setProduct_name("招财猫");
        //相互建立关系
        o1.getProducts().add(p1);
        o2.getProducts().add(p1);
        o1.getProducts().add(p2);
        o2.getProducts().add(p2);
        //保存
        session.save(o1);
        session.save(o2);
        session.save(p1);
        session.save(p2);
        transaction.commit();
    }

结果:技术分享图片技术分享图片技术分享图片

 

 

 

级联操作:

什么是级联操作?当表之间有关系的时候,对一个表做操作(删除、修改),对应去更改关联的表。【注意!这里的关系相对于表而言,但对于对象来说,是对象中是否存储着另一方的对象(当然还需要映射关系的帮助),所以只有关联上的才会级联,没有关联上的不会级联】

一对多的级联操作设置:多中的set中的cascade,一中的many-to-one中的cascade

<many-to-one name="grade" class="work.domain.Grade"  column="gid" cascade="save-update"></many-to-one>

多对多的级联操作设置:set中的cascade

<set name="shopcars" table="shopcar_product" cascade="save-update">
            <key column="product_id"></key>
            <many-to-many class="work.domain.ShopCar" column="shopcar_id"></many-to-many>
        </set>
  • cascade的值:
    • save-update 【级联保存或修改】
      • set中配置了,那么"一"一方的对象保存时,会同时保存建立了关系的"多"一方的对象。
      • many-to-one中配置了,那么"多"一方的对象保存时,会同时保存建立了关系的"一"一方的对象。
    • delete【级联删除】
      • 这个比较少有与多对多,通常使用于一对多中的一一方(一个球队解散了,就删除对应的球员信息)。

示例:

在学生hbm中加上cascade="save-update":那么只保存学生对象,也可以同时保存学生对象所管理的班级对象。

<many-to-one name="grade" class="work.domain.Grade"  column="gid" cascade="save-update"></many-to-one>
public void test5() {
            Configuration configure = new Configuration().configure();
            SessionFactory factory = configure.buildSessionFactory();
            Session session = factory.openSession();
            Transaction transaction = session.beginTransaction();
            //创建两个学生一个班级
            Student s1=new Student();
            s1.setName("林则徐");
            Grade g=new Grade();
            g.setCname("清朝");
            //设置关系
            s1.setGrade(g);
            g.getStudents().add(s1);
            //保存
            session.save(s1);//只保存学生,可以同时保存这个学生对象所关联的班级对象
            transaction.commit();
        }

 

 

题外话:对于一对多级联操作(假设一一方为A,一方为B),可能会产生多余SQL语句,原因是缓存区中A,B的数据都发生了变化了,使得不与快照区的数据映像相同,Hibernate默认就会对更新了的对象进行修改处理。

      在新建的时候,不涉及到快照区,所以不会发生上述问题;在修改外键的时候就会发生,比如先查询出来一个A,两个B(数据会存储到缓存区和快照区),A原本仅仅与B1关联,现在增加了与B2的关联,那么这时候缓存区中的A对象发生了数据更改,B2对象也发生了更改。在提交的时候,校验缓存区和快照区的数据发现缓存区的A和B2与快照区的数据不同了,这时候hibernate会发起对A的数据更新(外键新增)以及对B2的数据更新(外键新增)。所以发起了两次外键新增,这就产生了多余的SQL语句。【而多对多中,Hibernate考虑到如果两个都有的话,会在中间表插入重复的,所以多对多不会发生这个问题(测试过!)】

     解决方案:inverse是Hibernate定义的一种维护外键的属性,当这个属性设置为true时,那么设置的一方会放弃外键维护权(代表外键更新了它也不会去管理),对于一对多,通常都是在set上配置inverse=”true”,让多的一方去维护外键 。

 


日志

 

如果你已经导入前面说讲的那几个日志包了的话,那么你还需要一个log4j.properties就可以配置好你的日志功能了。

  • 日志包:
    • 技术分享图片【这是Hibernate的日志接口,如果要扩展日志功能,需要这个接口】【这个可以在libspatial中找到】
    • 技术分享图片我们这里要使用log4j来实现日志功能,但slf4j不能直接到log4j,它需要转换器,这个就是转换器】【这个需要你自己下载】
    • 技术分享图片【这个也需要你自己下载】

 

至于log4j.properties怎么写,project/etc中有一个log4j.properties,可以参照那个来写。

 

#
# Hibernate, Relational Persistence for Idiomatic Java
#
# License: GNU Lesser General Public License (LGPL), version 2.1 or later.
# See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
#

### 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

#log4j.logger.org.hibernate=info
log4j.logger.org.hibernate=debug

### log HQL query parser activity
#log4j.logger.org.hibernate.hql.ast.AST=debug

### log just the SQL
#log4j.logger.org.hibernate.SQL=debug

### log JDBC bind parameters ###
log4j.logger.org.hibernate.type=info
#log4j.logger.org.hibernate.type=debug

### log schema export/update ###
log4j.logger.org.hibernate.tool.hbm2ddl=debug

### log HQL parse trees
#log4j.logger.org.hibernate.hql=debug

### log cache activity ###
#log4j.logger.org.hibernate.cache=debug

### log transaction activity
#log4j.logger.org.hibernate.transaction=debug

### log JDBC resource acquisition
#log4j.logger.org.hibernate.jdbc=debug

### enable the following line if you want to track down connection ###
### leakages when using DriverManagerConnectionProvider ###
#log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace

 


写在最后:Hibernate毕竟是一个框架,市面上的书写它都能写它几百页,在一篇博文内不可能把内容写得详尽。所以这篇博文很大程度上主要是你让你了解Hibernate,剩下的就要你自己去了解了。获取百度才是你最好的老师?

 这篇博文断断续续写了三天,很多时候都是在想以什么一种方式来讲述才能更好地让大家理解。最终成了这个样子,希望能帮到大家。


以上是关于Hibernate--从认识到细化了解的主要内容,如果未能解决你的问题,请参考以下文章

具有运行时 pojos 的带有 Hibernate 的 OSGi 片段包

关于JS递归函数细化认识及实用实例

Hibernate之全面认识

面试问hibernate和mybatis的区别和对mybatis的认识

如何从 recyclerview 片段传递到另一个 recyclerview 片段

Hibernate注解