1 Hibernate工作原理

2 第一个Hibernate程序

  • 举例:primary

2.1 定义持久化对象(PO)

	package com.eason.hibernate.po;

	public class Student {
		//这里通常使用Integer而不是使用int,因为低版本框架底层可能使用null与id进行比较,如果使用int类型,则会出现错误
		private Integer id;
		private String name;
		private int age;
		private double score;

		public Student(String name, int age, double score) {
			super();
			this.name = name;
			this.age = age;
			this.score = score;
		}

		public Student() {
			super();
			// TODO Auto-generated constructor stub
		}

		public Integer getId() {
			return id;
		}
		public void setId(Integer id) {
			this.id = id;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public int getAge() {
			return age;
		}
		public void setAge(int age) {
			this.age = age;
		}
		public double getScore() {
			return score;
		}
		public void setScore(double score) {
			this.score = score;
		}

		@Override
		public String toString() {
			return "Student [id=" + id + ", name=" + name + ", age=" + age + ", score=" + score + "]";
		}
	}
	```
## 2.2 配置映射文件
-  Hibernate中主要涉及两个配置文件:主配置文件和映射文件。这两个配置文件的约束文件在Hibernate核心Jar文件 hibernate-core-5.0.1.Final.jar中的or.hibernate包中:
![](http://i2.51cto.com/images/blog/201803/10/ae24512a37ca6e5578238168e3014bed.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
![](http://i2.51cto.com/images/blog/201803/10/3465975d2644419078ac5ea5d54667c6.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
-  配置映射文件,即配置两个关系:实体类与数据库中表的映射关系,属性与表中字段的映射关系。
-  在Student类所在的包中定义和配置student.hbm.xml文件:
<?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 package="com.eason.hibernate.po">
	<class name="Student" table="t_student">
		<id name="id" column="sid">
			<generator class="native"></generator>
		</id>
		<property name="name" column="sname"></property>
		<property name="age" column="sage"></property>
		<property name="score" column="score"></property>
	</class>
</hibernate-mapping>
## 2.3 配置主配置文件
### 2.3.1 配置DB连接四要素与方言
-  主配置文件中的数据库连接相关属性值可以在hibernate框架解压目录下的project/etc/hibernate.properties文件中找到。
-  DB连接四要素与方言的key:
![](http://i2.51cto.com/images/blog/201803/10/7d4206ce6332bcbdb0ad14d968c72530.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
<!-- 连接四要素 -->
	<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
	<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test</property>
	<property name="hibernate.connection.username">root</property>
	<property name="hibernate.connection.password">02000059</property>		
	<!-- 方言 -->
	<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
### 2.3.2 配置数据源
-  数据源的key:
![](http://i2.51cto.com/images/blog/201803/10/9dbb3e1efa5f17f40e4a03fcd9c86ed4.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
<!-- C3P0数据源 -->
	<property name="hibernate.connection.provider_class"> org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
-  注意:若不指定第三方数据源,将使用Hibernate内置的数据源
### 2.3.3 配置当前Session上下文
-  指定当前Session上下文为线程,即在同一线程内所使用的Session为同一Session。
<!-- 指定当前Session上下文为线程 -->
	<property name="hibernate.current_session_context_class">thread</property>
### 2.3.4 配置自动建表
<!-- 自动建表 -->
	<property name="hibernate.hbm2ddl.auto">update</property>
### 2.3.5 配置控制台SQL输出
	<!-- 控制台SQL输出 -->
	<property name="hibernate.show_sql">true</property>
	<property name="hibernate.format_sql">true</property>	
### 2.3.6 注册映射文件

<!-- 注册映射文件 --> <mapping resource="com/eason/hibernate/po/student.hbm.xml"/>

### 2.3.7 主配置文件整体配置情况

<?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://localhost:3306/test</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">02000059</property>

	<!-- 方言 -->
	<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
	<!-- C3P0数据源 -->
	<property name="hibernate.connection.provider_class"> org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
	<!-- 指定当前Session上下文为线程 -->
	<property name="hibernate.current_session_context_class">thread</property>
	<!-- 自动建表 -->
	<property name="hibernate.hbm2ddl.auto">update</property>
	<!-- 控制台SQL输出 -->
	<property name="hibernate.show_sql">true</property>
	<property name="hibernate.format_sql">true</property>	
	<!-- 注册映射文件 -->
	<mapping resource="com/eason/hibernate/po/student.hbm.xml"/>
</session-factory>

</hibernate-configuration>

## 2.4 创建数据库
-  使用Mysql自带的test数据库。
## 2.5 定义测试类
package com.eason.hibernate.test;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.eason.hibernate.po.Student;
public class Test {

	public static void main(String[] args) {
		//1.加载主配置文件和映射文件
		Configuration configure = new Configuration().configure();
		//2.创建session工厂对象
		SessionFactory sessionFactory = configure.buildSessionFactory();
		//3.开启Session对象
		Session session = sessionFactory.getCurrentSession();
		try {
			//4.开启事务
			session.beginTransaction();
			Student student = new Student("张三", 23, 93.5);
			//5.执行操作
			session.save(student);
			//6.提交事务
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			//7.回滚事务
			session.getTransaction().rollback();
		}
	}
}
# 3 CRUD操作
## 3.1 工具类的创建
-  每次在获取Session时,都需要通过Configuration对象加载主配置文件和映射文件,并且要创建SessionFactory对象,降低了开发和执行效率。所以,对于Session对象的获取,可以创建一个工具类。
-  在primary项目中,创建一个utils顶层功能包,将工具类放入其中。
package com.eason.hibernate.hbnUtils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HbnUtils {
	private static SessionFactory sessionFactory;

	public static Session getSession() {
		return getSessionFactory().getCurrentSession();
	}

	private static SessionFactory getSessionFactory() {
		if(sessionFactory == null || sessionFactory.isClosed()) {
			sessionFactory = new Configuration().configure().buildSessionFactory();
		}
		return sessionFactory;
	}
}
## 3.2 CURD测试
-  在primary项目的基础上,再创建一个测试类MyTest。在其中通过使用工具类HbnUtils来测试增、删、改和简单查询操作。
### 3.2.1 save()
//测试save()
	@org.junit.Test
	public void testSave() {
		Student student = new Student("李四", 24, 94.5);
		Session session = HbnUtils.getSession();
		try {
			session.beginTransaction();
			session.save(student);
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}
### 3.3.2 persist()
-  save()为Hibernate的API,而persist()为JPA的API。均用于完成持久化。
	//测试persist()
	@org.junit.Test
	public void testPersist() {
		Student student = new Student("李四", 24, 94.5);
		Session session = HbnUtils.getSession();
		try {
			session.beginTransaction();
			session.persist(student);
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}
### 3.2.3 delete()
-  其底层是根据id进行删除的。所以,指定的删除对象必须要有id属性。
	//测试delete()
	@org.junit.Test
	public void testDelete() {
		Student student = new Student();
		student.setId(1);
		Session session = HbnUtils.getSession();
		try {
			session.beginTransaction();
			session.delete(student);
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}
### 3.2.4 update()
-  其底层是根据id进行修改的。所以,指定的修改对象必须要有id属性。
//测试update()
	@org.junit.Test
	public void testUpdate() {
		Student student = new Student("赵六", 25, 95.5);
		student.setId(2);
		Session session = HbnUtils.getSession();
		try {
			session.beginTransaction();
			session.update(student);
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}
### 3.2 5 saveOrUpdate()
-  通过参数对象是否具有id值来判断是执行save()操作还是执行update()操作。若参数对象具有id,但是该id在DB中不存在,则会抛出异常。
//测试saveOrUpdate()
	@org.junit.Test
	public void testSaveOrUpdate() {
		// 设置要插入或者修改为的值
		Student student = new Student("赵六", 28, 95.5);
		//设置要修改的对象的id。该语句决定是执行save还是update()
		student.setId(2);
		Session session = HbnUtils.getSession();
		try {
			session.beginTransaction();
			session.saveOrUpdate(student);
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}
### 3.2.6 get()
-  在查询无果时,会给出null,但是不会抛出异常。
//测试get()
	@org.junit.Test
	public void testGet() {
		Session session = HbnUtils.getSession();
		try {
			session.beginTransaction();

			//没有id为90的student,其查询结果为null,没有异常抛出
			Student student = session.get(Student.class, 90);
			System.out.println(student);
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}
### 3.2.7 load()
-  在查询无果时,会抛出异常。
//测//测试load()
@org.junit.Test
public void testLoad() {
	Session session = HbnUtils.getSession();
	try {
		session.beginTransaction();
		
		Student student = session.load(Student.class, 90);
		//没有id为90的student,抛出异常
		System.out.println(student);
		session.getTransaction().commit();
	} catch (Exception e) {
		e.printStackTrace();
		session.getTransaction().rollback();
	}
}
### 3.2.8 对于增、删、改的底层SQL执行顺序
-  对于不同操作对象的增、删、改操作,无论其代码的书写顺序是怎么样的,底层SQL的执行顺序都是insert、update、delete。
-  若要修改其底层的执行顺序,则可通过Session的flush()方法刷新Session完成。flush()会使得前后分隔为两部分,各部分会按照其顺序依次执行。
# 4 详解API
## 4.1 Configuration 接口
-  org.hibernate.cfg.Configuration接口的作用是加载主配置文件以及映射文件,以实现对Hibernate的启动。
-  Configuration 实例的获取方式:Configuration cfg = new Configuration().configure();。
### 4.1.1 new Configuration()
-  new Configuration()会加载一个属性文件hibernate.properties。该属性文件中存放着数据连接配置、Hibernate配置等配置信息。一般情况下不用设置该属性文件,其设置已经放在主配置文件中。若要设置,应将其放于src目录中。该文件的模版存放于Hibernate框架解压目录下的project/etc中。
![](http://i2.51cto.com/images/blog/201803/11/784ea553c5e892f87a3ece996ea5134b.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
### 4.1.2 无参configure()方法
-  configure()方法,默认用于加载和解析名称为hibernate.cfg.xml配置文件,并通过主配置文件找到并解析映射文件。该方法返回一个Configuration对象。所以,cfg是一个包含配置信息以及映射信息的Configuration对象。
-  在查看configure()方法的源码:
![](http://i2.51cto.com/images/blog/201803/11/1c0d09175cb2317a746e138e924a48d9.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
![](http://i2.51cto.com/images/blog/201803/11/e996b0e52ac5844bc1837f077d764a64.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
### 4.1.3 带参configure()方法
-  Hibernate主配置文件默认名称为hibernate.cfg.xml,应存放在src类路径下。但也可以更换路径和文件名。此时,需要使用带参的configure()方法。
![](http://i2.51cto.com/images/blog/201803/11/bee454753bd56eb9073af681bdf1f69c.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
![](http://i2.51cto.com/images/blog/201803/11/f167fb5ef6677d5ab126d980a4b7a654.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
## 4.2 SessionFactory接口
-  org.hibernate.SessionFactory接口对象是由Configuration对象通过buildSessionFactory()方法创建的。创建该对象的目的是,用于开启Session对象。
-  SessionFactory sessionFactory = cfg.buildSessionFactory();。
### 4.2.1 SessionFactory对象特点
-  重量级对象(系统开销大)、单例的、线程安全的。
-  按理说,单例对象一定是被共享的,是线程不安全的。但是查看SessionFactory接口的实现类SessionFactoryImpl源码,可以看起大多数成员变量时final的,所以是线程安全的。
![](http://i2.51cto.com/images/blog/201803/11/f2e8474fc3ec3a571586e8e23e838ee9.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
### 4.2.2 SessionFactory对象的使用原则
-  基于其是线程安全的重量级对象,其创建和销毁时系统开销大,又是单例的特点,SessionFactory对象一般不手工关闭,而是在应用结束时自动将其销毁。因此,SessionFactory不用进行close()关闭。
## 4.3 Session接口
-  org.hibernate.classic.Session接口是应用程序与Hibernate连接的核心API,是Hibernate向应用程序提供的操纵DB的最主要的接口。它提供了基本的保存、更新、删除和查询方法。由SessionFactory对象创建。
-  Session s= sessionFactory.getCurrentSession();。
### 4.3.1 Session对象的特点
-  一个轻量级对象、线程不安全的、多例的。
-  在Web应用中,多个用户对同一应用访问,Hibernate会为每个用户创建一个Session对象。所以是多例的。Session中包含大量非final成员变量,对于同一个用户的操作,可能会产生多个事务。这多个事务若同时对同一个Session的同一个成员变量进行访问,就会引起并发问题。所以session是线程不安全的。
![](http://i2.51cto.com/images/blog/201803/11/278947a3119987e46f8cb335a49b90d1.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
### 4.3.2 Session对象的使用原则
-  基于Session的以上特点,Session在使用时要做到一个线程一个Session,即一个事务一个Session。使用完毕,立即关闭。Session不要作为某个类的成员变量出现,因为这样会出现多个实例对象对同一个session的共享,使其不安全。
### 4.3.3 Session对象的获取
-  SessionFactory对于Session对象的获取,提供了两种获取方式:
-  sessionFactory.openSession():创建一个新的 Session 对象。
-  sessionFactory.getCurrentSession():获取当前线程中的 Session 对象。
1、openSession()方式:每次执行一次openSession()方法,均会创建一个新的Session对象。
![](http://i2.51cto.com/images/blog/201803/11/53249bdb3b0651ebb256f96af5230126.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
2、getCurrentSession()方式:每次获取到的都是当前线程中的Session对象,都是同一个Session。
![](http://i2.51cto.com/images/blog/201803/11/19cd651034e710ceda8987ffb65b707b.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
3、当前Session上下文:为了保证一个线程一个Session,即一个线程中使用的Session是同一个对象,一般在获取Session对象时,使用SessionFactory的getCurrentSession()方法。不过,使用该方法获取Session对象,需要在主配置文件中对Session所处的上下文环境,即事务环境进行注册。
![](http://i2.51cto.com/images/blog/201803/11/ab9280a1b55ff9bc2044e508dcef4ca5.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
-  hibernate.current_session_context_class取值有三种:

| 取值 | 意义 | 
| -------- | -------- | 
| thread   | 表示当前Session所处的环境为本地事务环境,Session会与当前线程绑定     |
| jta         |  表示当前Session所处的环境为分布式事务环境          |
|  SpringSessionContext类 |   SSH整合时使用。表示将当前Session的管理权交由Spring容器,而Spring容器中会有事务环境。即当前Session所处的环境为Spring事务环境。|


-----


4、为什么getCurrentSession()方式获取到的是同一个对象?
-  getCurrentSession()首先会获取到当前线程Thread.currentThread(),然后从ThreadLocal中读取key为当前线程的value。而该value即为当前线程中的Session对象。
-  ThreadLocal是用于在线程中共享数据的。其底层为Map<Thread, Object>,即该map的key为线程Thread,而value则为要共享的数据。
-  当在Hibernate主配置文件中设置 hibernate.current_session_context_class属性值为thread时,就表明每个用户获取到的Session对象都是自己当前线程中的Session,是同一个Session。


-----


5、Session的关闭:使用getCurrentSession()方法获取的Session,在进行事务提交或者回滚后,会自动关闭,无需再手工进行close()。当然,也不能在最后的finally{}语句块中对Session进行手工关闭。因为,无论是执行事务的commit(),还是执行rollback(),均会在finally{}之前执行。也就是说,在执行finally{}之前,session已经关闭。若再手工关闭,将会抛出异常。


-----


6、总结:两种获取Session对象方式的区别
-  获取Session对象,可以使用openSession()方式,也可以使用getCurrentSession()方式。这两种方式在本质上有着很大的区别。

| | getCurrentSession()方式 | openSession()方式 | 
| -------- | -------- | -------- | 
| 获取的对象    | 无论执行多少次该方法,只要是在同一个线程中,获取的都是同一个Session对象     |  每执行一次该方法,获取到的都是一个新的Session对象            | 
|  对象的关闭  | 自动关闭Session,无需手工关闭     | 必须手工关闭Session对象    | 
| 环境的注册    | 需要注册Session的运行环境     | 无需注册    | 
| 查询对事务的支持 |  查询必须在事务内执行 |  查询可以不在事务内执行  |

### 4.3.4 Session中的常用方法
-   save()/persist:添加对象
-   update:修改对象
-   saveOrUpdate():添加或者修改对象
-   delete():删除对象
-   get()/load():根据主键查询
-  createQuery()/createSQLQuery():创建查询对象
-  createCriteria():条件查询,QBC,纯面向对象语句。   
## 4.4 Transaction接口
-  通过该接口,可以将事务从持久层,提升到业务层,由Session对象创建。
-  事务的开启:session.beginTransaction();或者session.getTransaction().begin();
-  事务的提交:session.getTransaction().commit();
-  事务的回滚:session.getTransaction().rollback();
## 4.5 总结 -- 单例、多例和线程安全问题
-  单例和多例问题是指,当多个用户访问某个类时,系统是为每个用户创建一个该类实例,还是整个系统无论多少用户访问,只会创建一个该类实例。
-  线程安全问题是指,多个用户同时在访问同一个程序时,其对于某一个数据的修改,会不会影响到其他用户中的该数据。若没有影响,则是线程安全的;若有影响,则是线程不安全的。
-  现在对于HttpServlet、HttpSession、Struts2中的Action、Hibernate中的SessionFactory与Session,进行总结。


-----

1、HttpServlet:
-  其是单例的。即无论多少用户访问同一个业务,如LoginServlet,Web容器只会创建一个该Servlet实例,而该实例是允许多用户访问的。                                         
-  若Servlet中包含成员变量,则每个用户对于成员变量的修改,均会影响到其他用户所看到的该变量的值,所以这时线程是不安全的。若不包含成员变量,则是线程安全的。


-----

2、HttpSession:
-  其是多例的。Web容器会为每个用户开辟一个Session,多个用户会有多个Session,而每用户只能访问自己的Session。所以对于Session来说,就不存在并发访问的情况,也就不存在线程安全问题。所以可以说是线程安全的。


-----

3、Struts2的Action:
-  其是多例的。对于同一个业务,例如LoginAction,系统会为每一个用户创建一个LoginAction的实例,并使其成员变量username和password接收用户提交的数据。同一用户只能访问自己的Action。所以,对于Aciton来说,就不存在并发访问的情况,也就不存在线程安全的问题。所以可以说是线程安全的。


-----

4、Hibernate的SessionFactory:
-  其是单例的。无论多少用户访问该项目,系统只会创建一个SessionFactory对象,即这个对象是可以被所有用户访问的。
-  SessionFactory实现类中所包含的成员变量基本都是final常量,即任何用户均不能修改。所以,也就不存在用户的修改对其他用户的影响问题,所以是线程安全的。


-----


5、Hibernate的Session
-  其是多例的,系统会为每个用户创建一个Session。
-  Session的实现类中定义了很多的非final成员变量,一个事务对成员变量所做的修改,会影响到另一个事务对同一个数据的访问结果,所以线程是不安全的。

# 5 主配置文件详解
-  主配置文件、主要配置三方面信息:
1、连接数据库的基本信息:驱动、URL、用户名、密码。
2、Hibernate框架特性。
3、注册映射文件,即指定映射文件的位置。
-  对于<session-factory/>标签,可以包含多个<property>元素,用于配置hibernate与DB的连接信息以及数据源信息;可以包含多个<mapping>元素,用于注册多个映射文件。                               

<session-factory> DB的连接信息 Hibernate特性 注册映射文件 </session-factory>

## 5.1 数据库连接设置
### 5.1.1 DB连接四要素的key
-  在数据库连接四要素中的name属性名称,即连接四要素的key的写法,connnection.*与hibernate.connection.*的效果是完全相同的。是为了兼容以前的版本。
### 5.1.2 url取值的写法
-  在数据库连接的url属性值的设置,一般写法是jdbc:,mysql://localhost:3306/test。但是localhost:3306不写也是正确的,即:jdbc:mysql:///test。
![](http://i2.51cto.com/images/blog/201803/11/0b0120314a53467dc89ad4c178c3c535.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
### 5.1.3 hibernate.properties文件
-  对于DB连接四要素,一般习惯于将其放入到属性文件properties中,再将该属性文件注册到配置文件中。那么,Hibernate主配置文件中的DB连接四要素是否需要放到属性文件中呢?
-  通过Hibernate运行时所输出的日志信息可以看出,Hibernate在运行时默认会首先查找和加载一个属性文件hibernate.properties。
![](http://i2.51cto.com/images/blog/201803/11/9e14fb33d3cb24cb2b3ac136b419a823.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
-  hibernate.cfg.xml文件的<properties/>标签中设置的属性几乎都可以在这个文件中设置。其中就包括数据库连接四要素。所以,在Hibernate中,无需专门设置让hibernate.cfg.xml文件从属性文件中读取DB连接四要素。这个四要素可以直接放到hibernate.properties文件中,Hibernate即会自动读取。
![](http://i2.51cto.com/images/blog/201803/11/eae521d32d28b4b4615d983ca337366f.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
-  若要使用hibernate.properties文件,该文件是放在src的类路径下的。当然,若程序中同时使用了两个配置文件,且hibernate.properties与hibernate.cfg.xml中均对同一属性进行了设置,那么后者会将前者的值给覆盖,即后者的优先级更高。(一般不使用hibernate.properties属性文件)
## 5.2 方言设置
-  方言的设置信息可以从Hibernate核心Jar文件 hibernate-core-5.0.1.Final.jar中的or.hibernate.dialect 包中找到相应的类。复制全类名即为方言的值。
![](http://i2.51cto.com/images/blog/201803/11/9f9f2c8be8f166b03f50e721b3ee1cb5.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
## 5.3 自动建表设置
-  hibernate.hbm2ddl.auto的取值有三种:create、create-drop、update。
1、create:每次加载主配置文件时都会删除上一次生成的表,然后再生成新表,哪怕两次表结构没有任何变化。
2、create-drop:每次加载主配置文件时会生成表,但是sessionFactory一旦关闭,表就自动删除。
3、update:当表字段增加时,会添加字段;当表字段减少时,不会减少字段。若表结构没有变化,但是数据变化时,会修改数据。
## 5.4 C3P0数据库连接池配置
-  Hibernate5默认使用的是其自己开发的内置(build-in)连接池。该连接池只是调试代码时使用,在真正产品中不能够使用。可以从控制台的Hibernate启动信息中查看到。
![](http://i2.51cto.com/images/blog/201803/11/8638d8080c5d5ba5c050c7756d5cf8dd.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
注意,若不指定第三方数据源,将使用Hibernate内置的数据源。该数据源无法自动将数据库连接Connection释放,而数据源作为SessionFactory的属性,一直被当前程序占用,导致SessionFactory在程序运行结束后无法自动关闭。而这将导致整个程序无法结束。其表现为Eclipse的Terminate按钮一直显示为红色运行状态。
-  工业生产中,常用的数据源有DBCP、C3P0等。下面以C3P0为例进行配置。
-  数据源在Hibernate主配置文件中的key,在Hibernate框架解压目录的project/etc下的hibernate.properties中可以找到:
![](http://i2.51cto.com/images/blog/201803/11/ff3cf049a8e37785bcf0b5ed8d572880.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
-  C3P0在主配置文件中的类名,在导入的包中可以查看到。
![](http://i2.51cto.com/images/blog/201803/11/55d126527b54582fb2fcead298822230.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
-  其在Hibernate主配置文件中的设置如下:
![](http://i2.51cto.com/images/blog/201803/11/579d9b427d5d49a639e9214218814025.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
## 5.5 注册映射文件
-  映射文件若存在多个,则可写多个<mapping/>
![](http://i2.51cto.com/images/blog/201803/11/13b6b260b5c3bba99bc68f36c994c360.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
# 6 映射文件详解
-  配置映射文件,即配置两个关系:
1、实体类与数据库中表的映射关系。
2、属性与表中字段的映射关系。
## 6.1 <hibernate-mapping/>标签
-  该标签是Hibernate映射文件的根元素,其下可以包含多个<class/>标签。常用的属性主要是package属性,用于指定其所包含的<class/>类所在的包。
## 6.2 <class/>标签
-  该标签用于设置PO类和数据表间的映射关系。
1、name属性:指定持久化类。若<hibernate-mapping/>标签设置了package属性,name,此处的name属性只需是类名即可;否则,需要是含包名的完整类名。
2、table属性:指定与持久化类对应的数据表的名称。若不指定,Hibernate将默认为表名与类名相同。
3、catalog属性:指定数据库。默认为主配置文件中指定的DB。
## 6.3 <id/>与<property/>标签
-  它们都是<class/>标签的子标签。常用有: 
1、name属性:指定持久化类的属性名;
2、column属性:指定数据表中的name属性对应的字段名。若不指定,默认为与name属性同名;
3、length属性:指定属性所映射字段的长度,单位字节;
4、not-null属性:为指定字段添加非空约束;
5、unique属性:为指定字段添加唯一性约束;
6、type属性:指定属性所映射字段的类型。若省略Hibernate会自动从持久化类中检测到类型。这里类型取值支持两类:java类型和Hibernate类型;
-  java类型指的是java代码中的类型。若是基本数据类型,如int、double等,直接写即可。如果是对象类型,则需要写上全类名,如java.lang.String。
![](http://i2.51cto.com/images/blog/201803/11/64a0f09612f35a78e9f6e33bb292dda9.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
-  Hibernate类型指的是Hibernate中定义的类型。在type中的双引号中alt + ?可查看到Hibernate中的所有类型。
-  主要注意的是,若PO中属性类型为byte[], 则这里的type应指定为binary。
-  若PO中属性类型为boolean,type也可指定boolean,但是数据库中的类型为bit(二进制位)。用0代表false,1代表true。
7、sql-type属性:当然映射文件中字段类型还支持一种类型,即数据库中数据类型。但是这种类型的使用,需要使用<column>元素,其中有一个sql-type属性用于指定字段类型。其值为所使用DBMS的数据类型。
![](http://i2.51cto.com/images/blog/201803/11/f75615fdf268bcb7dd9eed71b85b933c.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
## 6.4 Hibernate常用的内置主键生成策略
-  主键生成策略,即主键的的生成方式,不同的生成策略,其生成主键的方法和值不同。注意,以下在测试时,需要将上一次测试时生成的表删除,这样才能够测试出正确结果。
### 6.4.1 increment生成策略
-  该策略是Hibernate自己在维护主键的值。当准备在数据库表中插入一条新记录时,首先从数据表中获取当前主键字段的最大值,然后在最大值基础上加上1,作为新输入记录的主键值,这就是increment生成策略。
-  用其生成的主键字段所对应的属性类型可以是long、short、int以及其封装类的类型。这种生成策略只有在没有其他进程向同一张表中插入数据时才能够使用,在高并发或者集群环境下是不能够使用的。
### 6.4.2 identity生成策略
-  该策略使用数据库自身的自增长来维护主键值。如mysql使用auto_increment来维护。用其生成的主键字段所对应的属性类型可以是long、short、int以及其封装类的类型。
-  该策略在生成主键值时会出现以下情况:对于插入操作,即使最后的执行时回滚,DB中记录主键值的变量也会增一。因为该生成策略在发生回滚之前已经调用过DB的主键自增,所以无论是否提交,对于DB来说都已经执行。
### 6.4.3 sequence生成策略
-  在Oracle,DB2和PostgreSQL等数据库中创建一个序列(sequence),然后Hibernate通过该序列为当前记录获取主键值,从而为实体对象赋予主键字段映射属性值。此即sequence生成策略,用其生成的主键字段映射属性值的类型可以是long、short、int以及其封装类的类型。
-  对于MySql数据库,原来是不支持序列的,但是稍微修改后,MySql也支持该生成策略。
-  其测试情况是,在第一次执行时,后台会输出以下查询语句,并报错。
![](http://i2.51cto.com/images/blog/201803/11/81b443d8f614ffd746ed0af4cfaccc8d.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
-  该语句意思是,从hibernate-sequence序列表中查询字段值next_val,该值将作为要插入数据的主键值。当然,该查询语句中的for update表示,对该表的操作是使用了乐观锁的。
-  打开数据库,发现多生成了一张表hibernate_sequence,打开该表,发现其值为空,无法进行自增运算。这就是报错的原因:没有初始值,手工为其赋初值1即可进行运行。
-  再运行后,查看控制台,其执行了三条SQL语句。第一条用于查询出本次要使用的主键的值。第二条用于更新序列表中的值,为下一次读取做准备。第三条为插入操作,使用本次读取出来的值完成插入。
![](http://i2.51cto.com/images/blog/201803/11/dd1e065ef5b7a2f0998cf3da221fabaa.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
### 6.4.4 native生成策略
-  由Hibernate根据所使用的数据库支持能力从identity、sequence生成策略中选择一种。
-  使用这种标识符属性生成策略可以根据不同的数据库采用不同的生成策略,如Oracle中使用sequence,在MySQL中使用identity,便于Hibernate应用在不同的数据库之间移植。
### 6.4.5 uuid生成策略
-  uuid生成策略采用UUID(Universally Unique Identifier,通用唯一识别码)算法来生成一个字符串类型的主键值,该值使用IP地址、JVM的启动时间(精确到1/4秒)、当前系统时间和一个计数器值(在当前的JVM中唯一)经过计算产生,可以用于分布式的Hibernate应用中。产生的标识符属性是一个32位长度的字符串。使用这种生成策略,要求属性的类型必须为String类型。
-  这种标识符属性生成策略生成的数值可以保证多个数据库之间的唯一性,并且由于其生成与具体的数据库没有关系,所以其移植性较强。但是由于该值是32位长的字符串,所以占用的数据库空间较大,并且检索速度较慢。不过,实际开发中使用这种生成策略较多。
-  除了使用Hibernate外,在JDBC中也可以使用uuid生成主键。因为UUID是java.util包中的一个独立的类。打开项目中的JRE System Library库中的rt.jar,在其中找到java.util包,即可看到UUID这个类。
### 2.6.6 assigned生成策略
-  该生成策略的主键值来自于程序员的手工设置,即通过setId()方式设置。属性类型可以是整型,也可以是String,但一般为String。此生成策略,主要应用于业务相关主键。例如学号、×××号做主键。
# 7 持久对象状态管理
## 7.1 Hibernate的对象状态
-  对象的状态一般是指对象的一组属性的值。而这里的状态是指对象处于什么存储介质中。
-  用于存放对象的存储介质有三个:普通内存(与Hibernate无关)、Session缓存、数据库。对象处于不同的介质,就将处于不同的状态。
1、瞬时态:transient状态,对象在内存中存在,但是DB无记录,与Session无关,是个过渡状态;
2、持久态:persistent状态,在内存中存在,DB中有记录,与Session相关,在Session中有对象的副本;
3、游离态:detached状态,在内存中存在,在DB中有记录,与Session无关;
4、无名态:在内存中不存在,但是在DB中有记录,与Session无关。
## 7.2 状态转换图
![](http://i2.51cto.com/images/blog/201803/11/78fb3d90012ad3a2993448545392f0f6.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
## 7.3 状态转换常用方法
1、save():将瞬时态对象同步到DB中;
2、update():将游离态对象同步到DB中;
3、delete():将指定的对象从session中删除,同时也删除DB中的该数据;
4、close():关闭Session对象;
5、clear():清空Session缓存;
6、saveOrUpdate():根据参数对象的id属性是否为null来判断是执行保存还是更新操作;
7、evict():将指定对象仅仅从session中删除,但不删除DB中的该数据;
8、load()与get():将无名态对象转换为持久化对象;