Hibernate
EJB:企业级JavaBean 现在被spring代替了。
JBoss:web服务器,和tomcat一样
Hibernate的优缺点:
优点:
简化了jdbc繁琐的编码
Sessiong session=HibernateUtil.currentSession();
Query query = session.createQuery("from User");
List<User> users=(List<User>)query.list();
对面向特性支持良好
可移植性好
缺点:
不适用sql优化;
不适合大规模的批量数据处理
与mybatis的比较
1.相对于MyBatis的ORM实现,Hibernate的orm实现更加完善,提供了状态管理,级联操作等功能。
2.完全面向对象,语句与数据库无关,开发者无需关注SQL的生成,开发简单,便于修改,移植性好。
3.mybatis直接使用SQL语句,不同数据库之间会有差异,修改工作量大,可移植性差。
3.mybatis是半自动的,Mapper接口方法和SQL映射,使用灵活性更高。
4.Hibernate对于关系模型设计不合理,不规范的系统不适用。
5.不考虑缓存的情况下,mybatis执行效率更高。
6.mybatis,不同的数据库要定义不同的SQL映射文件,无法做到与数据库无关,Hibernate数据库无关性比较好。
6.hibernate没有动态SQL
半自动和全自动的区别
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以是全自动的
Mybatis在查询关联对象或关联集合对象时,需要手动编写SQL来完成,所以称之为半自动ORM映射工具
使用Hibernate的步骤
1.下载并部署jar文件[http://hibernate.org],推荐使用3和5版本
hibernate3.jar;
lib\required目录下的jar包
lib\jpa\hibernate-jpa-2.0-api-1.0.1.final.jar; //jpa:Java持久层api
数据库驱动包
2.hibernate核心配置文件
<!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 name="dev">
<!-- 各种配置信息 -->
<!-- 连接数据库的四个连接信息 -->
<property name="connection.url">jdbc:oracle:thin:@localhost:1521:ORCL</property>
<property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="connection.username">scott</property>
<property name="connection.password">tiger</property>
<!-- 数据库方言:dialect;配置对应数据库方言的全类名:按具体数据库生成SQL语句 -->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- SQL日志的输出配置 -->
<!-- 是否输出SQL -->
<property name="show_sql">true</property>
<!-- 是否格式化SQL:格式化就是给SQL语句输出是添加空格,换行之类的东西 -->
<property name="format_sql">true</property>
<!-- 管理连接会话: 线程-->
<property name="current_session_context_class">thread</property>
<!--导入映射文件:
resource:映射文件的全路径
class:使用注解时,实体类的全类名
jar
package
-->
<mapping resource="cn/many/to/many/pojo/Meaber.hbm.xml"/>
</session-factory>
</hibernate-configuration>
3.实体类和映射文件
映射文件(*.hbm.xml)用来映射实体类和数据库表;
实体类:Emp.java 实体类尽量实现(implements) Serializable接口(缓存)
protobuf(用的比较多的一种第三方序列化组件)
对象——>序列化——>文件
序列化:Object ————> byte[]
反序列化:byte[] ————> Object
映射文件:Emp.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- package:实体类所在包名,(可以不设置,但映射实体类就需要全类名) -->
<hibernate-mapping package="cn.bdqn.pojo">
<!-- 实体类映射表 -->
<!-- name:实体类类名; table:映射的表名 -->
<class name="Dept" table="DEPT">
<!-- 映射实体类的属性和表的字段 -->
<!-- 主键字段用ID标签 -->
<id name="deptNo" column="deptno" type="integer">
<!-- 主键生成策略 :assigned:主键值自己设置-->
<generator class="assigned"></generator>
</id>
<!-- 普通字段使用property -->
<property name="dname" column="dname" type="string"></property>
<property name="loc" column="loc" type="string"></property>
</class>
</hibernate-mapping>
4.测试:
工具类:
private static SessionFactory sessionFactory;
static{
Configuration configuration=new Configuration().configure("hibernate.cfg.xml");
sessionFactory=configuration.buildSessionFactory();
}
public static Session openSession(){
return sessionFactory.openSession();
}
public static void closeSession(Session session){
session.close();
}
通过api执行增删改查
session.get(class,id);
session.update(entity); //可以不写
session.delete(entity);
session.save(entity);
注意:如果configure的参数是fil对象那么给定的是绝对路径的文件;
区别:
sessionFactory.openSession();//新打开一次会话
sessionFactory.getCurrentSession();//获取当前线程管理的那个会话(这个会话由当前线程打开和关闭),调用者不要关闭,一般用于处理事务时用的多;
主键生成策略(Insert)
1.native:框架自动判断数据库是支持auto_increment还是序列;支持哪种就用哪种。
<generator class="native">
<param name="sequence">序列名</param>
</generator>
2.sequence:序列生成主键值;
针对于支持序列的数据库(Oracle,DB2等);
<generator class="sequence">
<param name="sequence">序列名</param>
</generator>
不会造成拆表以后主键重复问题
3.identity:自增长auto_increment;
针对于支持auto_increment的数据库(Mysql,sql Server,DB2等)
4.assigned(默认):在Insert要自己提供主键值:
5.increament:自增长(针对整数类型),hibernate框架提供的自增长,与数据库无关。
原理:使用select max(id) 查询出表中的最大ID,然后加1作为新ID,
缺点:会在大数据量拆表后出现主键重复问题
<generator class="increment"></generator>
Hibernate中Java对象的3种状态
瞬时状态(Transient):
java对象没有主键值(或者主键值无效),并且不跟数据库关联(session关了).
瞬时状态的对象:一般可以做save,变为持久状态。
持久状态(Persistent):
与session关联,被session关联,有有效的主键值,并且跟数据关联(session)。
持久的对象,一般可以delete变为临时态;或者关闭session变为游离态。
游离状态(Detached)
又称托管状态,脱离与其关联的session的管理。
有有效的主键值,但不跟数据库关联(session关了).
游离的对象,一般可以做update变为持久态;或者delete变为临时状态。
sequenceDiagram
瞬时状态->>持久状态: save(),saveOrUpdate(),get(),load();
持久状态->>瞬时状态: delete();
持久状态->>游离状态: evict(),clear(),close();
游离状态->>持久状态: update(),saveOrUpdate();
游离状态->>瞬时状态: delete();
关于:saveOrUpdate方法
saveOrUpdate方法既可以做save又可以update;
判断规则:
看对象状态,如果是游离态,就是update;
如果是瞬时态,就是save.
持久态对象的特点:持久态对象修改以后,只要提交(刷缓存),就会更新到数据库。
持久态对象跟数据库管连得本质:在session中有这个对象的缓存(提交事务有刷缓存的功能)。
还有个方法也能做修改和添加:session.merge();:修改的是副本返回值是Object;
跟saveOrUpdate的区别:saveOrUpdate返回值为void,merge返回值是object。
merge安全方面比较好,因为它操作的是副本,把副本变成持久状态
HQL
实体类名和表名必须一样且大小写相同;
自己定义HQL语句;
String hql="HQl语句";
查询:Query session.createQuery(hql);
获取查询结果:query.list(); query.iterate();
参数绑定:按参数位置绑定,使用占位符'?'
按参数名称绑定,命名以':'开头
多个参数
可以封装为动态数组(Object ... obj)
但hql需要用占位符
赋值2种方法:
1.使用for循环
query.setParameter(index,obj[index])方法赋值
2.使用自带类型赋值(setParameters(数组名,每个占位符对应的类型,hibernate封装好的类型))
query.setParameters(obj,new Type[]{new StringType(),new IntegerType()});
可以封装为实体类:hql中的参数名称就必须和实体类中的属性名相同;
赋值:query.setProperties(实体类对象);
可以封装成Map:
map的键对应hql中的参数名
赋值:query.setProperties(map对象);
分页
fromIndex:当前页
pageSize:每页显示的条数
query.setFirstResult(pageSize*(fromIndex-1));
query.setMaxResults(pageSize);
投影查询
带select子句查询
如果hql不带select子句,默认查询表中所有字段,并且把查询结果中的每一行都自动封装
如果hql中带select子句,自己指定要查询那些子段,那么查询结果就不自动封装了。
注意:select 子句中不能有*或者数字,应为实体中没有对应的,必须用实体中有的,或者可以给from 表起别名,然后select 中直接用别名
投影查询:查询结果中的每一行的每一个字段值都是一个object;
select 属性1,属性2,属性3,属性4 from 实体;————>查询多个字段,那么每一行就是一个Obejct数组
query.list();————>list<Object[]>
query.uniqueResult();————> Object[]
select 实体0 from 实体 ————> 查询一个字段,那么每一行就是一个Object
query.list(); ————> list<Object>
query.uniqueResult();————> Object
如果查询结果有多个字段可以封装成一个对象,定义符合的构造方法:
在hql的select中直接new 实体名(字段1,字段2,...) from 实体名 。。。
引用类型转换:没有父子关系的是不能转换的,例如:Long 和 Integer ,List<String> 和List<Object>
Hibernate框架的两级缓存
提高查询性能,本质:减少数据库的查询次数
1.一级缓存:session会话级别缓存
对象直接缓存的就是持久对象。可以看做一个Map<primaryKey(主键),Pojo(持久对象)>
持久态:一级缓存中的对象叫持久态对象。
1.1.缓存研究的两个问题:
1.1.1 存 缓存
save() saveOrUpdate() merge() update() get(load)() Hql:list() iterator() uniqueResult()
1.1.2取 缓存
get(load) update() HQL:iterator()这个方法先执行一条select语句,查询出需要的ID,然后通过ID查询出所需要的字段。
如果某种数据存在于缓存中的时候可以使用iterator,一般时候不用
取缓存:都是通过主键从一级缓存中取出其中需要的数据,如果缓存中没有这个主键那么就会到数据库中查找。
1.1.3.刷缓存
flush():把一级缓存中数据和数据库同步。
如何知道需要刷缓存:
数据库和闪存是同步的,所以一级缓存和闪存对比一下
1.1.4.清缓存
clear():清除所有缓存对象
evict():清除具体的某一个缓存对象
2.二级缓存(SessionFactory)
二级缓存的实现:Hibernate提供了对多种缓存机制的支持
需要配置:开启,默认是关闭的
类型,选择缓存服务类型配置选择的缓存服务。
默认二级缓存也是按照主键存储
但是二级缓存也可以配置支持查询缓存(语句缓存,按照语句存储)。
缓存都是缓存到缓存服务中,Hibernate只提供二级缓存的连接
缓存服务:
EhCache
OSCache
JbossTreeCache
SwarmCache
HashTable
hibernate提供的二级缓存类型
CacheProvider
EhCacheProvider
OSCacheProvider
TreeCacheProvider
SwarmCacheProvider
HashTable
HashTable是jdk就提供的
配置二级缓存
Hibernate的核心配置文件配置:
在映射文件配置:
使用二级缓存
在查询时将Query的setCacheable(true)方法设置为true;
用主键查的时候用不上二级缓存。
Hibernate中一下4点是常常放到二级缓存中的:
1.很少被修改的数据
2.不是很重要的数据,允许出现偶尔并发的数据
3.不会被并发访问的数据
4.常亮数据
Hibernate的逆向工程
hibernate 逆向工程可以帮我们生成:hibernate.cfg.xml
实体类
映射文件
dao
MyEclipse自带hibernate 逆向工程插件。
步骤:先用MyEclipse连接数据库
导入所需的包
关联映射
一对一:<one-to-one>
多对一:<many-to-one>
<many-to-one name="dept" column="DEPTNO" class="cn.bdqn.pojo.Dept"></many-to-one>
name:持久化类对应的属性名
column:持久化类对应的表的外键
class:持久化类的属性的类型
一对多:<one-to-many>
-- 一对多使用set集合
<set name="emps">
<!-- 设置关联字段名 -->
<key column="deptno"></key>
<!-- 一对多:class:关联实体的类名-->
<one-to-many class="cn.hib.pojo.Emp"/>
</set>
-- 一对多使用list集合
<list name="streets" lazy="true">
<key column="DISTRICT_ID"></key>
<index column="id"></index>
<one-to-many class="cn.bdqn.pojo.Street"/>
</list>
name:实体中对应的属性
key中的column:关联的外键名
index column:表示按那个列来进行排序
class:表示关联的那个实体类名
注意:双向关联,以上两个关联关系都映射;
多对多(三张表):
两种方式 1.拆成两个一对多(多对一)
2.直接映射多对多(没有直接关系的两张表)<many-to-many>
懒加载:lazy属性
true:表示是懒加载,在输出所关联的实体时加载出它的数据
false:表示立即加载,在输出自己的数据时,所关联的数据也会进行同步加载。
extra:表示增强延迟加载策略。
多对一关联查询策略:
lazy属性:proxy:默认值,延迟加载
no-proxy:无代理延迟加载
false:立即加载
元素的lazy属性或者一对多
lazy属性值 | 加载策略 |
---|---|
true | 默认值,延迟加载 |
false | 立即加载 |
extra | 增强延迟加载 |
设置多对一关联的加载策略
lazy属性 | 加载策略 |
---|---|
proxy | 默认值,延迟加载 |
no-proxy | 无代理延迟加载 |
false | 立即加载 |
Hibernate关联映射
关联关系
类与类之间最普通的关系就是关联关系,而且关联是有方向的。
以部门和员工为例,一个部门下有多个员工,而一个员工只能在一个部门下,从员工到部门就是多对一关联
Hibernate关联映射的作用
避免了在对象模型和关系数据模型之间的切换
缺陷
hibernate不是适合数据链比较多的操作,比如删除外键的关联对象,他要一条一条的删除,效率不高。
建立单项多对一关联关系
<many-to-one>
属性:
name:设定关联类对应的属性名;
column:设定关联类的属性对应的表的外键;
class:设定关联类的属性类型;
建立双向一对多关联关系
- 当类与类之间建立了关联,就可以方便的从一个对象导航到另一个对象,或者通过集合导航到另一组对象。
- 在面向对象于洋编写的程序中,通过关联关系从一个对象导航到另一个对象显然比通过编码到数据库中的查询更加方便,且无需额外编码,基于关联关系,在增删改查操作中还可以对相关对象实现自动化得级联处理,同样减少编码工作,提高开发效率。
set集合是无序的,list是有序的,访问s需要下标,所以用set <set name cascade inverse lazy> <key column=""> <one-to-many class /> </set> 1.key:column属性设定与所关联的持久化类相对应的表的外键 2.one-to-many:class属性设定所关联的持久化类型
双向关联关系下的增删改操作
关联关系除了可以通过对象间的导航实现相关的对象的自动检索外,还可以在对象的增删改操作中,对相关对象实现自动化得级联处理,而无需人工继续相关编码,从而提高效率。
级联的操作细节可以在持久化类的映射文件中通过cascade属性和inverse属性进行控制。
- cascade属性
cascade属性值 | 描述 |
---|---|
none | 当session对象操作当前对象时,忽略其他的关联对象,cascade的默认值。 |
save-update | 通过session的save(),update()和saveOrUpdate()的方法,保存所有关联的瞬时状态的对象 |
delete | 当通过session的delete()方法操作时,会级联删除所有关联的对象 |
all | 包含所有的行为,处于安全性的考虑,一般避免使用 |
marge | 执行session的marge() 方法 |
<set name=“” cascade = "save-update,delete,merge"> //cascade属性可以赋多个,用逗号隔开
cascade 建议用在一对多或者多对一,一的一方,避免不必要的麻烦
- 元素的inverse属性
"inverse" 意为反转,在Hibernate中,inverse属性指定了关联关系中的方向。 inverse属性有两个值,即true和false,默认是false。 就是不反转,就是不交出维护外键的权利, 因此会执行相关对象关联的外键的HQL语句,从而保证数据的可靠性。 如果设置成true,则不会在执行相关的修改外键的hql语句,在编码中必须建立对象的双向关联关系。
建立多对多关系
多对多关系除了两张"d多"方的表之外,还需要一张关系表,通过外键分别引用两张"多"方的主键来实现多对多的关联。
<set name="" table="第三方表" cascade="merge,save-update">
<key column="对应第三方关系型表的外键">
<many-to-many class="对应持久化类" column="对应第三方关系表的外键">
</set>
注意事项:
cascade="merge,save-update"是合理的,不建议吧cascade属性设置为"all"和"delete",如果删除一个项目对象时,级联会删除所有与他相关的信息,会破会数据库的完整性
1.双向关联时,必须有一方放弃控制反转权,即设置inverse="true"
2.保存时,一定要保存有控制权的一方。
多对多的总结:
1. 多对多都是set,并且使用set时new一个HashSet<>()避免空指针异常
2. 通过第二张"关系表"映射
3. 需要把一方的inverse设置为true,放弃维护外键的权利
4. 可以分解成两个一对多的映射,这种情况出现在第三张关系表除了外键列,还有其他的业务字段需要使用时。
延迟加载
立即加载存在两大不足
1.会执行不必要的查询,影响查询性能。
2.可能会加载大量不需要的对象,增加系统开销,浪费内存空间
Hibernate在ORM文件中使用lazy属性配置加载策略,并且可以分为类级和关联级两个级别,分别进行控制。
- Session和list()和get()方法都是立即执行,不受lazy(懒加载)的影响
级别 | Lazy属性取值 |
---|---|
类级别 | 元素中lazy的可选值true(延迟加载)和false(立即加载)。默认为true,推荐使用默认值. |
一对多和多对多关联级别 | 元素中的lazy可选值为true(延迟加载)和extra(增强延迟加载)和false(立即加载),默认值为true,推荐使用extra(增强延迟加载) |
多对一关联级别 | 元素lazy属性的可选值为proxy(延迟加载)和no-proxxy(无代理延迟加载)和false(立即加载),默认值proxy(延迟加载),推荐使用默认值(不设置)。 |
注意:选择no-proxy时需要笔译期间进行字节码增强操作,否则和proxy效果一样。
类级别的查询策略
Lazy(懒加载)的控制权大于load()方法。
<class lazy="false">此时load()访问效果等同于get().
如果程序加载一个持久化对象目的时为了访问它的属性,则可以立即加载;如果只是获取它的引用,则可以采用延迟加载。
一对多和多对多的查询策略
推荐使用<set lazy="extra">增强延迟加载,如果使用延迟加载,那么在访问一个集合的个数size()属性时,Hibernate会查询出所欲的集合元素,从而浪费资源。
多对一的查询策略
在<many-to-one>中设置lazy属性,一般推荐使用默认值,也就是proxy,懒加载,也就是延迟加载,用的时候在加载。
Open Session In View模式
Open Session in View模式是为了解决一些数据在延迟加载(lazy懒加载)时,会话(Session)已经关闭而引发的错误。
这个模式的主要思想是:
在用户进行每次请求的时候,始终保持有一个Session对象处于开启状态。
Open Session In View模式的具体实现有以下三个步骤:
- 把Session 绑定到当前线程,要保证每次的请求中只有一个Session对象。Dao层次的HibernateUtil.currentSession()方法使用SessionFactory的getCurrentSession()方法获得Session,可以保证每次的请求只有一个Session对象存在。
- 用Fiter过滤器在请求到达时打开Session,在页面生成完毕时关闭Session。
- 调整业务层代码,删除和会话及事务管理的相关代码,仅保留业务逻辑代码。
Hibernate
EJB:企业级JavaBean 现在被spring代替了。
JBoss:web服务器,和tomcat一样
Hibernate的优缺点:
优点:
简化了jdbc繁琐的编码
Sessiong session=HibernateUtil.currentSession();
Query query = session.createQuery("from User");
List<User> users=(List<User>)query.list();
对面向特性支持良好
可移植性好
缺点:
不适用sql优化;
不适合大规模的批量数据处理
与mybatis的比较
1.相对于MyBatis的ORM实现,Hibernate的orm实现更加完善,提供了状态管理,级联操作等功能。
2.完全面向对象,语句与数据库无关,开发者无需关注SQL的生成,开发简单,便于修改,移植性好。
3.mybatis直接使用SQL语句,不同数据库之间会有差异,修改工作量大,可移植性差。
3.mybatis是半自动的,Mapper接口方法和SQL映射,使用灵活性更高。
4.Hibernate对于关系模型设计不合理,不规范的系统不适用。
5.不考虑缓存的情况下,mybatis执行效率更高。
6.mybatis,不同的数据库要定义不同的SQL映射文件,无法做到与数据库无关,Hibernate数据库无关性比较好。
6.hibernate没有动态SQL
半自动和全自动的区别
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以是全自动的
Mybatis在查询关联对象或关联集合对象时,需要手动编写SQL来完成,所以称之为半自动ORM映射工具
使用Hibernate的步骤
1.下载并部署jar文件[http://hibernate.org],推荐使用3和5版本
hibernate3.jar;
lib\required目录下的jar包
lib\jpa\hibernate-jpa-2.0-api-1.0.1.final.jar; //jpa:Java持久层api
数据库驱动包
2.hibernate核心配置文件
<!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 name="dev">
<!-- 各种配置信息 -->
<!-- 连接数据库的四个连接信息 -->
<property name="connection.url">jdbc:oracle:thin:@localhost:1521:ORCL</property>
<property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="connection.username">scott</property>
<property name="connection.password">tiger</property>
<!-- 数据库方言:dialect;配置对应数据库方言的全类名:按具体数据库生成SQL语句 -->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- SQL日志的输出配置 -->
<!-- 是否输出SQL -->
<property name="show_sql">true</property>
<!-- 是否格式化SQL:格式化就是给SQL语句输出是添加空格,换行之类的东西 -->
<property name="format_sql">true</property>
<!-- 管理连接会话: 线程-->
<property name="current_session_context_class">thread</property>
<!--导入映射文件:
resource:映射文件的全路径
class:使用注解时,实体类的全类名
jar
package
-->
<mapping resource="cn/many/to/many/pojo/Meaber.hbm.xml"/>
</session-factory>
</hibernate-configuration>
3.实体类和映射文件
映射文件(*.hbm.xml)用来映射实体类和数据库表;
实体类:Emp.java 实体类尽量实现(implements) Serializable接口(缓存)
protobuf(用的比较多的一种第三方序列化组件)
对象——>序列化——>文件
序列化:Object ————> byte[]
反序列化:byte[] ————> Object
映射文件:Emp.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- package:实体类所在包名,(可以不设置,但映射实体类就需要全类名) -->
<hibernate-mapping package="cn.bdqn.pojo">
<!-- 实体类映射表 -->
<!-- name:实体类类名; table:映射的表名 -->
<class name="Dept" table="DEPT">
<!-- 映射实体类的属性和表的字段 -->
<!-- 主键字段用ID标签 -->
<id name="deptNo" column="deptno" type="integer">
<!-- 主键生成策略 :assigned:主键值自己设置-->
<generator class="assigned"></generator>
</id>
<!-- 普通字段使用property -->
<property name="dname" column="dname" type="string"></property>
<property name="loc" column="loc" type="string"></property>
</class>
</hibernate-mapping>
4.测试:
工具类:
private static SessionFactory sessionFactory;
static{
Configuration configuration=new Configuration().configure("hibernate.cfg.xml");
sessionFactory=configuration.buildSessionFactory();
}
public static Session openSession(){
return sessionFactory.openSession();
}
public static void closeSession(Session session){
session.close();
}
通过api执行增删改查
session.get(class,id);
session.update(entity); //可以不写
session.delete(entity);
session.save(entity);
注意:如果configure的参数是fil对象那么给定的是绝对路径的文件;
区别:
sessionFactory.openSession();//新打开一次会话
sessionFactory.getCurrentSession();//获取当前线程管理的那个会话(这个会话由当前线程打开和关闭),调用者不要关闭,一般用于处理事务时用的多;
主键生成策略(Insert)
1.native:框架自动判断数据库是支持auto_increment还是序列;支持哪种就用哪种。
<generator class="native">
<param name="sequence">序列名</param>
</generator>
2.sequence:序列生成主键值;
针对于支持序列的数据库(Oracle,DB2等);
<generator class="sequence">
<param name="sequence">序列名</param>
</generator>
不会造成拆表以后主键重复问题
3.identity:自增长auto_increment;
针对于支持auto_increment的数据库(Mysql,sql Server,DB2等)
4.assigned(默认):在Insert要自己提供主键值:
5.increament:自增长(针对整数类型),hibernate框架提供的自增长,与数据库无关。
原理:使用select max(id) 查询出表中的最大ID,然后加1作为新ID,
缺点:会在大数据量拆表后出现主键重复问题
<generator class="increment"></generator>
Hibernate中Java对象的3种状态
瞬时状态(Transient):
java对象没有主键值(或者主键值无效),并且不跟数据库关联(session关了).
瞬时状态的对象:一般可以做save,变为持久状态。
持久状态(Persistent):
与session关联,被session关联,有有效的主键值,并且跟数据关联(session)。
持久的对象,一般可以delete变为临时态;或者关闭session变为游离态。
游离状态(Detached)
又称托管状态,脱离与其关联的session的管理。
有有效的主键值,但不跟数据库关联(session关了).
游离的对象,一般可以做update变为持久态;或者delete变为临时状态。
sequenceDiagram
瞬时状态->>持久状态: save(),saveOrUpdate(),get(),load();
持久状态->>瞬时状态: delete();
持久状态->>游离状态: evict(),clear(),close();
游离状态->>持久状态: update(),saveOrUpdate();
游离状态->>瞬时状态: delete();
关于:saveOrUpdate方法
saveOrUpdate方法既可以做save又可以update;
判断规则:
看对象状态,如果是游离态,就是update;
如果是瞬时态,就是save.
持久态对象的特点:持久态对象修改以后,只要提交(刷缓存),就会更新到数据库。
持久态对象跟数据库管连得本质:在session中有这个对象的缓存(提交事务有刷缓存的功能)。
还有个方法也能做修改和添加:session.merge();:修改的是副本返回值是Object;
跟saveOrUpdate的区别:saveOrUpdate返回值为void,merge返回值是object。
merge安全方面比较好,因为它操作的是副本,把副本变成持久状态
HQL
实体类名和表名必须一样且大小写相同;
自己定义HQL语句;
String hql="HQl语句";
查询:Query session.createQuery(hql);
获取查询结果:query.list(); query.iterate();
参数绑定:按参数位置绑定,使用占位符'?'
按参数名称绑定,命名以':'开头
多个参数
可以封装为动态数组(Object ... obj)
但hql需要用占位符
赋值2种方法:
1.使用for循环
query.setParameter(index,obj[index])方法赋值
2.使用自带类型赋值(setParameters(数组名,每个占位符对应的类型,hibernate封装好的类型))
query.setParameters(obj,new Type[]{new StringType(),new IntegerType()});
可以封装为实体类:hql中的参数名称就必须和实体类中的属性名相同;
赋值:query.setProperties(实体类对象);
可以封装成Map:
map的键对应hql中的参数名
赋值:query.setProperties(map对象);
分页
fromIndex:当前页
pageSize:每页显示的条数
query.setFirstResult(pageSize*(fromIndex-1));
query.setMaxResults(pageSize);
投影查询
带select子句查询
如果hql不带select子句,默认查询表中所有字段,并且把查询结果中的每一行都自动封装
如果hql中带select子句,自己指定要查询那些子段,那么查询结果就不自动封装了。
注意:select 子句中不能有*或者数字,应为实体中没有对应的,必须用实体中有的,或者可以给from 表起别名,然后select 中直接用别名
投影查询:查询结果中的每一行的每一个字段值都是一个object;
select 属性1,属性2,属性3,属性4 from 实体;————>查询多个字段,那么每一行就是一个Obejct数组
query.list();————>list<Object[]>
query.uniqueResult();————> Object[]
select 实体0 from 实体 ————> 查询一个字段,那么每一行就是一个Object
query.list(); ————> list<Object>
query.uniqueResult();————> Object
如果查询结果有多个字段可以封装成一个对象,定义符合的构造方法:
在hql的select中直接new 实体名(字段1,字段2,...) from 实体名 。。。
引用类型转换:没有父子关系的是不能转换的,例如:Long 和 Integer ,List<String> 和List<Object>
Hibernate框架的两级缓存
提高查询性能,本质:减少数据库的查询次数
1.一级缓存:session会话级别缓存
对象直接缓存的就是持久对象。可以看做一个Map<primaryKey(主键),Pojo(持久对象)>
持久态:一级缓存中的对象叫持久态对象。
1.1.缓存研究的两个问题:
1.1.1 存 缓存
save() saveOrUpdate() merge() update() get(load)() Hql:list() iterator() uniqueResult()
1.1.2取 缓存
get(load) update() HQL:iterator()这个方法先执行一条select语句,查询出需要的ID,然后通过ID查询出所需要的字段。
如果某种数据存在于缓存中的时候可以使用iterator,一般时候不用
取缓存:都是通过主键从一级缓存中取出其中需要的数据,如果缓存中没有这个主键那么就会到数据库中查找。
1.1.3.刷缓存
flush():把一级缓存中数据和数据库同步。
如何知道需要刷缓存:
数据库和闪存是同步的,所以一级缓存和闪存对比一下
1.1.4.清缓存
clear():清除所有缓存对象
evict():清除具体的某一个缓存对象
2.二级缓存(SessionFactory)
二级缓存的实现:Hibernate提供了对多种缓存机制的支持
需要配置:开启,默认是关闭的
类型,选择缓存服务类型配置选择的缓存服务。
默认二级缓存也是按照主键存储
但是二级缓存也可以配置支持查询缓存(语句缓存,按照语句存储)。
缓存都是缓存到缓存服务中,Hibernate只提供二级缓存的连接
缓存服务:
EhCache
OSCache
JbossTreeCache
SwarmCache
HashTable
hibernate提供的二级缓存类型
CacheProvider
EhCacheProvider
OSCacheProvider
TreeCacheProvider
SwarmCacheProvider
HashTable
HashTable是jdk就提供的
配置二级缓存
Hibernate的核心配置文件配置:
在映射文件配置:
使用二级缓存
在查询时将Query的setCacheable(true)方法设置为true;
用主键查的时候用不上二级缓存。
Hibernate中一下4点是常常放到二级缓存中的:
1.很少被修改的数据
2.不是很重要的数据,允许出现偶尔并发的数据
3.不会被并发访问的数据
4.常亮数据
Hibernate的逆向工程
hibernate 逆向工程可以帮我们生成:hibernate.cfg.xml
实体类
映射文件
dao
MyEclipse自带hibernate 逆向工程插件。
步骤:先用MyEclipse连接数据库
导入所需的包
关联映射
一对一:<one-to-one>
多对一:<many-to-one>
<many-to-one name="dept" column="DEPTNO" class="cn.bdqn.pojo.Dept"></many-to-one>
name:持久化类对应的属性名
column:持久化类对应的表的外键
class:持久化类的属性的类型
一对多:<one-to-many>
-- 一对多使用set集合
<set name="emps">
<!-- 设置关联字段名 -->
<key column="deptno"></key>
<!-- 一对多:class:关联实体的类名-->
<one-to-many class="cn.hib.pojo.Emp"/>
</set>
-- 一对多使用list集合
<list name="streets" lazy="true">
<key column="DISTRICT_ID"></key>
<index column="id"></index>
<one-to-many class="cn.bdqn.pojo.Street"/>
</list>
name:实体中对应的属性
key中的column:关联的外键名
index column:表示按那个列来进行排序
class:表示关联的那个实体类名
注意:双向关联,以上两个关联关系都映射;
多对多(三张表):
两种方式 1.拆成两个一对多(多对一)
2.直接映射多对多(没有直接关系的两张表)<many-to-many>
懒加载:lazy属性
true:表示是懒加载,在输出所关联的实体时加载出它的数据
false:表示立即加载,在输出自己的数据时,所关联的数据也会进行同步加载。
extra:表示增强延迟加载策略。
多对一关联查询策略:
lazy属性:proxy:默认值,延迟加载
no-proxy:无代理延迟加载
false:立即加载
元素的lazy属性或者一对多
lazy属性值 | 加载策略 |
---|---|
true | 默认值,延迟加载 |
false | 立即加载 |
extra | 增强延迟加载 |
设置多对一关联的加载策略
lazy属性 | 加载策略 |
---|---|
proxy | 默认值,延迟加载 |
no-proxy | 无代理延迟加载 |
false | 立即加载 |
Hibernate关联映射
关联关系
类与类之间最普通的关系就是关联关系,而且关联是有方向的。
以部门和员工为例,一个部门下有多个员工,而一个员工只能在一个部门下,从员工到部门就是多对一关联
Hibernate关联映射的作用
避免了在对象模型和关系数据模型之间的切换
缺陷
hibernate不是适合数据链比较多的操作,比如删除外键的关联对象,他要一条一条的删除,效率不高。
建立单项多对一关联关系
<many-to-one>
属性:
name:设定关联类对应的属性名;
column:设定关联类的属性对应的表的外键;
class:设定关联类的属性类型;
建立双向一对多关联关系
- 当类与类之间建立了关联,就可以方便的从一个对象导航到另一个对象,或者通过集合导航到另一组对象。
- 在面向对象于洋编写的程序中,通过关联关系从一个对象导航到另一个对象显然比通过编码到数据库中的查询更加方便,且无需额外编码,基于关联关系,在增删改查操作中还可以对相关对象实现自动化得级联处理,同样减少编码工作,提高开发效率。
set集合是无序的,list是有序的,访问s需要下标,所以用set <set name cascade inverse lazy> <key column=""> <one-to-many class /> </set> 1.key:column属性设定与所关联的持久化类相对应的表的外键 2.one-to-many:class属性设定所关联的持久化类型
双向关联关系下的增删改操作
关联关系除了可以通过对象间的导航实现相关的对象的自动检索外,还可以在对象的增删改操作中,对相关对象实现自动化得级联处理,而无需人工继续相关编码,从而提高效率。
级联的操作细节可以在持久化类的映射文件中通过cascade属性和inverse属性进行控制。
- cascade属性
cascade属性值 | 描述 |
---|---|
none | 当session对象操作当前对象时,忽略其他的关联对象,cascade的默认值。 |
save-update | 通过session的save(),update()和saveOrUpdate()的方法,保存所有关联的瞬时状态的对象 |
delete | 当通过session的delete()方法操作时,会级联删除所有关联的对象 |
all | 包含所有的行为,处于安全性的考虑,一般避免使用 |
marge | 执行session的marge() 方法 |
<set name=“” cascade = "save-update,delete,merge"> //cascade属性可以赋多个,用逗号隔开
cascade 建议用在一对多或者多对一,一的一方,避免不必要的麻烦
- 元素的inverse属性
"inverse" 意为反转,在Hibernate中,inverse属性指定了关联关系中的方向。 inverse属性有两个值,即true和false,默认是false。 就是不反转,就是不交出维护外键的权利, 因此会执行相关对象关联的外键的HQL语句,从而保证数据的可靠性。 如果设置成true,则不会在执行相关的修改外键的hql语句,在编码中必须建立对象的双向关联关系。
建立多对多关系
多对多关系除了两张"d多"方的表之外,还需要一张关系表,通过外键分别引用两张"多"方的主键来实现多对多的关联。
<set name="" table="第三方表" cascade="merge,save-update">
<key column="对应第三方关系型表的外键">
<many-to-many class="对应持久化类" column="对应第三方关系表的外键">
</set>
注意事项:
cascade="merge,save-update"是合理的,不建议吧cascade属性设置为"all"和"delete",如果删除一个项目对象时,级联会删除所有与他相关的信息,会破会数据库的完整性
1.双向关联时,必须有一方放弃控制反转权,即设置inverse="true"
2.保存时,一定要保存有控制权的一方。
多对多的总结:
1. 多对多都是set,并且使用set时new一个HashSet<>()避免空指针异常
2. 通过第二张"关系表"映射
3. 需要把一方的inverse设置为true,放弃维护外键的权利
4. 可以分解成两个一对多的映射,这种情况出现在第三张关系表除了外键列,还有其他的业务字段需要使用时。
延迟加载
立即加载存在两大不足
1.会执行不必要的查询,影响查询性能。
2.可能会加载大量不需要的对象,增加系统开销,浪费内存空间
Hibernate在ORM文件中使用lazy属性配置加载策略,并且可以分为类级和关联级两个级别,分别进行控制。
- Session和list()和get()方法都是立即执行,不受lazy(懒加载)的影响
级别 | Lazy属性取值 |
---|---|
类级别 | 元素中lazy的可选值true(延迟加载)和false(立即加载)。默认为true,推荐使用默认值. |
一对多和多对多关联级别 | 元素中的lazy可选值为true(延迟加载)和extra(增强延迟加载)和false(立即加载),默认值为true,推荐使用extra(增强延迟加载) |
多对一关联级别 | 元素lazy属性的可选值为proxy(延迟加载)和no-proxxy(无代理延迟加载)和false(立即加载),默认值proxy(延迟加载),推荐使用默认值(不设置)。 |
注意:选择no-proxy时需要笔译期间进行字节码增强操作,否则和proxy效果一样。
类级别的查询策略
Lazy(懒加载)的控制权大于load()方法。
<class lazy="false">此时load()访问效果等同于get().
如果程序加载一个持久化对象目的时为了访问它的属性,则可以立即加载;如果只是获取它的引用,则可以采用延迟加载。
一对多和多对多的查询策略
推荐使用<set lazy="extra">增强延迟加载,如果使用延迟加载,那么在访问一个集合的个数size()属性时,Hibernate会查询出所欲的集合元素,从而浪费资源。
多对一的查询策略
在<many-to-one>中设置lazy属性,一般推荐使用默认值,也就是proxy,懒加载,也就是延迟加载,用的时候在加载。
Open Session In View模式
Open Session in View模式是为了解决一些数据在延迟加载(lazy懒加载)时,会话(Session)已经关闭而引发的错误。
这个模式的主要思想是:
在用户进行每次请求的时候,始终保持有一个Session对象处于开启状态。
Open Session In View模式的具体实现有以下三个步骤:
- 把Session 绑定到当前线程,要保证每次的请求中只有一个Session对象。Dao层次的HibernateUtil.currentSession()方法使用SessionFactory的getCurrentSession()方法获得Session,可以保证每次的请求只有一个Session对象存在。
- 用Fiter过滤器在请求到达时打开Session,在页面生成完毕时关闭Session。
- 调整业务层代码,删除和会话及事务管理的相关代码,仅保留业务逻辑代码。