文章目录
一、Hibernate的入门
1、Hibernate的概述
1-1 javaEE 三层结构
1-2 什么是Hibernate
Hibernate是一个持久层的ORM框架。
1-3 什么是ORM
ORM:Object Relational Mapping(对象关系映射)。指的是将一个Java中的对象与关系型数据库中的表建立一种映射关系,从而操作对象就可以操作数据库中的表。
1-4 为什么学习Hibernate
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
- documentation :Hibernate开发的文档
- lib :Hibernate开发包
required :Hibernate开发的必须的依赖包
optional :Hibernate开发的可选的jar包 - project :Hibernate提供的项目
2-3 创建一个项目,引入jar包
- 数据库驱动包
- Hibernate开发的必须的jar包
- Hibernate引入日志记录包
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
3-2 映射的配置
- 【class标签的配置】
- 标签用来建立类与表的映射关系
- 属性:
name :类的全路径
table :表名(类名与表名一致,table可以省略)
catalog :数据库名
- 【id标签的配置】
- 标签用来建立类中的属性与表中的主键的对应关系
- 属性:
name :类中的属性名
column :表中的字段名(类中的属性名和表中的字段名如果一致,column可以省略)
length :长度
type :类型
- 【property标签的配置】
- 标签用来建立类中的普通属性与表的字段的对应关系
- 属性:
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>
-
必须的配置
连接数据库的基本的参数- 驱动类
- url路径
- 用户名
- 密码
方言
-
可选的配置
显示SQL :hibernate.show_sql
格式化SQL :hibernate.format_sql
自动建表 :hibernate.hbm2ddl.auto- none :不使用hibernate的自动建表
- create :如果数据库中已经有表,删除原有表,重新创建,如果没有表,新建表。(测试)
- create-drop :如果数据库中已经有表,删除原有表,执行操作,删除这个表。如果没有表,新建一个,使用完了删除该表。(测试)
- update :如果数据库中有表,使用原有表,如果没有表,创建新表(更新表结构)
- validate :如果没有表,不会创建表。只会使用数据库中原有的表。(校验映射和表结构)。
-
映射文件的引入
引入映射文件的位置
<mapping resource="com/qgc/hibernate/Demo1/Customer.hbm.xml"/>
4、Hibernate的核心API
4-1 Configuration对象(Hibernate的配置对象,了解)
作用:
- 加载核心配置文件
如果是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 一级缓存的原理
5、 Hibernate的事务管理
5-1 Hibernate中设置事务的管理级别
- 设置事务的级别
Read uncommitted | 以上读问题全部解决 |
---|---|
Read committed | 解决脏读,但是不可重复读和虚读有可能发生 |
Repeatable read | 解决脏读和不可重复读,但是虚读有可能发生 |
serializable | 解决所有的读问题 |
在hibernate.cfg.xml中插入如下代码即可:
<property name="hibernate.connection.isolation">4</property>
5-2 Hibernate解决Service的事务管理
5-2-1 为什么要解决service事务管理
如上图所示,事务要加到service层。因为service要进行很多的操作才能算是完成一次事务。比如说注册,首先要查询数据库中有没有重名的,然后才是往数据库中增加数据。
在前面的学习中,使用JDBC保证是同一个连接对象采用的是向下传递,也就是在service里面新建collection连接对象,在调用dao中的方法时作为参数传递过去。还有一种方式是采用ThreadLocal绑定到本地线程中
Hibernate内部已经提供了绑定好的ThreadLocal
在sessionFactory中提供了提供了一个方法getCurrentSession();
此方法需要完成配置
5-2-2 如何使用getCurrentSession();方法
- 修改配置文件,开启getCurrentSession();方法
#在hibernate.cfg.xml文件下插入如下代码
<property name="hibernate.current_session_context_class">thread</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();
}
public static Session getCurrentSession() {
return sf.getCurrentSession();
}
- 测试是否成功
@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。语句特别复杂的情况下使用此方式。