文章目录


一、Hibernate的入门

1、Hibernate的概述

 1-1 javaEE 三层结构

SSH之Hibernate(一)_hibernate

1-2 什么是Hibernate

SSH之Hibernate(一)_持久化_02


Hibernate是一个持久层的ORM框架。


1-3 什么是ORM


ORM:Object Relational Mapping(对象关系映射)。指的是将一个Java中的对象与关系型数据库中的表建立一种映射关系,从而操作对象就可以操作数据库中的表。


SSH之Hibernate(一)_主键_03

1-4 为什么学习Hibernate

SSH之Hibernate(一)_主键_04

2、Hibernate的入门

 2-1 下载Hibernate的开发环境

Hibernate:
https://sourceforge.net/projects/hibernate/files/hibernate-orm/5.0.7.Final/
mysql驱动包:
https://pan.baidu.com/s/1lpa5eNkuoI-Uw6arDf3rlw
log4j:
https://pan.baidu.com/s/1fi6jNGtWtwp_8fXQo-cGPg

 2-2 解压Hibernate

SSH之Hibernate(一)_持久化_05


  • documentation :Hibernate开发的文档
  • lib :Hibernate开发包
     required :Hibernate开发的必须的依赖包
     optional :Hibernate开发的可选的jar包
  • project :Hibernate提供的项目

 2-3 创建一个项目,引入jar包


  • 数据库驱动包
  • Hibernate开发的必须的jar包
  • Hibernate引入日志记录包
    SSH之Hibernate(一)_hibernate_06

 2-4 创建表

CREATE TABLE `cst_customer` (
`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
`cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
`cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
`cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',
`cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

 2-5 创建实体类

public class Customer {
private Long cust_id;
private String cust_name;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_phone;
//省略get和set方法

 2-6 创建映射

映射需要通过XML的配置文件来完成,这个配置文件可以任意命名。尽量统一命名规范(类名.hbm.xml)

这里创建一个Customer.hbm.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!-- 下面的约束在hibernate-core-5.0.7.Final.jar下的org.hibernate最后面的hibernate-mapping-3.0.dtd下 -->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping><!-- 根标签 -->
<!-- 建立类与表的映射,name代表哪个类,table代表哪个表。哪个类与哪个表形成映射 -->
<class name="com.qgc.hibernate.Demo1.Customer" table="cst_customer">
<!-- 建立类中的属性与表中的主键对应,name代表类里面的属性,column代表表里面的字段 -->
<id name="cust_id" column="cust_id">
<!-- 使用本地主键生成策略native -->
<generator class="native"></generator>
</id>

<!-- 建立类中普通的属性与表中字段的对应 -->
<property name="cust_name" column="cust_name"></property>
<property name="cust_source" column="cust_source"></property>
<property name="cust_industry" column="cust_industry"></property>
<property name="cust_level" column="cust_level"></property>
<property name="cust_phone" column="cust_phone"></property>
</class>
</hibernate-mapping>

 2-7 创建Hibernate核心配置文件

Hibernate的核心配置文件的名称:hibernate.cfg.xml

在src下创建hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 下面的约束在hibernate-core-5.0.7.Final.jar下的org.hibernate最后面的hibernate-configuration-3.0.dtd下 -->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 下面的参数可以在project\etc\hibernate.properties文件中找到 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://47.97.91.178:3306/hibernate_day01</property>
<property name="hibernate.connection.username">qgc</property>
<property name="hibernate.connection.password">2wsx@WSX</property>
<!-- 配置Hibernate的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!--=============可选配置====================-->
<!-- 打印sql语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化sql -->
<property name="hibernate.format_sql">true</property>
<!-- 数据库中没有这个表时,自动建表-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!--=============可选配置====================-->
<!-- 告诉hibernate映射文件在哪,注意此处不是.而是/ -->
<mapping resource="com/qgc/hibernate/Demo1/Customer.hbm.xml"/>
</session-factory>
</hibernate-configuration>

 2-8 编写测试代码

在包下新建一个HibernateDemo1的类

public class hibernateDemo1 {
@Test
//保存客户案例
public void test() {
//1、加载Hibernate核心配置文件
Configuration configure = new Configuration().configure();
//2、创建一个sessionFactory对象:类似于JDBC中的连接池
SessionFactory sessionFactory = configure.buildSessionFactory();
//3、通过sessionFactory获取到session连接对象:类似于JDBC中的Connection
Session session = sessionFactory.openSession();
//4、手动开启事务
Transaction transaction = session.beginTransaction();
//5、编写代码
Customer customer = new Customer();
customer.setCust_name("test");
session.save(customer);
//6、提交事务
transaction.commit();
//7、资源释放
session.close();
}
}

3、Hibernate的常见配置

 3-1 不联网xml不提示问题解决

选择eclipse上方的window按钮,选择Preference。在弹出的对话框中的左上角输入xml catalog

右侧选择User…单机add

SSH之Hibernate(一)_主键_07

SSH之Hibernate(一)_hibernate_08

 3-2 映射的配置


  • 【class标签的配置】


  1. 标签用来建立类与表的映射关系
  2. 属性:
     name :类的全路径
     table :表名(类名与表名一致,table可以省略)
     catalog :数据库名


【id标签的配置】

  1. 标签用来建立类中的属性与表中的主键的对应关系
  2. 属性:
     name :类中的属性名
     column :表中的字段名(类中的属性名和表中的字段名如果一致,column可以省略)
     length :长度
     type :类型


【property标签的配置】

  1. 标签用来建立类中的普通属性与表的字段的对应关系
  2. 属性:
     name :类中的属性名
     column :表中的字段名
     length :长度
     type :类型
     not-null :设置非空
     unique :设置唯一



 3-3 核心的配置

例如:

<hibernate-configuration>
<session-factory>
<!-- 下面的参数可以在project\etc\hibernate.properties文件中找到 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://47.97.91.178/hibernate_day01</property>
<property name="hibernate.connection.username">qgc</property>
<property name="hibernate.connection.password">2wsx@WSX</property>
<!-- 配置Hibernate的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property> -->
<!-- 告诉hibernate映射文件在哪,注意此处不是.而是/ -->
<!-- 打印sql语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化sql -->
<property name="hibernate.format_sql">true</property>
<mapping resource="com/qgc/hibernate/Demo1/Customer.hbm.xml"/>

</session-factory>
</hibernate-configuration>


必须的配置
连接数据库的基本的参数


  1. 驱动类
  2. url路径
  3. 用户名
  4. 密码

方言



可选的配置
显示SQL :hibernate.show_sql
格式化SQL :hibernate.format_sql
自动建表 :hibernate.hbm2ddl.auto


  1. none :不使用hibernate的自动建表
  2. create :如果数据库中已经有表,删除原有表,重新创建,如果没有表,新建表。(测试)
  3. create-drop :如果数据库中已经有表,删除原有表,执行操作,删除这个表。如果没有表,新建一个,使用完了删除该表。(测试)
  4. update :如果数据库中有表,使用原有表,如果没有表,创建新表(更新表结构)
  5. validate :如果没有表,不会创建表。只会使用数据库中原有的表。(校验映射和表结构)。



映射文件的引入
引入映射文件的位置




​<mapping resource="com/qgc/hibernate/Demo1/Customer.hbm.xml"/>​


4、Hibernate的核心API

 4-1 Configuration对象(Hibernate的配置对象,了解)

SSH之Hibernate(一)_hibernate_09

作用:

  • 加载核心配置文件


如果是hibernate.properties文件


Configuration cfg = new Configuration();


如果是hibernate.cfg.xml


Configuration cfg = new Configuration().configure();
  • 加载映射文件
// 手动加载映射
configuration.addResource("com/itheima/hibernate/demo1/Customer.hbm.xml");

4-2 SessionFactory对象(Session工厂)

SessionFactory内部维护了Hibernate的连接池和Hibernate的二级缓存(不讲)。是线程安全的对象。一个项目创建一个对象即可。

  • 显示日志(了解)
    在src下面新建log4j.properties配置文件,写入如下代码
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
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 mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c\:mylog.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' ###
# error warn info debug trace 这是权限,如果设置为info这显示error,warn和info的信息
# stdout为向控制台打印 file为向文件输出
log4j.rootLogger= info, stdout
  • 配置连接池(了解)
    在hibernate资源包下的\lib\optional\c3p0下,将3个jar包导入
    在hibernate.cfg.xml文件下插入如下代码
<!-- 配置C3P0连接池 -->
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!--在连接池中可用的数据库连接的最少数目 -->
<property name="c3p0.min_size">5</property>
<!--在连接池中所有数据库连接的最大数目 -->
<property name="c3p0.max_size">20</property>
<!--设定数据库连接的过期时间,以秒为单位,
如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
<property name="c3p0.timeout">120</property>
<!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
<property name="c3p0.idle_test_period">3000</property>
  • 抽取工具类
public class HibernateUtils {
public static final Configuration cfg;
public static final SessionFactory sf;
static {
cfg = new Configuration().configure();
sf = cfg.buildSessionFactory();
}
public static Session getSession() {
return sf.openSession();
}
}

4-3 Session对象(类似Connection对象是连接对象)

Session代表的是Hibernate与数据库的链接对象。不是线程安全的。与数据库交互桥梁。

Session中的API:


  • 保存方法
    Serializable save(Object obj);
  • 查询方法
    T get(Class c,Serializable id);
    T load(Class c,Serializable id);
    如下代码:

public void test2() {
Session session = HibernateUtils.getSession();
Transaction transaction = session.beginTransaction();

Customer customer = session.get(Customer.class, 1l);
System.out.println(customer.getCust_id());

Customer customer2 = session.load(Customer.class, 1l);
System.out.println(customer2.getCust_id());

transaction.commit();
session.close();
}

get方法和load方法的不同(重要)


get方法
 采用的是立即加载,执行到这行代码的时候,就会立即发送SQL语句去查询
 查询后返回的是真是对象本身
 查询一个找不到的对象时返回的是null
load方法
 采用的是延时加载(lazy加载),执行到这行代码的时候不会立即发送SQL语句,当真正使用这个对象的时候才会发送SQL语句去查询
 查询后返回的是代理对象;利用javassist技术产生的代理。
 查询一个找不到的对象的时候,返回ObjectNotFoundException


  • 修改方法
    void update(Object obj);
public void test3() {
Session session = HibernateUtils.getSession();
Transaction transaction = session.beginTransaction();

/*//直接创建对象进行修改(这种方法会将下面代码没有设置值的置为空)
Customer customer = new Customer();
customer.setCust_id(1l);
customer.setCust_name("pig");
session.update(customer);*/

//先查询再修改(推荐)
Customer customer = session.get(Customer.class, 1l);
customer.setCust_name("pig");
session.update(customer);

transaction.commit();
session.close();
}
  • 删除方法
    void delete(Object obj);
public void test4() {
Session session = HibernateUtils.getSession();
Transaction transaction = session.beginTransaction();

/*//直接创建对象删除
Customer customer = new Customer();
customer.setCust_id(1l);
session.delete(customer);*/

//先查询再删除(推荐),----可以进行级联删除
Customer customer = session.get(Customer.class, 2l);
session.delete(customer);

transaction.commit();
session.close();
}
  • 保存或更新
    void saveOrUpdate(Object obj)
    如果数据库中有,则执行更新如果没有则插入
public void test5() {
Session session = HibernateUtils.getSession();
Transaction transaction = session.beginTransaction();

/*Customer customer = new Customer();
customer.setCust_name("dog");
session.saveOrUpdate(customer);*/

Customer customer2 = new Customer();
customer2.setCust_id(3l);
customer2.setCust_name("5656556");
session.saveOrUpdate(customer2);

transaction.commit();
session.close();
}
  • 查询所有
public void test6() {
Session session = HibernateUtils.getSession();
Transaction transaction = session.beginTransaction();
//以后会讲到以下方法的具体使用
Query query = session.createQuery("from Customer"); //此处参数不是sql,是hql(面向对象的查询语言)
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}

//使用SQL语句查询所有
SQLQuery query2 = session.createSQLQuery("select * from cst_customer");
List<Object[]> list2 = query2.list();
for (Object[] objects : list2) {
System.out.println(Arrays.toString(objects));
}


transaction.commit();
session.close();
}

4-4 Transaction对象(事务对象)

Hibernate中管理事务的对象。


  • commit();
  • rollback();

二、主键生成策略&一级缓存&事务管理

 1、持久化类的编写规则

  1-1 什么是持久化类?

持久化:将内存中的一个对象持久化到数据库中的过程。Hibernate框架就是用来进行持久化的框架。

持久化类:一个java对象与数据库表建立了映射关系,那么这个类在hibernate中就成为持久化类

  • 可以理解为 持久化类 = java类 + 映射文件

  1-2 持久化类的编写规则


  • 对持久化类编写一个无参的构造方法 (因为hibernate底层要使用反射生成实例)
  • 属性需要私有,对外提供set和get方法(hibernate设置对象的值)
  • 对持久化类提供一个唯一标识OID与数据库主键对应(确定是否是同一对象)
  • 持久化类中的属性尽量使用包装类类型(如果使用基本数据类型,其默认值是0,会有很多的歧义)
  • 持久化类不要使用final修饰


延迟加载本身是hibernate的一个优化手段,返回的是一个代理对象(javassist,可以对没有实现接口的类产生一个代理-----使用了非常底层的字节码增强技术来继承这个类)。如果持久化类用final修饰,则不能被继承,不能产生代理对象,延时加载的技术也失效了。get方法和load方法就一致了。


2、主键生成策略

 2-1 主键的分类


  • 自然主键
    主键的本身就是表中的一个字段(实体中一个具体的属性)
     比如创建一个学生表,学生表里的省份证号为主键就是自然主键
  • 代理主键
    主键的本身不是表中必须的一个字段,比如id
    在实际开发中,尽量使用代理主键

 2-2 Hibernate的主键生成策略

在实际开发中一般不允许用户手动设置主键,一般将主键交给数据库,手动编写程序进行设置。在Hibernate中,提供了很多种的主键生成策略。


  • increment
     Hibernate提供的自动增长机制,适用于主键为int、short、long类型。在单线程中使用。
     底层原理:底层发送select max(id)from 表名;id+1为当前主键。
  • identity
     适用于short、int、long类型,采用数据库底层的自动增长技术。适用于自动增长的数据库(mysql、MSSQL)但是oracle不支持自动增长。
  • sequence
     适用于int、short、long类型的主键。采用的是序列的方式(oracle支持)mysql就不支持了
  • uuid
     适用于字符串类型的主键。使用Hibernate中的随机方式生成字符串。
  • native
     本地策略,可以在identity和sequence之间来回切换。
  • assigned
     Hibernate放弃外键的管理,需要编写程序设置,或者用户自己设置。

3、持久化类的三种状态(了解)


hibernate是持久化框架,通过持久化类完成ORM操作。Hibernate为了更好的管理持久化类,将持久化类分成三种状态。



  • 瞬时态
    这种对象没有唯一的标识OID,被session管理,称为瞬时态对象。
    获得瞬时态对象
      通过创建javabean的对象获得,例如:Customer customer = new Customer();
    状态转换
      瞬时→持久 save(Object obj)、saveOrUpdate(Object obj)
      瞬时→托管 customer.setId(1)
  • 持久态
    这种对象有唯一标识OID,被session管理,称为持久态对象。
    获得持久态对象
      使用get()、load()方法
    状态转换
      持久→瞬时 delete()
      持久→托管 close()、clear()
    持久态对象可以自动更新数据库
  • 托管态
    种对象有唯一标识OID,没有被session管理,称为托管态
    状态转换
      托管→持久 update()、savaOrUpdate()
      托管→瞬时 customer.setId(null)

如何区分三种状态的对象

public void test() {
Session session = HibernateUtils.getSession();
Transaction transaction = session.beginTransaction();

Customer customer = new Customer(); //瞬时态对象
customer.setCust_name("adecf");
session.save(customer); //持久态对象

transaction.commit();
session.close();
System.out.println(customer.getCust_id()); //托管态对象
}

4、Hibernate一级缓存

 4-1缓存概述

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

 4-2 Hibernate的一级缓存

Hibernate的一级缓存被称为是session级别的缓存,一级缓存的生命周期与session的生命周期一致。

一级缓存是自带的,不可卸载。

  • 证明一级缓存的存在
public void test1(){
Session session = HibernateUtils.getSession();
Transaction transaction = session.beginTransaction();

Customer customer = session.get(Customer.class, 2l); //发送sql语句

Customer customer2 = session.get(Customer.class, 2l); //不发送sql语句,在缓存中查找

System.out.println(customer==customer2);//返回结果为true
transaction.commit();
session.close();
}

 4-3 一级缓存的原理

SSH之Hibernate(一)_hibernate_10

5、 Hibernate的事务管理

 5-1 Hibernate中设置事务的管理级别

  • 设置事务的级别

Read uncommitted

以上读问题全部解决

Read committed

解决脏读,但是不可重复读和虚读有可能发生

Repeatable read

解决脏读和不可重复读,但是虚读有可能发生

serializable

解决所有的读问题

SSH之Hibernate(一)_持久化_11

在hibernate.cfg.xml中插入如下代码即可:

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

 5-2 Hibernate解决Service的事务管理

  5-2-1 为什么要解决service事务管理

SSH之Hibernate(一)_hibernate_12


如上图所示,事务要加到service层。因为service要进行很多的操作才能算是完成一次事务。比如说注册,首先要查询数据库中有没有重名的,然后才是往数据库中增加数据。
在前面的学习中,使用JDBC保证是同一个连接对象采用的是向下传递,也就是在service里面新建collection连接对象,在调用dao中的方法时作为参数传递过去。还有一种方式是采用ThreadLocal绑定到本地线程中
Hibernate内部已经提供了绑定好的ThreadLocal
 在sessionFactory中提供了提供了一个方法getCurrentSession();
 此方法需要完成配置


  5-2-2 如何使用getCurrentSession();方法

  1. 修改配置文件,开启getCurrentSession();方法
#在hibernate.cfg.xml文件下插入如下代码
<property name="hibernate.current_session_context_class">thread</property>
  1. 编写工具类
public class HibernateUtils {
public static final Configuration cfg;
public static final SessionFactory sf;
static {
cfg = new Configuration().configure();
sf = cfg.buildSessionFactory();
}
public static Session getSession() {
return sf.openSession();
}
public static Session getCurrentSession() {
return sf.getCurrentSession();
}
  1. 测试是否成功
@Test
public void test() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
Customer customer = new Customer();
customer.setCust_name("588666");
session.save(customer);
transaction.commit();
}


注意:不要手动关闭session了,因为线程接结束后session会自动关闭


6、Hibernate的其它API

 6-1 Query

  • Query接口用于接收HQL,查询多个对象。
public void test() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
String hql = "from Customer"; //查询所有的记录
// String hql = "from Customer where cust_name like ?"; //条件查询
Query query = session.createQuery(hql);
// query.setParameter(0, "林%"); //设置条件
query.setFirstResult(1); //设置分页从哪开始
query.setMaxResults(2); //设置分页每页显示多少
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
}

 6-2 Criteria


  • 是一种条件查询
  • 更加面向对象的查询方式

//查询所有
public void test2() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
Criteria criteria = session.createCriteria(Customer.class);
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
}
//条件查询
public void test2() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
Criteria criteria = session.createCriteria(Customer.class);
// criteria.add(Restrictions.ilike("cust_name", "林%"));
criteria.add(Restrictions.ilike("cust_name", "得",MatchMode.ANYWHERE));
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
}
//分页查询与上表一样

 6-3 SQLQuery(了解)

  • SQL用于接收sql。语句特别复杂的情况下使用此方式。