1.什么是Hibernate?
Hibernate是数据访问层的框架,对JDBC进行了封装,是针对数据访问层提出的一套面向对象的解决方案。
hibernate允许我们直接访问实体对象,它会将这种访问自动装换为sql并执行,从而达到间接访问数据库的目的,
可以提高我们数据访问层的开发效率
2 .Hibernate框架作用
hibernate是判卜数据i万问框架(姜寺久层框架)在顶目中利用Hibemate框架可以实
现对数握库的增删改查操作,为业务层构建一个持久层原有使用JDBC+SQL万式对
数据库
操作,这种操操作有些弊湍:
- 表字段多的情况下SQL繁琐
- 在SQL中包含大量的?,要给他们赋值
- 由于数据表记录与Java对象之间存在差异将Java对象和数据表记录转换时,非常繁琐.
- 不同数据库SQL语句存在一定差异,使用了数据库特有的关键字和函数,如果更换数据库,不可复用,移植性较差
- 自动生成SQL
- 能自动的给?赋值
- 能够根据实体对象和数据库的关系,自动将结果集转换为实体对象
- 采用统一的API访问数据库,这样可移植性好
4.Hibernate体系结构Hibernate采用ORM设计思想,对JDBC进行的封装ORM:Object Relation Mapping ,即对象关系映射直接访问实体对象,然后根据对象与数据库的关系,自动的转换SQL并执行,达到访问数据库的目的。对象-->关系配置文件xml-->表/字段-->生成SQL-->结果集-->实例化对象-->赋值
1.hibernate.cfg.xml
是Hibernate的主配置文件,可以用来设置数据库连接参数以及Hibernate框架参数等。
2.实体类
是用来封装数据结果
3.hbm.xml
是映射关系文件,用来配置实体类和数据库表的关系以及类中的属性和表中字段的关系。
4.Hibernate底层API
主要是用来解析映射文件的,能够得到类与库的关系,从而自动的帮我们生成SQL并执行
5.Hibernate常用API
1.Configuration用于加载主配置文件的,同时也 加载映射关系文件,用于创建SessionFactory2.SessionFactory用于创建Session对象的3.Session是Hibernate中 的数据库连接会话,可以理解为Connection,用于进行增删改查改查操作。也可以开始一个 Transaction或创建一个Query4.Transaction用来控制事务5.Query是Hibernate中用来进行高级查询的对象
6.Hibernate使用步骤
- 导入Hibernate包,以及数据库驱动包。
2.引入Hibernate主配置文件hibernate.cfg.xml。
<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD 3.0//EN"<hibernate-configuration><session-factory><property name="dialect">org.hibernate.dialect.MySQLDialect</property><property name="connection.url">jdbc:mysql://localhost:3306/test</property><property name="connection.username">root</property><property name="connection.password">zwwy</property><property name="connection.driver_class">com.mysql.jdbc.Driver</property><property name="Connection.useUnicode">true</property><property name="connection.characterEncoding">utf8</property><property name="myeclipse.connection.profile">test</property><property name="show_sql">true</property><mapping resource="entity/user.hbm.xml" /><mapping resource="entity/role.hbm.xml" /></session-factory></hibernate-configuration>
3创建实体类。
4创建映射关系文件。
user.hbm.xml
<?xml version="1.0" encoding='UTF-8'?><!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN"<hibernate-mapping package="entity"><class name="User" table="t_user"><id name="id" column="id" type="int"><generator class="identity"/></id><property name="username" column="username" type="java.lang.String" not-null="true" /><property name="password" column="password" type="java.lang.String" /><property name="realname" column="realname" type="java.lang.String" /><property name="sex" column="sex" type="java.lang.String" /><many-to-one name="role" lazy="false" fetch="join" column="role_id" not-null="true" class="entity.Role"></many-to-one></class></hibernate-mapping>role.hbm.xml
<?xml version="1.0" encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
" http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="entity">
<class name="Role" table="t_role">
<id name="id" column="id" type="int">
<generator class="identity"/>
</id>
<property name="title" column="title" type="java.lang.String" />
<property name="action" column="action" type="java.lang.String" />
<set name="users" cascade="all" inverse="true">
<key column="role_id"></key>
<one-to-many class="entity.User"/>
</set>
</class>
</hibernate-mapping>ps :在property元素上追加属性update="false", 这样在Hibernate做更新时,不会讲这个字段拼到update语句中
在property元素上追加属性dynamic-update="true",这样在Hibernate做更新时
,会将实体对象中非空的属性拼到update语句中
5使用Hibernate常用API执行增删改查操作。
HibernateUtil.java
package util;
import org.hibernate.HibernateException;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.cfg.Configuration;
public class HibernateUtil {private static SessionFactory sf;private static ThreadLocal<Session> tl = new ThreadLocal<Session>();static {Configuration cnf = new Configuration();try {cnf.configure("/hibernate.cfg.xml");sf = cnf.buildSessionFactory();} catch (HibernateException e) {}}
public static SessionFactory getSessionFactory() {return sf;}
public static Session getSession() {Session session = tl.get();if (session == null) {session = sf.openSession();tl.set(session);}return session;}
public static void closeSession() {Session session = tl.get();if (session != null) {session.close();tl.set(null);}}}
Test.java@Testpublic void find(){Session session=HibernateUtil.getSession();User user=(User)session.get(User.class, 1);System.out.println(user.getRealname());HibernateUtil.closeSession();}
1)映射关系文件中,需要设置实体类中属性和表中字段的关系,并且要设置转换时所需的类型,即
type="java.lang.String"
2)次种映射类型的设置方式有2类
a.Java类型
如果某些类型java自带类型无法满足是,可以自己创建一个类,实现UserType接口,以此实现类来充当映射类型
b.hibernate提供的类型
整数:byte,short,integer,longjava类型 数据库字段类型java.lang.Integer number(4)
小数:float,doublejava类型 数据库字段类型java.lang.Double number(9,2)
字符:stringjava类型 数据库字段类型java.lang.String varchar2(20)
日期(年月日):datejava类型 数据库字段类型java.sql.Date date
日期(时分秒):timejava类型 数据库字段类型java.sql.Time date
时间戳( 年月日时分秒):timestampjava类型 数据库字段类型java.sql.Timestamp date
布尔:yes_nojava类型 数据库字段类型java.lang.Boolean char(1) y/n
布尔:true_falsejava类型 数据库字段类型java.lang.Boolean char(1) t/f
ps:这些类型都要小写
8 Hibernate主键生成方式
1)sequence 采用序列的方式生成主键
只是针对ORACLE数据库的
<generator class="sequence">
</generator><param name="sequence">序列名</param>
2)identity 采用数据库自增长的方式生成主键,针对MySql,SQLServer等数据库的
<generator class="identity"></generator>
3)native,根据方言来自动的选择主键生成方式,若方言是Oracle则使用sequence
,若方言是其他数据库则使用identity.
<generator class="native">
<param name="sequence">序列名</param>
4)increment,是Hibernate提供的自增长机制,不是数据库自带的自增长,有别于identity.</generator>
Hibernate提供一个组件,在需要生成ID时,该组件会查询这张表主键的最大值,然后+1,以此作为新的主键
5)assigned 采用忽略的方式来处理,即Hibernate不负责主键的生成,需要程序员手动来维护主键。<generator class="increment"></generator>注意:此种方式不推荐使用,原因是在并发量大的时候,会存在重复性问题,不安全
6)uuid/hilo ,采用uuid/hilo算法来生成一个主键,生成的主键是一个长字符串,可以保证不重复,但是没有规则
14.Hibernate 高级特性
数据库相应的主键字段应该是varchar或者char型,32位
<generator class="uuid"></generator>
9.Hibernate一级缓存
a.一个Session不能访问其它Session的缓存区,即一级缓存不能交叉访问
b.在访问数据库时Hibernate会优先向缓存查找要访问的数据,如果缓存区存在数据
,则直接返回,如果缓存区不存在,再向数据库查找并返回
注意:当调用query.list()方法查询全部数据时,查询到的结果会拆开成一个一个的实体对象,单独的存到一级缓存区
那么我们如果再调用get方法查询某一个数据时,该数据由于存在于一级缓存区,因此不会重新执行查询。
如果我们再调用query.list()还是会查询数据库
c.一级缓存的管理
session.evict(obj):将obj从缓存中移除
session.clear():移除缓存区中的全部对象session.close():释放缓存区
10.Hibernate对象持久性
a.临时态
对象刚new出来的是临时态
b.持久态
对象临时态通过save ,saveOrUpdate()转变为持久态
执行get,load,list,iterate方法查询出来的对象,其状态就是持久态
1.持久态对象不能被垃圾回收
2.持久态对象存在于一级缓存中
3.持久态对象可以自动与数据库同步更新
4.更新时机为事务提交,或者是session.flush(),实际上事务提交时就会调用session.flush(),
c.游离态
持久态的对象通过
session.evict(obj),session.clear(),session.close() 转变为游离态
11.Hibernate延迟加载(默认开启)
1)什么是延迟加载
Hibernate中有一些查询方法执行后,并没有立刻查询数据库,而是返回一个实体对象,该对象中只有ID有值
,其他属性均为空,当我们访问这个对象时,HIbernate再去查询数据库,我们把这种推迟查询的行为称之为延迟加载
注意:返回的对象并不是null,只是其内部的属性为空,如果仅仅是访问ID,hibernate也不会去查询数据库
2)什么方法采用延迟加载?
session.load(Class,id)
query.iterate()
关联查询
3)延迟加载有时会有session提前关闭的问题
解决方法方式:
a.不使用延迟加载的方法
b.想办法,在使用对象之后关闭session
1.servlet中考虑用filter
2.Struts2中考虑用拦截器
3.Spring中考虑用AOP
4)延迟加载的原理
Hibernate中采用动态代理CGLIB的技术,来实现延迟加载,即在运行时,当查询某对象时,它返回的不是这个对象的类型
,而是动态的生成一个对象的子类,子类中做了特殊的处理,才实现了延迟加载
12.关联映射
1)什么是关联映射?
当两张表具有关联关系时,Hibernate中可以对这种关联关系进行匹配,然后在我们访问一张表的数据时
,Hibernate可以通过设置好的关系,帮我们自动的将关联表的数据查出来(新增,更新,删除)
,这种关系的设置和使用称之为关联映射。
2)Hibernate中如何使用关联映射?
首先要分析出表的关联关系,并明确关系字段
在实体类中追加关联属性,让两个实体类具有关联关系,然后在我们使用Hibernate的API操作一方实体类时
,它可以通过关联属性来访问另一方的数据
在hbm.xml中,追加关联 关系的设置,当我们访问一方实体类对象时,Hibernate可以通过这个关系操作到另一张表
3)一对多关联
要在一的一方配置文件中追加关联关系的配置
<set name="一的一方关联属性名">
<key column="多的一方关联字段名"/>
<one-to-many class=" 多的一方对象类型名"/></set>
4)多对一关联
要在多的一方配置文件中追加关联关系的配置
<many-to-one name="多的一方
关联属性名" column="
多的一方
关联字段名" class="一
的一方
对象类型名">
5)关联操作
a.延迟加载
lazy=true 时,是延迟加载,默认的情况
lazy=false时 ,不延迟加载
b.连接查询
fetch="select" ,不连接查询,默认情况
fetch="join" 采用连接查询的方式,一旦采用了连接查询,延迟加载就失效了。
c.写HQL,通过join fetch来连接查询
String hql="from User u join fetch u.roles where u.id=?"Query query=session.createQuery(hql);注意HQL中参数赋值,从0开始,HQL大小写敏感
HQL分页查询
int from=(page-1)*pageSize;query.setFirstResult(from)query.setMaxResults( pageSize);
在hbm.xml定义hql<query name="hqlName"><![CDATA[........]]></query>
Query query=session.getNamedQuery("
hqlName") ;
d.关联做新增,修改,删除 (又叫 级联新增,级联修改,级联删除)
给set,many-to-one标记设置cascade属性
none:不进行级联操作
save-update:级联新增、修改关联表
delte:级联删除关联表
all:级联新增、修改、删除关联表
级联操作有时需要设置inverse(反转)
hibernate中的一对多关系,默认双方都会维护关联关系字段,维护关联关系字段的行为称之为控制权inverse意思是“是否控制反转”,默认值为false,意味着不反转即拥有控制权, true,意味着放弃控制权
5)多对多关联
a.多对多关系一般都有一个中间的关系表
b.中间表是解决问题的设计手段,不体现在实体对像中(即中间表没有必要写对应的实体类)
两个表ta,tb
ta对应的类中添加关联属性
<set name="ta对应类的关联属性" table="中间表名">
<key column="中间表的与ta相关的字段名">
<many-to-many class="tb对应的类名" column="中间表的与tb相关的字段名"></set>
6)继承关联
Book extends
Product
Product.hbm.xml 正常写,普通的写
Book.hbm.xml的特殊写法
<joined-subclass class="Book" table="Book对应的表名" extends="Product">
</joined-subclass><key clumn="Book对应的表的关系字段名"><property .../>...
13.Hibernate访问数据库的其他方式
1.Criteria(了解)
a.可以理解为HQL的替代品,是以API的方式来动态的拼一个HQL。
b.涉及到的类:Criteria,Restrictions,Order....
例:
Criteria c=session.createCriteria(User.class);c.add(Restrictions.eq("userName","aaa"));c.addOrder(Order.asc("id"));List<User> li=c.list();
2.Native SQL
Hibernate出于兼容性考虑,当自身提供的访问数据库的方法不能满足需求时,它允许我们写一个SQL,来帮助我们使用JDBC的方式执行。在HIbernate框架下,尽量使用它提供的手段。在很特殊的情况下,Hibernate满足不了需求,才考虑使用SQL例:
String sql="select * from t_user";SQLQuery query =session.createSQLQuery(sql);List<Object[]> li=query.list();
14.Hibernate 高级特性
1.二级缓存
a.SessionFactory下所有的Session都可以访问该二级缓存区域
b.使用步骤:
例:
- 导包ehcache.jar
- 导入缓存配置文件ehcache.xml
<!--指定在磁盘上存储数据的位置,默认是系统的临时目录-->
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="200"eternal="false"timeToIdleSeconds ="120"
timeToLiveSeconds ="300"
overflowToDisk ="true"
/>
cache参数详解:
name:指定区域名
maxElementsInMemory :缓存在内存中的最大数目
maxElementsOnDisk:缓存在磁盘上的最大数目
eternal :缓存是否持久
overflowToDisk : 超出
内存中的最大数目是否写入磁盘
timeToIdleSeconds :当缓存条目闲置n秒后销毁
timeToLiveSeconds :当缓存条目存活n秒后销毁
memoryStoreEvictionPolicy:缓存算法,有LRU(默认)、LFU、FIFO
常见缓存算法:
a) LFU(Least Frequently Used):最近不常被使用(命中率低),一定时间段内使用次数最少的
b) LRU(Least Recently Used):最近很少使用(
LinkedHashMap
),没有被使用时间最长的c) FIFO(First In First Out):先进先出
3.在主配置文件hibernate.cfg.xml中设置开启二级缓存,并且设置缓存驱动。
<property name="cache.use_second_level_cache">true</property><property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>4.想缓存哪个实体对象,就修改其hbm.xml配置文件,追加<cache>元素
<cache usage="read-only"/>
注意cache元素放的位置,位置不对会报错
2.查询缓存
a.
默认是关闭的,基于二级缓存,查询缓存可以缓存除对象之外的东西,比如集合,数组等
b.使用步骤:
1.开启二级缓存2.在主配置文件hibernate.cfg.xml中,设置开启查询缓存
<property name="cache.use_query_cache">true</property>
3.在代码中,查询之前设置其可以缓存的标志 setCacheable(true);如:Query query=session.createQuery("from User");query.setCacheable(true);List<User> li=query.list();
15.Hibernate中并发控制
1.悲观锁
不管发生什么情况,即使不会出现问题,也悲观的假设其会出现问题,那么这种处理问题的方式为悲观方式我们处理一条数据,多个线程同时并发访问的并发场景时,往往采取加锁的机制,即当某个线程访问该数据时,对该数据加锁 不允许其他线程访问。不管结果是否有问题,统一采用加锁方式来处理,称之为悲观锁优缺点:很严谨,安全,不会让数据出现问题
由于采用统一的方式去严格处理,那么当不会出现错误的情况发生时,这个处理是个多余的,消耗了资源、降低了效率例:/*LockMode.UPGRADE 指定当前线程查询数据后对其加锁,其他线程不可以访问该数据,直到其提交事务
*/session.get(User.class,1,LockMode.UPGRADE);
2.乐观锁
乐观的认为所有的情况都合法,不会产生问题,但是当执行数据库更新时,它会检查当前数据是否在此期间被修改,如果是则抛出异常,阻止此次更新优缺点:并没有统一加锁,而是任由线程自由访问,因此效率更高
一旦发现数据被更新,就抛出异常,导致操作失败使用步骤:需要在表中增加一个版本字段,每次更新时,Hibernate会自动更新此字段,即当前值+1.每次更新前,Hibernate会自动检查版本字段,如果数据库中最新的版本字段和当前线程查询到的版本字段不一致则认为有并发访问存在,就抛出异常
1.实体类中追加版本属性
2.映射配置文件hbm.xml中追加版本配置
<version name="版本属性" type="integer" column="
版本字段名">
3.应选择悲观锁还是乐观锁?
如果客户群体比较少,且客户能接受等候,那么可以采用悲观锁
如果客户群体很大,让客户等待不现实,那么需要采用乐观锁。