Hibernate
轻量级、持久层、ORM,Hibernate 是一款全自动的 ORM 框架。
ORM
ORM 是一种自动生成 SQL 语句的技术,它可以将对象中的数据自动存储到数据库中,也可以反过来将数据库中的数据自动提取到对象中,整个过程不需要人工干预,避免了手写 SQL 带来的麻烦
- Object:对象
- Relation:关系型数据库
- Mapping:映射
Hibernate与MyBatis的比较
缓存机制
Hibernate 提供了缓存机制(一级缓存、二级缓存、查询缓存),我们可以将那些经常使用的数据存放到 Hibernate 缓存中。当 Hibernate 在查询数据时,会优先到缓存中查找,如果找到则直接使用,只有在缓存中找不到指定的数据时,Hibernate 才会到数据库中检索,因此 Hibernate 的缓存机制能够有效地降低应用程序对数据库访问的频次,提高应用程序的运行性能。
版本
-
hibernate3.x
-
hibernate4.x
-
hibernate5.x
搭建hibernate环境
- hibernate jar放进项目
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<hibernate.version>5.4.1.Final</hibernate.version>
<hibernate-validator.version>6.0.13.Final</hibernate-validator.version>
<mysql.version>5.1.47</mysql.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<!--hibernate核心依赖包-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!--数据持久化依赖-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!--表单验证依赖-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<!--数据库连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--Junit测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
- 核心配置文件hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!--数据库URL -->
<property name="connection.url">
jdbc:mysql://localhost:3306/video
</property>
<!--数据库用户 -->
<property name="connection.username">root</property>
<!--数据库用户密码 -->
<property name="connection.password">123456</property>
<!--数据库JDBC驱动 -->
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!--每个数据库都有其对应的Dialect以匹配其平台特性 -->
<property name="dialect">
org.hibernate.dialect.MySQL5Dialect
</property>
<!--在控制台打印语句-->
<property name="show_sql">true</property>
<!--指定当前session范围和上下文,为了使用懒加载 -->
<property name="current_session_context_class">thread</property>
<!--格式化sql-->
<property name="hibernate.format_sql">true</property>
<!--添加映射路径 -->
<mapping resource="Admin.hbm.xml"/>
</session-factory>
</hibernate-configuration>
- 创建映射文件
<?xml version='1.0' encoding='utf-8'?>
<!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="net.biancheng.www.po.User" table="user">
<!-- 主键-->
<id name="id" column="id">
<!--主键生成策略-->
<generator class="native"></generator>
</id>
<!--type:三种写法-->
<!--Java类型 :java.lang.String-->
<!--Hibernate类型:string-->
<!--SQL类型 :不能直接使用type属性,需要子标签<column>-->
<!--<column name="name" sql-type="varchar(20)"/>-->
<property name="userId" column="user_id" type="java.lang.String"/>
<property name="userName" column="user_name"/>
<property name="password" column="password"/>
<property name="email" column="email"/>
</class>
</hibernate-mapping>
关联映射
- 一对多
- 多对多
- 一对一
常用主键生成策略
-
assigned
主键由外部程序负责生成,在 save() 之前必须指定一个,Hibernate不负责维护主键生成。也就是可以每次指定主键。该方式不推荐
-
increment
这种方式在每次插入前,需要通过“select max(主键) from 表名 ”这种方式先查询最大ID,然后通过ID+1来作为新的主键值。这种方式也不推荐,因为这样会出现线程安全问题。
-
identity
identity由底层数据库生成标识符。identity是由数据库自己生成的,但这个主键必须设置为自增长,使用identity的前提条件是底层数据库支持自动增长字段类型,如DB2、SQL Server、MySQL、Sybase和HypersonicSQL等,Oracle这类没有自增字段的则不支持。
-
sequence
采用数据库提供的sequence机制生成主键,需要数据库支持sequence。如oralce、DB、SAP DB、PostgerSQL、McKoi中的sequence。MySQL这种不支持sequence的数据库则不行 hibernate3可指定使用哪个序列,hibernate5 为hibernate_sequence
-
hilo
hilo(高低位方式high low)是hibernate中最常用的一种生成方式,需要一张额外的表保存hi的值。保存hi值的表至少有一条记录(只与第一条记录有关),否则会出现错误。可以跨数据库。
-
native
native由hibernate根据使用的数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式,灵活性很强。如果能支持identity则使用identity,如果支持sequence则使用sequence。
-
uuid
UUID:Universally Unique Identifier,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字,标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12)
级联操作
一对多
<hibernate-mapping>
<class name="com.ishangu.model.Category" table="t_category">
<id name="cid" column="cid">
<generator class="native"/>
</id>
<property name="cname" column="cname"/>
<!--cascade级联操作-->
<set name="products" cascade="save-update">
<key column="cid"/>
<one-to-many class="com.ishangu.model.Product"/>
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.ishangu.model.Product" table="t_product">
<id name="pid" column="pid">
<generator class="native"/>
</id>
<property name="pname" column="pname"/>
<property name="price" column="price"/>
<many-to-one name="category" class="com.ishangu.model.Category" column="cid"/>
</class>
</hibernate-mapping>
- 增加
@Test
public void oneAndMany(){
Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
Category category = new Category();
category.setCname("水果");
Product product = new Product();
product.setPname("苹果");
product.setPrice(10);
// category 和 product 产生关系
category.getProducts().add(product);
// product 和 category 产生关系
product.setCategory(category);
session.save(category);
transaction.commit();
session.close();
}
- 删除
@Test
public void del(){
Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
Category category = session.get(Category.class, 2);
session.delete(category);
transaction.commit();
session.close();
}
多对多
普通
<hibernate-mapping>
<class name="com.ishangu.model.Student" table="student">
<id name="sid" column="sid">
<generator class="native"/>
</id>
<property name="sname" column="sname"/>
<!--cascade级联操作-->
<!--inverse外键维护,只能在一的一方放弃外键维护-->
<set name="courses" table="sc">
<key column="sid"/>
<many-to-many column="cid" class="com.ishangu.model.Course"/>
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.ishangu.model.Course" table="course">
<id name="cid" column="cid">
<generator class="native"/>
</id>
<property name="cname" column="cname"/>
<!--cascade级联操作-->
<!--inverse外键维护,只能在一的一方放弃外键维护-->
<set name="students" table="sc">
<key column="cid"/>
<many-to-many column="sid" class="com.ishangu.model.Student"/>
</set>
</class>
</hibernate-mapping>
- 增加
@Test
public void add(){
Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
Student s1 = new Student();
s1.setSname("张三");
Student s2 = new Student();
s2.setSname("李四");
Course c1 = new Course();
c1.setCname("前端");
Course c2 = new Course();
c2.setCname("后端");
Course c3 = new Course();
c3.setCname("Java");
// s1选择了c1和c2
s1.getCourses().add(c1);
s1.getCourses().add(c2);
// s2选择了c1和c3
s2.getCourses().add(c1);
s2.getCourses().add(c3);
// c1被s1选了
c1.getStudents().add(s1);
// c2被s1,s2选了
c2.getStudents().add(s1);
c2.getStudents().add(s2);
// c3被s2选了
c3.getStudents().add(s2);
// 如果双向关联,一定要一方放弃主键维护
session.save(s1);
session.save(s2);
session.save(c1);
session.save(c2);
session.save(c3);
transaction.commit();
session.close();
}
级联
外键维护
一对多
<!--inverse外键维护,只能在一的一方放弃外键维护-->
<set name="products" cascade="save-update" inverse="true">
<key column="cid"/>
<one-to-many class="com.ishangu.model.Product"/>
</set>
@Test
public void foreignKey(){
Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
// 获取苹果
Product product = session.get(Product.class, 1);
// 获取衣服类别
Category category = session.get(Category.class, 1);
// product 和 category 关联
product.setCategory(category);
// category 和 product 关联
category.getProducts().add(product);
transaction.commit();
session.close();
}
多对多
HQL查询
- 查询所有数据
/**
* 获取所有数据
* @return
*/
@Override
public List<House> getAllHouse() {
Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
List house = session.createQuery("from House").list();
return house;
}
/**
* 查询所有数据
*/
@Test
public void selectAllStreet(){
HouseDao houseDao = new HouseDaoImpl();
List<House> allHouse = houseDao.getAllHouse();
for (House h:allHouse
) {
System.out.println(h);
}
}
- 分页查询
@Override
public List<House> pageInfo(int pageSize, int pageNumber) {
Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
Query query = session.createQuery("from House");
query.setFirstResult(pageSize);
query.setMaxResults(pageNumber);
List<House> list = query.list();
transaction.commit();
return list;
}
HouseDao houseDao = new HouseDaoImpl();
List<House> houses = houseDao.pageInfo(1, 5);
for (House h:houses
) {
System.out.println(h);
}
hibernate的DQL操作
1:创建配置对象
2:读取配置文件
3:创建session工厂
4:获取session对象
5:具体查询操作
6:关闭session
7:关闭工厂
hibernate的DML操作
1:创建配置对象
2:读取配置文件
3:创建session工厂
4:获取session对象
5:获取事务
5:具体DML
6:提交事务
7:关闭session
8:关闭工厂
hibernate默认使用占位符形式代替参数
hbm2ddl.auto 的值
update:
1:每次使用hibernate的时候会自动检查如果表不存在则会自动创建表结构
2:每次使用hibernate的时候会自动检查,如果表中的列与xml映射不符合,会自动增加表中不存在的列(并不会删除表中存在
但是xml中没有的列)
create:
每次启动hibernate都会先删除表结构,然后再次创建表后执行sql
create-drop:
1:每次使用完hibernate后都会直接把表删掉
2:注意:只有在session工厂正常关闭的情况下才会执行最后的删除表操作
validate:
用于检查mapper与数据库表是否一致,如果不一致则直接抛异常
hibernate 主键自增策略
1:native
使用数据库本地主键生成策略 mysql=auto_increment oracle = sequence
2:uuid
使用uuid作为表的主键,需要主键类型必须是String(varchar(varchar2))
3:identity
使用数据库本地主键生成策略 mysql=auto_increment ,oracle不支持
4:sequence
使用序列作为主键(如果只配置sequence 则会自动创建一个序列叫 hibernate_sequence)mysql不支持
5:increment
会先查询当前ID的最大值,然后在上面+1(与mysql的自增长没有任何的关系)
6:tableGenerator
使用序列生成器,使用一张表来模拟序列操作,会造成很多额外sql浪费。
hibernate四种状态:(根据OID区别)
1:临时状态(瞬时态)
刚刚使用new出来的对象,没有被持久化,不再session中,没有OID
2:持久态
对象已经被持久化,加入到session的缓存中,有OID
3:游离态(托管态)
对象已经被持久化,不在session的缓存中,hibernate在提交事务的时候会自动关闭session所以事务提交后对象自动
转为游离态
4:删除状态
对象由OID,并且在session的管理中,但是已经有计划删除
getCurrentSession() 与openSession的区别
openSession 是每次都打开一个全新的session
而getCurrentSession 是从当前线程中获取session,如果当前线程不存在session的话,会使用openSession创建一个session并绑定到当前线程中。使用getCurrentSession 需要在hibernate的配置文件中配置:
thread
session对象(缓存是以map的形式存在)
1:session对象包含了很多操作数据库的方法
2:session的线程是不安全的,所以session的声明周期为(在web中)一次请求request
3:session是有一级缓存的,在一次生命周期内,获得相同类型,相同OID的对象只会发送1次sql
4:session查询出来的对象,都会放到缓存中管理
5:清楚session对象的方法,clear() 清空所有对象 session.evict(user); 清除缓存指定对象
hibernate 中查询叫做HQL查询
select 后面跟着的是model类的列名,必须与model类列名一致(大小写一致)
from 后面跟着的是model类名,大小写要求一致
hibernate中get与load的区别
- 如果未能发现符合条件的记录,get方法返回null,而load方法会抛出一个ObjectNotFoundException
- Load方法可返回实体的代理类实例,而get方法永远直接返回实体类
- load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如果没有发现符合条件的数据,将越过二级缓存,直接调用SQL完成数据读取