1、什么是Hibernate,好处是什么?
答:
1)Hibernate是一个操作数据库的框架,实现了对JDBC的封装;
2)Hibernate是一个ORM(对象关系映射)框架,我们在写程序时 ,用的时面向对象的方法,但是在关系型数据库里,存的是一条条的数据,为了用纯面向对象的思想解决问题,所有需要将程序中的对象和数据库的记录建立起映射关系,ORM就是这样的技术,而Hibernate就是这样一个框架,以操作对象的方式操作数据库。
3)Hibernate简化了代码的编写,原生态的JDBC需要很对代码来实现操作数据库,在这里用Hibernate只需要很少的代码就可以实现。
4)使用Hibernate的基本流程是:配置实现类与数据库表的映射关系,产生sessionFactory,打开session通道拿到session对象,开启事务,完成操作,关闭session。
5)Hibernate屏蔽了数据库的差异,增强了对数据库的可移植性。
6)使用Hibernate时,先要配置hibernate.cfg.xml文件,其中配置数据库连接信息和方言等,还要为每个实体配置相应的hbm.xml文件(Hibernate的映射文件),当然,也可以采用注解编程实现映射关系,hibernate.cfg.xml文件中需要登记每个hbm.xml文件
2、Hibernate工作原理及为什么要用?
1)读取并解析配置文件 (hibernate.cfg.xml文件 需要配置数据库基本信息,数据方言,映射文件的信息)
2)读取并解析映射信息(如:customer.hbm.xml),创建SessionFactory
3)打开Sesssion
4)创建事务Transation
5)持久化操作
6)提交事务
7)关闭Session
8)关闭SesstionFactory
get和load的最大区别是,如果在缓存中没有找到相应的对象,get将会直接访问数据库并返回一个完全初始化好的对象,而这个过程有可能会涉及到多个数据库调用;而load方法在缓存中没有发现对象的情况下,只会返回一个代理对象,只有在对象getId()之外的其它方法被调用时才会真正去访问数据库,这样就能在某些情况下大幅度提高性能。
sorted collection是通过使用 Java的Comparator在内存中进行排序的
ordered collection中的排序用的是数据库的order by子句。
持久化对象的三种状态
1、transient 瞬时状态
new一个新的对象即为瞬时状态,瞬时状态和session没有关联,一个瞬时对象和数据库记录不对应。
瞬时对象--->save或saveOrUpadate---->持久对象。
2、persistent 持久状态
持久状态对象和session有关联,持久对象和数据库记录存在对应,持久对象属性值变更可以持久到数据库中。
执行Get查询可以得到一个持久对象。
持久对象--->delete删除--->瞬时对象
持久对象-->session.close(session关闭)--->托管对象
3、detached 托管状态
托管对象和session没有关联,它和瞬时对象的区别在于托管对象可能会和数据库记录存在对应。
托管对象------>update或saveOrUpdate --->持久对象
什么是一级缓存
Hibernate框架中共有两级缓存. Session级别的缓存是属于一级缓存,SessionFactory级别的缓存是二级缓存.
缓存:将数据库或硬盘中的数据,存入到内存中.当下次使用的时候可以从内存中获得,减少数据库访问次数.(优化)
一级缓存:生命周期与session一致.
在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期.
当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试图get()、 load()对象时,会判断缓存中是否存在该对象,有则返回,此时不查询数据库。没有再查询数据库
存放公有数据
1、适用场合:
1、数据不能频繁更新
2、数据能公开,私密性不是很强
2、hibernate本身并没有提供二级缓存的解决方案
3、二级缓存的实现是依赖于第三方供应商完成的
ehcache
oscache
jbosscache
swamchache
4、二级缓存的操作
1、二级缓存存在sessionFactory中
2、生命周期:与sessionFactory保持一致
3、使用二级缓存的步骤
1在hibernate.cfg.xml
<property name="cache.use_second_level_cache">true</property>
<property name="cache.provider_class"> org.hibernate.cache.EhCacheProvider </property>
2、让某一个对象进入到二级缓存中
* 在配置文件中 <class-cache usage="read-only" class="cn.itcast.hiberate.sh.domain.Classes"/>
* 在映射文件中 <cache usage="read-only"/>
3、使用 session.get/session.load
事务
A:原子性(Atomicity)
事务中包括的诸操作要么全成功,要么全失败。
B:一致性(Consistency)
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。
也就是说事务执行之前数据库数据处于正确状态,执行之后也处理正确状态,如果有些事务运行中发生故障,这些事务对数据库所做的修改有一部分已写入数据库,这时数据库就处于不正确的状态即不一致的状态。
一致性与原子性是密切相关的,如果事务没有原子性的保证,那么在发生系统故障的情况下,数据库就有可能处于不一致状态。
C:隔离性(Isolation)
一个事务的执行不能被其他事务干扰。解决多个线程并发操作事务情况,每个事务互相不影响
D:持续性/永久性(Durability)
一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
Serializable:串行化。隔离级别最高
Repeatable Read:可重复读 可解决可重复读
Read Committed:已提交数据读 可以解决脏读
Read Uncommitted:未提交数据读。隔离级别最差
- 脏读: 一个事务读取到另一个事务 未提交数据
- 可重复读: 一个事务中,先后读取两次,两次读取结果不同,读取到了另一个事务已经提交的数据,此问题针对update更新来说。
- 虚读: 一个事务,先后读取两次,结果不同,读取到了另一个事务已经提交的数据 ,此问题针对insert插入来说。
- 丢失更新: 两个事务 同时修改一条数据,后提交事务,覆盖了之前提交事务结果
在不可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现不可重复 读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会 发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。
丢失更新的解决方法:
设置锁:乐观锁和悲观锁。
乐观锁:使用版本号或时间戳来检测更新丢失,在的映射中设置 optimistic-lock=”all”可以在没有版本或者时间戳属性映射的情况下实现 版本检查,此时Hibernate将比较一行记录的每个字段的状态
行级悲观锁:Hibernate总是使用数据库的锁定机制,从不在内存中锁定对象!只要为JDBC连接指定一下隔 离级别,然后让数据库去搞定一切就够了。类LockMode 定义了Hibernate所需的不同的锁定级别:LockMode.UPGRADE,LockMode.UPGRADE_NOWAIT,LockMode.READ;
Hibernate有哪几种查询数据的方式?
答:3种:hql、条件查询QBC(QueryBy Criteria)、原生sql (通过createSQLQuery建立) OID检索 、对象导航检索
什么是SessionFactory,她是线程安全么?
答:SessionFactory 是Hibrenate单例数据存储和线程安全的,以至于可以多线程同时访问。一个SessionFactory 在启动的时候只能建立一次。SessionFactory应该包装各种单例以至于它能很简单的在一个应用代码中储存. Hibernate的Session对象是非线程安全的
双向关联产生多余的SQL语句
解决:一方放弃外键的维护 。原理:比对一级缓存和快照,不一致则更新数据库
多对多关系
多对多只保存一边 也是不可以的.,许多配置级联保存。如果建立了双向的关系 一定要有一方放弃外键维护权.
Hibernate是如何延迟加载?
答:当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。
【类级别的延迟加载】:类级别的延迟一般不修改.采用默认值即可.lazy=”true”通过一些延迟加载的方法load方法检索某个对象.这个对象是否采用延迟.
关联级别的延迟加载 通过客户关联其联系人的对象.联系人是否采用延迟.
抓取其关联对象.
fetch: 控制的是查询其关联对象的时候采用的SQL语句的格式.
* select :默认值. 发送一条select语句查询其关联对象.
* join :发送 一条迫切左外连接查询关联对象.
* subselect :发送 一条子查询查询其关联对象.
什么时候会遇到1+N的问题?
前提:Hibernate默认表与表的关联方法是fetch="select",不是fetch="join",这都是为了懒加载而准备的。
1)一对多(<set><list>) ,在1的这方,通过1条sql查找得到了1个对象,由于关联的存在 ,那么又需要将这个对象关联的集合取出,所以合集数量是n还要发出n条sql,于是本来的1条sql查询变成了1 +n条 。
2)多对一<many-to-one> ,在多的这方,通过1条sql查询得到了n个对象,由于关联的存在,也会将这n个对象对应的1 方的对象取出, 于是本来的1条sql查询变成了1 +n条 。
3)iterator 查询时,一定先去缓存中找(1条sql查集合,只查出ID),在没命中时,会再按ID到库中逐一查找, 产生1+n条SQL
怎么解决1+N 问题?
1 )lazy=true, hibernate3开始已经默认是lazy=true了;lazy=true时不会立刻查询关联对象,只有当需要关联对象(访问其属性,非id字段)时才会发生查询动作。
2)使用二级缓存, 二级缓存的应用将不怕1+N 问题,因为即使第一次查询很慢(未命中),以后查询直接缓存命中也是很快的。刚好又利用了1+N 。
3) 当然你也可以设定fetch="join",一次关联表全查出来,但失去了懒加载的特性。
如何优化Hibernate?
答:
1)使用双向一对多关联,不使用单向一对多
2)灵活使用单向一对多关联
3)不用一对一,用多对一取代
4)配置对象缓存,不使用集合缓存
5)一对多集合使用Bag,多对多集合使用Set
6)继承类使用显式多态
7)表字段要少,表关联不要怕多,有二级缓存撑腰
在数据库中条件查询速度很慢的时候,如何优化?
答:
1)建索引
2)减少表之间的关联
3)优化sql,尽量让sql很快定位数据,不要让sql做全表查询,应该走索引,把数据量大的表排在前面
4)简化查询字段,没用的字段不要,已经对返回结果的控制,尽量返回少量数据