Java Persistence API,是Java EE为ORM框架定义的规范,任何使用java语言的ORM框架都必须实现该规范。Hibernate/Mybatis都是是JPA的一种实现。java
Object Relational Mapping,对象到关系的映射,在关系型数据库与对象之间创建映射关系,以实体对象操做关系型数据库。程序员
将对数据库的操做永久地反映到数据库中的过程叫作持久化。数据库
封装了对数据库进行持久化操做的框架叫作持久化框架。所谓框架就是在内部实现了一些经常使用的基本操做,提供简单的接口,缩减操做步骤,并且扩展了功能。缓存
数据库链接是一个极其有限的宝贵资源,在Web应用程序中表现得尤其突出。Web应用访问量大,而数据库支持的并发链接数目是有限的,链接越多,速度越慢,下降了用户体验。安全
在数据库链接池产生之前,访问数据库须要先创建链接,访问结束后关闭链接,下次须要时再重复建立与关闭过程,而数据库链接的建立与关闭自己消耗大量的系统资源,这时产生了共享数据库链接的思想,数据库链接池应运而生。session
Hibernate框架推荐使用C3P0:并发
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
负责加载配置文件,启动Hibernate。配置文件的默认名称为“hibernate.cfg.xml”,放在类路径下。app
负责建立Session对象,保存了当前数据库中全部的映射关系,线程安全。重量级对象,初始化过程耗费大量的资源,所以在应用程序中应避免建立多个SessionFactory对象。框架
表明应用程序与数据库的一次会话,是Hibernate中持久化操做的核心,直接负责全部的持久化操做。ide
session对象线程不安全,应避免多个线程共享一个Session对象。多个线程共享一个session对象,可能会由于session缓存的存在出现数据泄露,或者一个线程关闭了session对象,另外一个线程出现异常。
Session只能在事务内部运行,即未开启事务,没法运行Session。
Session session=sessionFactory.openSession();
Session session=sessionFactory.getCurrentSession();
<property name="current_session_context_class">thread</property>
Session一旦关闭,就标志着应用程序与数据库的一次会话结束,再次通讯须要创建新的会话。
生成策略 | 含义 | 维护方 |
increment | 获取数据库中当前主键的最大值,加1做为新数据的主键 | Hibernate |
identity | 按照自增方式生成主键 | 数据库 |
native | 从底层数据库支持的主键生成策略中选择 | 数据库 |
assigned | 由程序员手动生成主键 | 程序员 |
sequence | 基于sequence表生成主键 | 数据库 |
uuid | 生成一个全球惟一的32位主键 | 数据库 |
foreign | 根据另外一张表的主键生成主键,造成一对一主键关联 | Hibernate |
对象被建立、未被Session引用时的状态,与数据库中的数据无链接,数据库不存在对应数据,一旦被Session经过save或者saveOrUpdate调用方法转化为持久化状态。
对象被session引用,出如今session缓存时处于持久化状态。对象只有处于持久化状态,对其进行的操做才会持久到数据库中。
当Session对象关闭之后,持久化对象由持久状态转化为脱管状态,持久化对象处在脱管状态下仍然与数据库中的数据存在关联,数据库中存在对应数据,经过update或者saveOrUpdate转化为持久化状态。
脱管状态与瞬时状态对比:
假如把关系型数据库中的表看作一个持久化类,每一行记录都是一个该类的对象,即持久化对象,一个对象若是在集合以外,则处于瞬时状态;在集合以内而且正在被Session调用,处在持久化状态;若是Session关闭,处在脱管状态。
对象的状态发生改变时,对象在内存中的地址并未发生改变,仍然是同一对象,只是对象与数据库的关系发生改变。
session.save(Object obj);//底层生成一条insert语句
Object obj=(Object)session.get(Object.class,Serializable id);//底层生成一条select语句
Object obj=(Object)session.get(Object.class,Serializable id);//底层生成一条select语句
返回对象的代理,在对象被调用时,才向数据库发送SQL语句。
Object obj=session.get(Object.class,Serializable id); session.delete(obj);//底层生成一条delete语句
Object obj=session.get(Object.class,Serializable id); obj.setter();//底层生成一条update语句
假定session缓存中保存的是对象自己,而不是对象的引用,那么清空session缓存,会销毁对象,测试一下:
@Test
public void testClear() { Session session = HibernateUtils.getSession(); try { session.beginTransaction(); Student stu = session.get(Student.class, 1); System.out.println("stu=" + stu); session.clear(); System.out.println("stu=" + stu); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } }
输出:
很明显,session缓存清空之后,依然能够输出对象的详情,说明对象依然存在,从而得出结论:session缓存中保存的不是对象,而是对象的引用。
加载完成之后,在session缓存中创建一个对象的引用变量,而且使引用变量stu也指定该对象。
事务提交是Hibernate中惟一的同步时间点,事务提交时将Session缓存中的数据同步到数据库。
遇到Query查询、flush方法、事务提交时,执行删除与修改操做更新session缓存中的内容,只有在事务提交时才同步到数据库。
同步时间点与刷新时间点所作的工做就是将以面向对象形式对数据的操做转化为以SQL形式对数据库的操做,由于不管框架怎么封装,底层数据库所能识别与执行的命令只有SQL语句,框架提供了面向对象这种相对简单的操做数据库的方式,底层再将这种方式转化为数据库接受的方式。
刷新时间点与同步时间点就是启动转化的时机。刷新时间点将转化的SQL语句写入session缓存,同步时间点将转化的SQL语句写入数据库。
修改缓存中已有的对象,好比修改对象属性值,删除对象,不会清空缓存,不会同步到数据库,不影响那些与缓存内容无关的操纵,好比save\get操做不受flush影响,当即执行。
同步时间点与刷新时间点都受快照的约束。
将从数据库中加载的对象分别保存在内存的3个区域,各区域之间相互独立,互不影响。
一级缓存与二级缓存中的内容都以Map集合的形式存储,key是持久化类与惟一性标识(id)的组合,value是对象的引用变量。
判断一级缓存或者二级缓存中是否存在一个对象的依据是,该对象的持久化类与惟一性标识构成的组合是否存在于Map集合中。
一级缓存是Session级的,生命周期与Session相同,随Session的建立而建立,随Session的销毁而销毁。
Session缓存中的数据在Session内部共享,Session之间不共享。
快照内存中一块存储区域,提供了一种修改审查机制。
事务提交时,将缓存中准备同步到数据库中的内容与快照中的内容进行对比,不一致才同步到数据库,避免了对数据库没必要要的访问,减轻了数据库的负担。
二级缓存即SessionFactory缓存,为全部Session对象共享。
Hibernate自己并未实现二级缓冲,须要使用第三方插件,其中一种插件为EHCache,注册后才可以使用
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
实体类只有在二级缓存中设定了使用策略以后才能够缓存在二级缓存中,设定使用策略有两种方式:
<class> <cache usage="read-only"/> </class>
<class-cache class=""usage=""/>
用来存储Query查询结果的内存区域,独立于一级与二级缓存,供Query查询使用。
Hibernate默认关闭了Query缓存,若是须要使用,须要在配置文件中开启:
<property name="cache.use_query_cache">true</property>
Query缓存不只包含查询结果,并且包含hql语句,搜索Query缓存时,首先对比hql语句,Query缓存只有对彻底相同的hql语句可见。
Query q=session.createQuery(hql); List list=q.setCacheable(true).list();
在多的一段添加外键:
<many-to-one name="属性名"class="一方全限定性类名"> <column name="在本表中添加的外键字段">//多的一方表中外键名 </many-to-one>
//多方映射文件 <many-to-one name="属性名" class="一方"> <column name="外键"> </many-to-one> //一方映射文件 <set name="属性名" inverse="true"> <key column="外键"/> <one-to-many class="多方"/> </set>
一个实体与另外一个实体一一对应,能够经过主键关联实现这种关联关系,造成表与表之间的一对一主键关联。也能够在一张表中创建外键,外键字段惟一,引用另外一张表的主键,造成一对一外键关联。
一对一主键关联的核心是一张表的主键由另外一张表的主键生成,即从另外一张表的主键中取值,至关于主键又做为外键,引用一张表的主键。
映射文件的建立:
<id> <generator class="foreign"> <param name="property">当前实体中另外一个实体的引用变量</param> </generator> </id> <one-to-to name="引用变量名"class="类名"constrained="true"/>
constrained:用来设定子表与父表之间是否存在外键约束,便是否把子表的主键当作外键关联父表的主键。可取值false/true,取值为false时,两张表孤立,无任何关系,取值为true时,子表的主键引用父表的主键。通常设定为为true。
一对一外键关联是多对一外键关联的特例,将外键字段设定为不可重复,即由多对一外键关联转化为一对一外键关联。
<many-to-one name=""class=""unique="true"> <column name=""/> </many-to-one>
将多对多关联分解成2个一对多关联,提供第三张表做为中间表,做为多的一方。
<set name="属性名"table="tb_mid"> <key column="中间表中的外键"/> <many-to-many class=""column="关联实体在中间表中的外键"/> </set>
实体A与实体B的属性彻底相同,其中一方包含另外一方,据此建立一个实体,包含A与B双向的关联关系。
由于两个实体属性全相同,存在共用一张表的可能,放在一张表中不只能够节省空间,并且能够提升查询效率。
将自关联看做多对一双向关联。
相关联的两方,一方为主动方,另外一方为被动方,当主动方发生变更时,被动方产生一样的变更,这种变化上的关联关系叫作级联。
一张表中的某个字段引用另外一张表中的某个字段,为了保证源字段发生该表时,引用字段作出一样的改变,保证数据的正确性,能够设置级联操做。
在映射文件中级联属性用cascade表示,可取值:
删除与当前对象不存在关联的对象,只能从父表删除,由于可能子表中多个对象引用同一父表对象,若是从子表删除,那么子表中引用该父表的其余对象也可能被删除,而这是不指望的。
Child oneChild=session.get(xxxxx);//首先获取子对象
parentObj.remove(oneChild);//解除子对象与父对象之间的关系,将子对象从子表中删除
不设置成all-delete-orphan时,只解除关联关系,即子表中对应数据的外键为空。
对一方做为主加载方加载多方的操做进行优化。
lazy可取值:
对多方做为主加载方加载一方的操做进行优化。
lazy可取值:
不管是一端加载,仍是多端加载,默认采用延时加载,分别查询的方式。
将存在继承关系的多个实体映射成一张表,表中既有共有字段,又有特有字段,为了代表数据从属的实体,增长一个身份标识字段:
<discriminator column="字段名"type="string">//身份标识字段,紧跟主键定义 //省略父类属性映射 <subclass name=""class=""discriminator-value="身份名"> <property name=""/>//特有字段定义 </subclass>
父类映射成一张表,子类新增属性映射成一张表,子类主键做为外键引用父类的主键,实现表的链接:
<joined-subclass name="全限定性类名"table="子表名"> <key column="id">//在子表中增长外键,关联本表的主键,该外键同时是子表的主键 <property name=""/> </joined-class>
将子类继承属性与新增属性映射到一张表中,该表为子类独有。
<class name="父类"abstract="true">//由于只为子类建立表,不为父类建立表,因此将父类定义为abstract <id name="id"> <generator class="assigned"/>//主键由程序负责生成 </id> -------------------xxxxxxxxxxxxxx----------------------- <union-subclass name="子类"table="子表名"> <property name="子类新增属性名"/> </union-subclass> </class>
Hibernate Query Language,一种彻底面向对象的查询语言,查询范围是对象的集合,返回结果也是对象的集合。
select obj.attr from Object obj where obj.attr xxxx group by xxxx having order by xxx;
String hql="from xxxx"; Query q=session.createQuery(hql); List list=q.list();//以List集合返回查询结果
Hibernate参数绑定机制提供了两种占位符,不一样的占位符有不一样的操做:
Query.setParameter(0,"具体指");
Query.setParameter("parameterName","具体指");
hql语句中支持limit关键字,分页查询能够经过如下方式实现:
String hql="不含hql";
Query q=session.createQuery(hql).setFirst(beginIndex).setMaxResults(length);
若是修改了hql语句,不但愿从新编译java文件,能够预先把hql语句定义在映射文件中,在java代码中经过名称调用hql语句,这种查询方式叫作命名查询。实现方式:
<class> xxxxxxxxxxxxxxx </class> <query name="namedQuery">hql</> Query q=session.getNamedQuery("namedQuery");
在配置文件中注册实体类 :
<mapping class="实体类全限定性类名" />
经常使用注解: