Hibernate框架——基本操作

在Java Web的后台开发中,数据持久化操作是非常重要的一步,数据持久化,简单来说是将程序的数据保存在数据库中,将数据写入磁盘。JDBC是一种持久化机制,是我们最经常使用的,但是,有许多开源的框架有更好的操作性,例如Hibernate.

JDBC的优点和缺点

优点

直接底层操作,提供了很简单、便捷的访问数据库的方法,跨平台性比较强。灵活性比较强,可以写很复杂的SQL语句。

缺点

因为java是面向对象的,JDBC没有做到使数据能够面向对象的编程,使程序员的思考任然停留在SQL语句上。
操作比较繁琐,很多代码需要重写很多次。
如果遇到批量操作,频繁与数据库进行交互,容易造成效率的下降。

JDBC的程序操作可以封装一些什么内容?又不可以封装哪些内容?

Hibernate

为什么要使用Hibernate

  • Hibernate实现了面向对象的数据库编程
  • Hibernate比起JDBC来,在代码的书写上比较简单化了。
  • Hibernate提出了缓存机制,这样可以使访问数据库的效率提高很大。

Hibernate的缺点

  • 该框架程序员是没有办法干预SQL语句的生成
  • 如果一个项目中,对SQL语句的优化的要求比较高,这个时候不能用Hibernate来做
  • 表之间的关系很复杂的情况下,不能用Hibernate来做
  • 如果一张表的数据超过了千万级别,也是不适合用hibernate来做

Hibernate的组成

持久化类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Person implements Serializable{
private Long pid;
private String name;
private String description;
public Long getPid() {
return pid;
}
public void setPid(Long pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

说明:持久化类中必须有一个默认的构造器,因为hibernate加载类是通过java的反射机制利用,默认的构造器创建持久化对象的。

映射文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 描述一个持久化类
name 属性为持久化类的全名
table 该持久化类对应的表名 默认情况下为类名
catalog 为数据库的名称
-->
<class name="com.wcc.hibernate.bean.Person" table="person">
<!--
id 对应表中的主键
name 为持久化类中的属性的名称
length 为对应数据库表中相应的字段的长度
column 属性的名称对应的表的字段名称 , 不写 则默认是和属性的名称保持一致
-->
<id name = "pid" length = "5" type= "java.lang.Long" column = "pid">
<!-- 主键的生成器 -->
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="java.lang.String" length="20"></property>
<property name="description" column="name" type="java.lang.String" length="50"></property>
</class></hibernate-mapping>

从映射文件中可以看出,该映射文件完成了从类到表、类中的属性到表中的字段的对应关系。

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?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">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<!-- 一个session-factory 代表一个数据库 -->
<session-factory>
<!-- 连接数据的用户名 -->
<property name="connection.username">root</property>
<!-- 连接数据库密码 -->
<property name="connection.password">wcc</property>
<!-- 连接数据库的驱动 -->
<property name="connection.driver_class">com.mysql.jdbc.Drvier</property>
<!-- 连接数据的url -->
<property name="connection.url">jdbc:mysql://localhost:3306/(你的数据库的名称)</property> // 你的数据库地址,这里是在本地做测试,所以使用的是localhost
<!-- 方言 : 告诉hibernate用什么样的数据库 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!--
validate 加载hibernate时,验证数据库的结构
update 加载hibernate时,检查数据库,如果表不存在,则创建,如果存在,则更新
create 每次加载hiberante,都会创建表
create-drop 每次加载hiberante,创建,卸载hiberante时,销毁
-->
<property name="hbm2ddl.auto">update</property>
<!-- 显示SQL语句 -->
<property name="show_sql">true</property>
<!-- 格式化SQL语句 -->
<property name="format_sql">true</property>
<!-- 加载映射文件 -->
<mapping resource="com/wcc/hibernate/bean/Person.hbm.xml"></mapping>
</session-factory>
</hibernate-configuration>
  • Hibernate.connection.url: 表示要链接的数据库地址
  • Hibernate.connection.driver_class : 表示要链接的数据库的驱动类
  • Hibernate.connection.username: 要连接的数据库的用户名
  • Hibernate.connection.password: 要连接的数据库的密码
  • Hibernate.dialect: 表示要使用的数据库的类型
    • org.hibernate.dialect.MySQL5Dialect mysql数据库
    • org.hibernate.dialect.Oracle9Dialect oracle数据库
    • org.hibernate.dialect.SQLServerDialect SQLServer数据库
  • hibernate.hbm2ddl.auto:
    • validate:加载hibernate时验证创建表结构
    • pdate:加载hibernate时自动更新数据库结构,如果表存在不用创建,如果不存在就创建。
    • create:每一次加载hibernate时都创建表结构
    • create-drop:加载hibernate时创建,退出时删除

使用Hibernate执行CRUD操作

增加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
public void savePerson(){
//创建配置文件对象
Configuration configuration = new Configuration();
// 加载配置文件 使用不带参数的方法 那么配置文件 应该按照如下的要求:
// 1、 该配置文件必须放在根目录下, 2、名称必须是hibernate.cfg.xml
configuration.configure();
// 还有其他的方法 configure(String path); 里面传入的是文件的路径或者是URL 以获得配置文件
//创建sessionFactory
SessionFactory sessionFactory = configuration.buildSessionFactory();
//打开一个Session 这个Session和javaWeb 和邮件的发送的session是不同的
Session session = sessionFactory.openSession();
//创建一个事务 开始事务
Transaction transaction = session.beginTransaction();
Person person = new Person();
person.setName("王小末");
person.setDescription("女神呀");
//利用Session将对象保存好
session.save(person);
// 提交事务
transaction.commit();
// 关闭Session
session.close();
}

加载Hibernate的配置文件的有多重方法,一般使用Configuration.configure()方法加载配置文件的时候,配置文件是要在类的根目录中为什么呢?查看Hibernate的源码可以知道,使用该Configuration.configure()构造方法的时候是从根目录下加载该文件的,如下图:
Configuration.configure源码

因为每次操作都是要进行配置文件的加载,和SessionFactory的创建,所以直接写一个Utils来方便使用:

1
2
3
4
5
6
7
8
9
10
public class HibernateUtils {
public static SessionFactory sessionFactory;
static{
Configuration configuration = new Configuration();
configuration.configure();
sessionFactory = configuration.buildSessionFactory();
}
}

使用上面的类,简化代码,进行更新,查找,删除的操作。

更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void updatePerson(){
//利用我们自己写的工具类,将Session拿到
Session session = HibernateUtils.sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
/**
* 根据主键查询数据库表中的一行记录
* 第一个参数为持久化类的class形式
* 第二个对象为对象 的序列化, 能接受基本数据类型
*/
Person person = (Person) session.get(Person.class, 2L);
person.setDescription("真美呀呀");
// 通过对象来更新数据
session.update(person);
// 事务的提交
transaction.commit();
session.close();
}

查找

1
2
3
4
5
6
7
@Test
public void testQueryPerson(){
Session session = HibernateUtils.sessionFactory.openSession();
// from 后面跟的是持久化类名, 不是表的名字
List<Person> peroList = session.createQuery("from Person").list();
System.out.println("从数据库中查询的记录条数为:" + peroList.size());
}

删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testDeletePerson(){
/**
* 从数据库中将pid为2的数据提取出来
*/
Session session = HibernateUtils.sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//把 pid为1 的持久化对象提取出来
Person person = (Person) session.get(Person.class, 2L);
// 利用Session 删除对象
session.delete(person);
transaction.commit();
session.close();
System.out.println("数据库中删除记录成功!");
}

Hibernate的流程

Hibernate执行流程图

说明:
1、CRUD操作是由session来完成的。
2、在Hibernate中事务不是自动提交的。

简单例子的详细解析

Configuration类

利用该类加载了Hibernate的配置文件

sessionFactory类

  • Hibernate配置文件的信息、持久化类的信息、映射文件的信息全部在该类中。
  • sessionFactory对象有且只有一个
  • sessionFactory的生命周期是整个Hibernate实例
  • sessionFactory本身就是线程安全的
  • 二级缓存在sessionFactory中存放。
  • sessionFactory和数据库的连接没有直接的关系

session类

  • CRUD操作由session来完成
  • 一个session代表数据库的一个连接

session 内部执行流程

session执行流程

Hibernate中的类型

在Hibernate中有两种类型: java.type , hibernate type

Java type

Hibernate 内部之间提供了Java类型到数据库的对照表,如下图的官方文档中的基本数据类型截图:

Hibernate中的JavaType

说明: 用Java类型可以直接完成从Java类型到数据库类型的映射

Hibernate type

从上面可以看出,如果选择hibernate类型,需要查找该hibernate类型对象的Java类型,从而再找到数据库类型,所以Java类型效率是比较高。

主键的产生器

Increment

主键自动增长,但是不同于Identity,Increment是和数据中的主键自动增长是一样的,那么Hibernate是怎么实现的呢? 当我们向数据库中执行增加一条记录的时候,Hibernate是怎么执行?请看下面的图:

使用increment的SQL输出

从Hibernate的查SQL语句的输出可以看出,在Increment设置下,执行SQL语句的时候是:先查找主键的最大值,再在最大值的基础上加1, 所以从这可以看出,使用Increment的效率比较低。

Assigned

需要由程序员手动赋值

1
<generator class="assigned"></generator>

那么我们在编程的时候是要对主键直接进行过设置,例如:

1
person.setPid(2L);

Identity

支持主键的自动增长
将这个进行配置之后,例如: 插入了3条记录,那么数据库中主键是 1, 2,3, 当删除主键为2的记录之后,再进行插入一条数据,就会主键值为4,数据库中记录主键值为:1,3,4 删除主键为3的记录,再插入一条记录,那么主键值为:1,4,5, 这样在数据库中并不是那么直观的看出所有的记录数。

UUID

1
<generator class="uuid"></generator>

持久化类中的属性必须是String类型

Native

Hibernate会根据数据库的不同,选择合适的主键的生成策略

Sequence

是oracle内部特有的内容,相当于uuid,所以是字符串类型。

坚持原创技术分享,您的支持将鼓励我继续创作!