Hibrenate概述
Hibernate框架是当今主流的Java持久层框架之一,由于它具有简单易学、灵活性强、扩展性强等特点,能够大大地简化程序的代码量,提高工作效率,因此受到广大开发人员的喜爱。
Hibernate是一个开放源代码的ORM框架,它对JDBC进行了轻量级的对象封装,使得Java开发人员可以使用面向对象的编程思想来操作数据库。
ORM概述
Object Relation Mapping 对象关系映射。
对象-关系映射(OBJECT/RELATIONALMAPPING,简称ORM),是随着面向对象的软件开发方法发展而产生的。用来把对象模型表示的对象映射到基于S Q L 的关系模型数据库结构中去。这样,我们在具体的操作实体对象的时候,就不需要再去和复杂的 SQ L 语句打交道,只需简单的操作实体对象的属性和方法 。O R M 技术是在对象和关系之间提供了一条桥梁,前台的对象型数据和数据库中的关系型的数据通过这个桥梁来相互转化 。
简单的说就是把我们程序中的实体类和数据库表建立起来对应关系。
为什么要学习Hibernate
使用传统的JDBC开发应用系统时,如果是小型应用系统,并不觉得有什么麻烦,但是对于大型应用系统的开发,使用JDBC就会显得力不从心。例如对几十、几百张包含几十个字段的表进行插入操作时,编写的SQL语句不但很长,而且繁琐,容易出错;在读取数据时,需要写多条getXxx语句从结果集中取出各个字段的信息,不但枯燥重复,并且工作量非常大。为了提高数据访问层的编程效率,Gavin King开发出了一个当今最流行的的ORM框架,它就是Hibernate框架。
所谓的ORM就是利用描述对象和数据库表之间映射的元数据,自动把Java应用程序中的对象,持久化到关系型数据库的表中。通过操作Java对象,就可以完成对数据库表的操作。可以把ORM理解为关系型数据和对象的一个纽带,开发人员只需要关注纽带一端映射的对象即可。ORM原理如图。
ORM原理
与其它操作数据库的技术相比,Hibernate具有以下几点优势:
- Hibernate对JDBC访问数据库的代码做了轻量级封装,大大简化了数据访问层繁琐的重复性代码,并且减少了内存消耗,加快了运行效率。
- Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现,它很大程度的简化了DAO(Data Access Object,数据访问对象)层编码工作。
- Hibernate的性能非常好,映射的灵活性很出色。它支持很多关系型数据库,从一对一到多对多的各种复杂关系。
- 可扩展性强,由于源代码的开源以及API的开放,当本身功能不够用时,可以自行编码进行扩展。
明确:
操作实体类就相当于操作数据库表
hibernate入门案例
开发包和版本介绍
下载网址:http://sourceforge.net/projects/hibernate/files/hibernate-orm/5.0.7.Final/
开发包目录,如下图所示:
从图可以看出,hibernate5.0.7的解压s目录中包含一系列的子目录,这些子目录分别用于存放不同功能的文件,接下来针对这些子目录进行简单介绍,具体如下:
documentation文件夹:存放Hibernate的相关文档,包括参考文档的API文档。
lib文件夹:存放Hibernate编译和运行所依赖的JAR包。其中required子目录下包含了运行Hibernate5项目必须的JAR包。
project文件夹:存放Hibernate各种相关的源代码。
搭建hibernate开发环境
第一步:拷贝必备的jar包到开发目录
数据库驱动包,如下图:
Hibernate/lib/required/*.jar 如下图
日志记录的包,如下图
第二步:创建数据库和实体类
持久化类是应用程序中的业务实体类,这里的持久化是指类的对象能够被持久化保存到数据库中。Hibernate使用普通Java对象(Plain Old Java Object),即POJO的编程模式来进行持久化。POJO类中包含的是与数据库表相对应的各个属性,这些属性通过getter和setter方法来访问,对外部隐藏了内部的实现细节。下面就来编写Customer持久化类。
在项目src目录下,创建cn.itcast.domain包,并在包中创建实体类Customer(对应数据库表cst_customer),Customer类包含与cst_customer数据表字段对应的属性,以及相应的getXxx ()和setXxx ()方法。
/**
* 客户的实体类
*/
public class Customer implements Serializable {
private Long custId;
private String custName;
private String custSource;
private String custIndustry;
private String custLevel;
private String custAddress;
private String custPhone;
public Long getCustId() {
return custId;
}
public void setCustId(Long custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
@Override
public String toString() {
return "Customer [custId=" + custId + ", custName=" + custName + ", custSource=" + custSource
+ ", custIndustry=" + custIndustry + ", custLevel=" + custLevel + ", custAddress=" + custAddress
+ ", custPhone=" + custPhone + "]";
}
}
第三步:编写映射配置文件(xml)
实体类Customer目前还不具备持久化操作的能力,而Hibernate需要知道实体类Customer映射到数据库Hibernate中的哪个表,以及类中的哪个属性对应数据库表中的哪个字段,这些都需要在映射文件中配置。
在实体类Customer所在的包中,创建一个名称为Customer.hbm.xml的映射文件,在该文件中定义了实体类Customer的属性是如何映射到cst_customer表的列上的。
<?xml version="1.0" encoding="UTF-8"?>
<!-- 导入约束:dtd约束
位置:在Hibernate的核心jar包中名称为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 package="com.itheima.domain"><!-- package属性用于设定包的名称,接下来该配置文件中凡是用到此包中的对象时都可以省略包名 -->
<!-- class标签
作用:建立实体类和表的对应关系
属性:
name:指定实体类的名称
table:指定数据库表的名称
-->
<class name="Customer" table="cst_customer">
<!-- id标签
作用:用于映射主键
属性:
name:指定的是属性名称。也就是get/set方法后面的部分,并且首字母要转小写。
column:指定的是数据库表的字段名称
-->
<id name="custId" column="cust_id">
<!-- generator标签:
作用:配置主键的生成策略。
属性:
class:指定生成方式的取值。
取值之一:native。使用本地数据库的自动增长能力。
mysql数据库的自动增长能力是让某一列自动+1。但是不是所有数据库都支持这种方式。
-->
<generator class="native"></generator>
</id>
<!-- property标签:
作用:映射其他字段
属性:
name:指定属性的名称。和id标签的name属性含义一致
column:指定数据库表的字段名称
-->
<property name="custName" column="cust_name"></property>
<property name="custLevel" column="cust_level"></property>
<property name="custSource" column="cust_source"></property>
<property name="custIndustry" column="cust_industry"></property>
<property name="custAddress" column="cust_address"></property>
<property name="custPhone" column="cust_phone"></property>
</class>
</hibernate-mapping>
第四步:编写主配置文件(hibernate.cfg.xml)
Hibernate的映射文件反映了持久化类和数据库表的映射信息,而Hibernate的配置文件则主要用来配置数据库连接以及Hibernate运行时所需要的各个属性的值。在项目的src下创建一个名称为hibernate.cfg.xml的文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- 导入dtd约束:
位置:在核心jar包中的名称为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>
<!-- 配置SessionFactory
SessionFactory就是一个工厂,用于生产Session对象的。
Session就是我们使用hibernate操作数据库的核心对象了。
明确:
它和我们Web阶段的HttpSession没一点关系。
此配置文件中的内容不需要背,很多配置都是可以在开发包中找到的。
但是要求必须知道:
创建SessionFactory由三部分组成,缺一不可。要知道是哪三部分
1、连接数据库的基本信息
2、hibernate的基本配置
3、映射文件的位置
找配置文件的key都是在hibernate的开发包中project文件夹下的etc目录中的hibernate.properties
-->
<session-factory>
<!-- 1、连接数据库的基本信息 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/day36_ee48_hibernate</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">1234</property>
<!-- 2、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>
<!-- 是否让hibernate根据表结构的变化来生成DDL语句
DDL:数据定义语言
hibernate可以根据映射文件来为我们生成数据库的表结构。
但是他不能生成数据库。
hbm2ddl.auto的取值
* none:不用Hibernate自动生成表.
* create:每次都会创建一个新的表.(测试)
* create-drop:每次都会创建一个新的表,执行程序结束后删除这个表.(测试)
* update:如果数据库中有表,使用原来的表,如果没有表,创建一个新表.可以更新表结构。
* validate:只会使用原有的表.对映射关系进行校验.
-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 3、映射文件的位置 -->
<mapping resource="com/itheima/domain/Customer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
实现保存操作:
在项目中新建一个名称为cn.itcast.test的包,然后在包中建立一个名为HibernateDemo1.java的文件,该文件是用来测试的类文件。
/**
* hibernate的入门案例:
* 需求:把一个客户保存到数据库中
*/
public class HibernateDemo1 {
/**
* 步骤分析:
* 1、加载主配置文件
* 2、根据主配置文件中的配置构建SessionFactory
* 3、使用工厂生产一个Session对象
* 4、使用Session对象开启事务
* 5、执行保存客户操作
* 6、提交事务
* 7、释放资源
*/
@Test
public void test1(){
Customer c = new Customer();
c.setCustName("黑马程序员1");
//1.加载主配置文件
Configuration cfg = new Configuration();
cfg.configure();
//2.构建SessionFactory
SessionFactory factory = cfg.buildSessionFactory();
//3.使用SessionFactory生产一个Session
Session session = factory.openSession();//打开一个新的Session
//4.开启事务
Transaction tx = session.beginTransaction();
//5.保存客户
session.save(c);//根据映射配置文件,生成SQL语句,实现保存。
//6.提交事务
tx.commit();
//7.释放资源
session.close();
factory.close();
}
}
入门案例的执行过程
首先创建Configuration类的实例,并通过它来读取并解析配置文件hibernate.cfg.xml。然后创建SessionFactory读取解析映射文件信息,并将Configuration对象中的所有配置信息拷贝到SessionFactory内存中。接下来,打开Session,让SessionFactory提供连接,并开启一个事务,之后创建对象,向对象中添加数据,通过session.save()方法完成向数据库中保存数据的操作。最后提交事务,并关闭资源。
映射文件的配置:
该文件用于向Hibernate提供持久化类到关系型数据库的映射,每个映射文件的的结构基本都是相同的,其普遍的代码形式如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<!—映射文件的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代表的是实体类名 talbe代表的是表名 -->
<class name="XXX" table="xxx">
<!-- name=id 代表的是XXX类中属性 column=id 代表的是xxx表中的字段 -->
<id name="id" column="id">
<generator class="native"/><!-- 主键生成策略 -->
</id>
<!-- 其它属性使用property标签来映射 -->
<property name="XXX" column="xxx" type="string" />
</class>
</hibernate-mapping>
映射文件通常是一个XML文件即可,但一般命名为类名.hbm.xml
核心配置
Hibernate的配置文件,包含了连接持久层与映射文件所需的基本信息,其配置文件有两种格式,具体如下:
- 一种是properties属性文件格式的配置文件,它使用键值对的形式存放信息,默认文件名称为hibernate.properties;
- 另一种是XML格式的配置文件,XML配置文件的默认名称为hibernate.cfg.xml。
上述两种格式的配置文件是等价的,具体使用哪个可以自由选择。XML格式的配置文件更易于修改,配置能力更强,当改变底层应用配置时不需要改变和重新编译代码,只修改配置文件的相应属性即可,而properties格式的文件则不具有此优势,因此,在实际开发项目中,大多数情况会使用XML格式的配置文件。下面将对XML格式的配置文件进行详细介绍。
hibernate.cfg.xml配置文件一般在开发时会放置在src的源文件夹下,发布后,该文件会在项目的WEB-INF/classes路径下。配置文件的常用配置信息如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<!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>
<!-- 必要的配置信息:连接数据库的基本参数 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate_day01</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">1234</property>
<!-- Hibernate的属性 -->
<!-- Hibernate的方言:作用,根据配置的方言生成相应的SQL语句 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- Hibernate显示SQL语句: -->
<property name="hibernate.show_sql">true</property>
<!-- Hibernate格式化SQL语句: -->
<property name="hibernate.format_sql">true</property>
<!-- Hibernate的hbm2ddl(数据定义语言:create drop alter ...)属性 -->
<!--
hbm2ddl.auto的取值
* none :不用Hibernate自动生成表.
* create :每次都会创建一个新的表.(测试)
* create-drop :每次都会创建一个新的表,执行程序结束后删除这个表.(测试)
* update :如果数据库中有表,使用原来的表,如果没有表,创建一个新表.可以更新表结构。
* validate :只会使用原有的表.对映射关系进行校验.
-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- Hibernate加载映射 -->
<mapping resource="cn/itcast/domain/Customer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
在上述代码中,首先进行了xml声明,然后是配置文件的dtd信息,该信息同样可以在核心包hibernate-core-5.0.7.Final.jar下的org.hibernate包中的hibernate-configuration-3.0.dtd文件中找到,读者只需要复制过来用即可,不需要刻意记忆。
Hibernate配置文件的根元素是hibernate-configuration,该元素包含子元素session-factory,在session-factory元素中又包含多个property元素,这些property元素用来对Hibernate连接数据库的一些重要信息进行配置。例如,上面的配置文件中,使用了property元素配置了数据库的方言、驱动、URL、用户名、密码等信息。最后通过mapping元素的配置,加载出映射文件的信息。
Hibernate配置文件的一些常用属性名称及用途,如表所示。
- Hibernate常用配置属性
名称 | 用途 |
hibernate.dialect | 操作数据库方言 |
hibernate.connection.driver_class | 连接数据库驱动程序 |
hibernate.connection.url | 连接数据库URL |
hibernate.connection.username | 数据库用户名 |
hibernate.connection.password | 数据库密码 |
hibernate.show_sql | 在控制台上输出SQL语句 |
hibernate.format_sql | 格式化控制台输出的SQL语句 |
hibernate.hbm2ddl.auto | 当SessionFactory创建时是否根据映射文件自动验证表结构或自动创建、自动更新数据库表结构。该参数的取值为:validate、update、create和create-drop。 |
hibernate.connection.autocommit | 事务是否自动提交 |
Hibernate中API介绍
Configuration对象
作用:
在使用Hibernate时,首先要创建Configuration实例,Configuration实例主要用于启动、加载、管理hibernate的配置文件信息。在启动Hibernate的过程中,Configuration实例首先确定Hibernate配置文件的位置,然后读取相关配置,最后创建一个唯一的SessionFactory实例。Configuration对象只存在于系统的初始化阶段,它将SessionFactory创建完成后,就完成了自己的使命。
Hibernate通常使用Configuration config = new Configuration().configure();的方式创建实例,此种方式默认会去src下读取 hibernate.cfg.xml配置文件。如果不想使用默认的hibernate.cfg.xml配置文件,而是使用指定目录下(或自定义)的配置文件,则需要向configure()方法中传递一个文件路径的参数,其代码写法如下:
Configuration config = new Configuration().configure("xml文件位置");
此种写法hibernate会去指定位置查找配置文件,例如,想要使用src下config包中的 hibernate.cfg.xml文件,只需将文件位置加入configure()中即可,其代码如下所示:
Configuration config = new Configuration().configure("/config/hibernate.cfg.xml");
【加载映射文件】
Hibernate除了可以使用Configuration对象加载核心配置文件以外,还可以利用该对象加载映射文件。因为如何使用properties文件作为Hibernate的核心配置文件,其他的属性可以使用key=value的格式来设置,但是映射没有办法加载。这时这个对象就有了用武之地。可以在手动编写代码的时候去加载映射文件。
Configuration configuration = new Configuration().configure("xml文件位置");
configuration.addResource("cn/itcast/domain/Customer.hbm.xml");
常用方法:
默认构造函数:
它只能加载类的根路径下,名称为hibernate.properties的配置文件。不能加载xml
configure():
它用于加载类的根路径下,名称为hibernate.cfg.xml的配置文件。
buildSessionFactory():
根据配置文件,构建SessionFactory
addResource(String url);
指定映射文件的位置
addClass(Class clazz);
指定实体类的字节码
SessionFactory
作用:
SessionFactory接口负责Hibernate的初始化和建立Session对象。它在Hibernate中起到一个缓冲区作用,Hibernate可以将自动生成的SQL语句、映射数据以及某些可重复利用的的数据放在这个缓冲区中。同时它还保存了对数据库配置的所有映射关系,维护了当前的二级缓存。
SessionFactory 实例是通过Configuration对象获取的,其获取方法如下所示。
SessionFactory sessionFactory = config.buildSessionFactory();
使用原则:
由于SessionFactory维护了很多信息同时又是线程安全的,一般情况下,一个项目中只需要一个SessionFactory,只有当应用中存在多个数据源时,才为每个数据源建立一个SessionFactory实例。因此,不应该反复的创建和销毁。
原则:一个应用应该只有一个SessionFactory。在应用加载时创建,应用卸载时销毁。
在hibernate中使用数据源(连接池)
SessionFactory内部还维护了一个连接池,如果我们需要使用第三方的连接池如C3P0,那么需要我们自己手动进行配置
配置C3P0步骤如下:
1、导入连接池的jar包
2、在hibernate主配置文件中配置
<!-- 配置数据源的提供商 -->
<property name="hibernate.connection.provider_class">
org.hibernate.connection.C3P0ConnectionProvider
</property>
Session
作用:
Session 是应用程序与数据库之间交互操作的一个单线程对象,是 Hibernate 运作的中心,它的主要功能是为持久化对象提供创建、读取和删除的能力,所有持久化对象必须在session的管理下才可以进行持久化操作。
创建SessionFactory实例后,就可以通过它获取Session实例。获取Session实例有两种方式,一种是通过openSession()方法,另一种是通过getCurrentSession()方法。两种方法获取session的代码如下所示:
//采用openSession方法创建session
Session session = sessionFactory.openSession();
//采用getCurrentSession()方法创建session
Session session = sessionFactory.getCurrentSession();
以上两种获取session实例方式的主要区别是,采用openSession方法获取Session实例时,SessionFactory直接创建一个新的Session实例,并且在使用完成后需要调用close方法进行手动关闭。而getCurrentSession方法创建的Session实例会被绑定到当前线程中,它在提交或回滚操作时会自动关闭。
在没有配置把Session绑定当前线程之前,getCurrentSession方法无法使用.
常用方法:
save(Object entity); :保存一个实体到数据库
update(Object entity);:更新一个实体
delete(Object entity);:删除一个实体
get(Class clazz,Serializable id);:根据id查询一个实体。参数的含义:Class表示要查询的实体类字节码。Serializable就是查询的条件。
beginTransaction();:开启事务,并返回事务对象
使用原则:
每个线程都只有一个Session对象
Transaction
作用:
Transaction接口主要用于管理事务,它是Hibernate的数据库事务接口,且对底层的事务接口进行了封装。Transaction接口的事务对象是通过Session对象开启的,其开启方式如下所示。
Transaction transaction = session.beginTransaction();
常用方法:
commit():提交事务
rollback():回滚事务
Session执行完数据库操作后,要使用Transaction接口的commit()方法进行事务提交,才能真正的将数据操作同步到数据库中。发生异常时,需要使用rollback()方法进行事务回滚,以避免数据发生错误。因此,在持久化操作后,必须调用Transaction接口的commit()方法和rollback()方法。如果没有开启事务,那么每个Session的操作,都相当于一个独立的操作。
get和load的区别
区别:
1、查询的时机不一样
get方法任何时候都是立即加载,即只要一调用get马上发起数据库查询
load方法默认情况下是延迟加载,即真正用到对象的非OID字段数据才发起查询
load方法可以通过配置的方式改为立即加载。
配置的方式:
由于load方法是hibernate的方法所以只有XML的方式:
<class name="Customer" table="cst_customer" lazy="false">
2、返回的结果不一样
get方法永远返回查询的实体类对象。
load方法当是延迟加载时,返回的是实体类的代理对象。
涉及的概念:
立即加载:
是不管用不用马上查询。
延迟加载:
等到用的时候才真正发起查询。