1、 Hibernate学习笔记 Java相关课程系列笔记之十四 笔记内容说明 Hibernate(梁建全老师主讲,占笔记内容100%); 目 录 一、 Hibernate的概述 1 1.1 Hibernate框架的作用 1 1.2 Hibernate访问数据库的优点 1 1.3 JDBC访问数据库的缺陷 1 1.4 Hibernate的设计思想 1 二、 Hibernate的基本使用 2 2.1 Hibernate的重要结构 2 2.2 Hibernate重要的API 2 2.3 Hibernate使用环节 2
2、 2.4 HQL语句(简要介绍) 6 三、 数据映射类型 7 3.1映射类型的作用 7 3.2 type映射类型的两种写法 7 四、 Hibernate主键生成方式 8 4.1五种生成方式 8 五、 Hibernate基本特性 9 5.1对象持久性 9 5.2处在持久状态的对象具有的特点 9 5.3三种状态下的对象的转换 9 5.4批量操作:注意及时清除缓存 9 5.5案例:三种状态下的对象使用 10 5.6一级缓存机制(默认启动) 10 5.7一级缓存的好处 10 5.8管理一级缓存的方法 10 5.9延迟加载机制 11 5.10具有延迟加载机制的操作 11
3、 5.11常犯的错误 12 5.12延迟加载的原理 12 5.13 Session的get和load方法的区别 12 5.14延迟加载的好处 12 5.15案例:测试延迟加载 12 5.16案例:重构NetCTOSS资费管理模块 13 5.17 Java Web程序中如何用延迟加载操作(OpenSessionInView) 15 六、 关联映射 18 6.1一对多关系one-to-many 18 6.2多对一关系many-to-one 19 6.3多对多关联映射many-to-many 19 6.4关联操作(查询join fetch/级联cascade) 21 6.5
4、继承关系映射 24 七、 Hibernate查询方法 27 7.1 HQL查询 27 7.2 HQL和SQL的相同点 27 7.3 HQL和SQL的不同点 27 7.4 HQL典型案例 27 7.5 Criteria查询 30 7.6 Native SQL原生SQL查询 31 八、 Hibernate高级特性 32 8.1二级缓存 32 8.2二级缓存启动方法及测试 32 8.3二级缓存管理方法 33 8.4二级缓存的使用环境 33 8.5查询缓存 33 8.6查询缓存启动方法及测试 33 8.7查询缓存的使用环境 33 九、 Hibernate锁机制 34
5、9.1悲观锁 34 9.2悲观锁的实现原理 34 9.3悲观锁使用环节及测试 34 9.4乐观锁 35 9.5乐观锁的实现原理 35 9.6乐观锁使用环节及测试 35 十、 其他注意事项 36 10.1源码服务器管理工具 36 10.2运用MyEclipse根据数据表自动生成实体类、hbm.xml 36 10.3根据实体类和hbm.xml生成数据表 37 10.4 Hibernate中分页查询使用join fatch的缺陷 37 10.5 Hibernate的子查询映射 38 一、 Hibernate的概述 1.1 Hibernate框架的作用 Hibern
6、ate框架是一个数据访问框架(也叫持久层框架,可将实体对象变成持久对象,详见第5章)。通过Hibernate框架可以对数据库进行增删改查操作,为业务层构建一个持久层。可以使用它替代以前的JDBC访问数据。 1.2 Hibernate访问数据库的优点 1)简朴,可以简化数据库操作代码。 2)Hibernate可以自动生成SQL,可以将ResultSet中的记录和实体类自动的映射(转化)。 3)Hibernate不和数据库关联,是一种通用的数据库框架(支持30多种数据库),可以方便数据库移植。任何数据库都可以执行它的API。由于Hibernate的API中是不涉及SQL语句的,它会根据Hi
7、bernate的配置文献,自动生成相应数据库的SQL语句。 1.3 JDBC访问数据库的缺陷 1)需要编写大量的复杂的SQL语句、表字段多时SQL也繁琐、设立各个问号值。 2)需要编写实体对象和记录之间的代码,较为繁琐。 3)数据库移植时需要修改大量的SQL语句。 1.4 Hibernate的设计思想 Hibernate是基于ORM(Object Relation Mapping)思想设计的,称为对象关系映射。负责Java对象和数据库表数据之间的映射。 Hibernate是一款主流的ORM工具,尚有其他很多ORM工具,如:MyBatis(以前叫iBatis)、JPA。Hibern
8、ate功能比MyBatis强大些,属于全自动类型,MyBatis属于半自动。但全自动会有些不可控因素,因此有些公司会用MyBatis。 ORM工具在完毕Java对象和数据库之间的映射后: 1)在查询时,直接运用工具取出“对象”(不管是查询一条记录还是多条记录,取出的都是一个个对象,我们不用再去转化实体了)。 2)在增删改操作时,直接运用工具将“对象”更新到数据库表中(我们不用再去把对象转成数据了)。 3)中间的SQL+JDBC细节,都被封装在了工具底层,不需要程序员参与。 u 注意事项: v Java程序想访问数据库,只能通过JDBC的方式,而Hibernate框架也就是基于ORM
9、思想对JDBC的封装。 v Hibernate是以“对象”为单位进行数据库的操作。 二、 Hibernate的基本使用 2.1 Hibernate的重要结构 1) hibernate.cfg.xml(仅1个):Hibernate的主配置文献,重要定义数据连接参数和框架设立参数。 u 注意事项:就是个xml文献,只是名字比较奇葩! 2) Entity实体类(n个,一个表一个):重要用于封装数据库数据。 3)hbm.xml映射文献(n个):重要描述实体类和数据表之间的映射信息。描述表与类,字段与属性的相应关系。 u 注意事项:hbm.xml是个后缀,如:命名可写Cost.hbm.x
10、ml。 2.2 Hibernate重要的API 1)Configuration:用于加载hibernate.cfg.xml配置信息。用于创建SessionFactory。 2)SessionFactory:存储了hbm.xml中描述的信息,内置了一些预编译的SQL,可以创建Session对象。 3)Session:负责对数据表执行增删改查操作。表达Java程序与数据库的一次连接会话,是对以前的Connection对象的封装。和JSP中的session不是一回事,就是名字同样而已。 4)Query:负责对数据表执行特殊查询操作。 5)Transaction:负责Hibernate操作
11、的事务管理。默认情况下Hibernate事务关闭了自动提交功能,需要显式的追加事务管理(如调用Transaction对象中的commit();提交事务)! u 注意事项: v 这些API都是在Hibernate包下的,导包别导错! v 第一次访问数据库比较慢,比较耗资源,由于加载的信息多。 2.3 Hibernate使用环节 step1:建立数据库表。 step2:建立Java工程(Web工程也可),引入Hibernate开发包和数据库驱动包。必须引入的包:hibernate3.jar、cglib.jar、dom4j.jar、commons-collections.jar、comm
12、ons-logging.jar…等
step3:添加hibernate.cfg.xml配置文献,文献内容如下:
14、system
15、"format_sql">true
16、ID private String feeName; //资费名称 private Integer baseDuration; //基本时长 private Float baseCost; //基本定费 private Float unitCost; //单位费用 private String status; //0:开通;1:暂停; private String descr; //资费信息说明 private Date createTime; //创建日期 private Date startTime; //启用日
17、期 private String costType; //资费类型 ……getter/setter方法 u 注意事项:POJO类表达普通类(Plain Ordinary Old Object),没有格式的类,只有属性和相应的getter/setter方法,而没有任何业务逻辑方法的类。这种类最多再加入equals()、hashCode()、toString()等重写父类Object的方法。不承担任何实现业务逻辑的责任。 step5:编写hbm.xml映射(文献)描述信息:映射文献用于指明POJO类和表之间的映射关系(xx属性相应xx字段),一个类相应一个映射文献。例如:Cost.
18、hbm.xml内容如下:
21、rator>
22、"BASE_DURATION" />
23、junit测试右键点Copy Trace查看错误列-->
24、nate API实现DAO 1) 新建HibernateUtil类,用于封装创建Session的方法。如下:每个用户会相应一个Session,但是SessionFactory是共享的。 public class HibernateUtil { private static SessionFactory sf; static{//不用每次都加载配置信息,所以放static块中,否则每次都加载会花费资源 Configuration conf=new Configuration();//加载主配置hibernate.cfg.xml conf.configure("/hiber
25、nate.cfg.xml"); sf=conf.buildSessionFactory();//获取SessionFactory } public static Session getSession(){//获取Session Session session =sf.openSession(); return session; } } 2) 新建CostDAO接口 public Cost findById(int id); public void save(Cost cost); public void delete(int id); p
26、ublic void update(Cost cost);
public List
27、的描述文献来定,get方法就是findById,就是按主键去查,需指定:操作哪个类和id(主键)条件值即可,其他条件查询做不了 */ public Cost findById(int id) { //Session session=HibernateUtil.getSession(); Cost cost=(Cost)session.get(Cost.class,id); session.close(); return cost; } /** save方法执行增长操作,注意1:获取事务并启动,增删改要注意,查询可以不管事务,由于没对数据库进行修改;注意2:主键值根据h
28、bm.xml中的
29、e();//释放 } /** delete方法执行删除操作,由于Hibernate以“对象”为单位进行数据库操作,所以这里要传进去一个对象,虽然是个对象,但还是按主键做条件删除,只要把主键值设立上就行,其他非主键值不用管。也可先通过id查再删 */ public void delete(int id) { //Session session=HibernateUtil.getSession(); Transaction tx=session.beginTransaction(); Cost cost=new Cost(); cost.setId(id);
30、 session.delete(cost); mit(); session.close(); } /** update方法执行修改操作, */ public void update(Cost cost) { //Session session=HibernateUtil.getSession(); Transaction tx=session.beginTransaction(); session.update(cost);//将cost对象更新到数据库 mit(); session.close(); } /** 特
31、殊查询,SQL语句:String sql="select * from COST_CHANG";
HQL语句:String hql="from Cost"; (Hibernate Query Language)是面向对象的查询语句。
from后写映射的类名,它是Hibernate中特有的查询语句,根据映射的类去查询。 */
public List
32、ion.createQuery(hql);
List
33、tln(cost.getName());System.out.println(cost.getBaseDuration()); System.out.println(cost.getBaseCost()); System.out.println(cost.getUnitCost()); System.out.println(cost.getDescr()); } @Test public void testSave(){//id主键列由Hibernate管理,这里不用设立 Cost cost=new Cost(); cost.setName("2023
34、计时"); cost.setUnitCost(0.8f); cost.setDescr("2023-08-09计时,0.8元/小时。"); cost.setStatus("0"); cost.setCreaTime(new Date(System.currentTimeMillis())); CostDAO costDao = new CostDAOImpl(); costDao.save(cost); } @Test public void testUpdate(){//开通某个资费,把状态由0变为1 CostDAO costDAO=new CostD
35、AOImpl(); /** 注意事项:更新部分字段,不能和实现类中的删除那样,做一个对象出来!否则没设立的字段将被改为空! 即不能:Cost cost=new Cost(); cost.setId(90); cost.setStatus("1"); cost.setStartTime(new Date(System.currentTimeMillis())); */ Cost cost=costDAO.findById(90);//只能先通过id找到带有所有值的对象 cost.setStatus("1");//然后再对部分字段进行更新,才干避免把其他字段更
36、新为空
cost.setStartTime(new Date(System.currentTimeMillis()));
costDAO.update(cost); }
@Test
public void testDelete(){
CostDAO costDAO=new CostDAOImpl(); costDAO.delete(90); }
@Test
public void testFindAll(){
CostDAO costDAO=new CostDAOImpl(); List
37、findAll(); for(Cost c:list){ System.out.println(c.getName()); } } 2.4 HQL语句(简要介绍) 简要介绍见2.3节中step6中的3)特殊查询(本页最上)。具体介绍见第七章。 三、 数据映射类型 hbm.xml在描述字段和属性映射时,采用type属性来指定映射类型。 3.1映射类型的作用 重要负责实现属性和字段值之间的互相转化。 3.2 type映射类型的两种写法 1)指定Java类型,例如:java.lang.String、java.lang.Integer …,不能写成String … 2
38、指定Hibernate类型,例如: ①整数:byte、short、integer、long; ②浮点数:float、double; ③字符串:string; ④日期和时间:date(只解决年月日),time(只解决时分秒),timestamp(解决年月日时分秒); ⑤布尔值:true/false<-yes_no->char(1)(数据库存Y/N,显示时自动转为true/false)、 true/false<-true_false->char(1)(数据库存T/F,显示时自动转为true/false)、 true/false<-boolean->bit(数据库存1/0,显示时自
39、动转为true/false);
⑥其他:blob(以字节为单位存储大数据)、clob(以字符为单位存储大数据)、
big_decimal、big_integer;
u 注意事项:Hibernate类型都是小写!建议使用Hibernate类型。
3)所以2.3节中step5的Cost.hbm.xml内的type也可这样写:
41、rnate主键生成方式
Hibernate负责管理主键值。它提供了多种主键生成方式。
4.1五种生成方式
1)sequence:可以按指定序列生成主键值。只合用于Oracle数据库。不紧张并发量!
例如: 42、"identity">
43、句话,这样通用性更强。
4) assigned:Hibernate会放弃主键生成,采用此方法,需要在程序中指定主键值。
例如:
44、hilo:按高低位算法生成一个主键值(数值类型)。
例如:
45、未调用 Session的API)。也叫临时对象。临时状态的对象会被Java的垃圾回收机制回收。 2) 持久状态:实体对象与Session发生关联(调用了Session的get、load、save、update等API)。也叫持久对象。 3) 游离状态:本来是持久状态,后来脱离了Session的管理。如:Session被关闭,对象将从持久状态变为游离状态,同时垃圾回收机制可以回收掉,不再占用缓存空间了。 5.2处在持久状态的对象具有的特点 1)对象生命期持久,垃圾回收机制不能回收。 2)对象的数据可以与数据库同步(即对象中的数据发生改变,则数据库中的数据自动同步)。由Session对
46、象负责管理和同步。 3)对象在Session的一级缓存中存放(或者说在Session缓存中的对象都是持久对象)。 u 注意事项:Session.close();有两个作用:①关闭连接、释放资源②使对象变为游离状态,当对象的引用不存在时,对象才被回收。没被回收时,对象中的数据还在! 5.3三种状态下的对象的转换 5.4批量操作:注意及时清除缓存 Transaction tx = session.beginTransaction(); for(int i=0;i<100000;i++){ Foo foo = new Foo(); session.save(f
47、oo);//设立foo属性 if(i%50==0){//够50个对象,与数据库同步下,并清除缓存 session.flush();//同步 session.clear();//清除缓存 } } mit(); 5.5案例:三种状态下的对象使用 当持久对象数据改变后,调用session.flush()方法,会与数据库同步更新。 commit()方法,内部也调用了flush()方法,因此使用commit()方法可以省略flush()方法的调用。 @Test public void test1(){//Foo实体有id
48、name、salary、hireDate、marry等属性 Session session=HibernateUtil.getSession(); Transaction tx=session.beginTransaction(); Foo foo=(Foo)session.get(Foo.class, 1);//foo具有持久性 foo.setName("chang"); foo.setSalary(6000); /** 提交事务,若后面不写flush,只写提交commit,则也能执行更新操作。由于commit在内部会先调用flush,再提交事务,所以此时flush
49、可不写 */ mit(); /** 触发同步动作,同步和提交是两回事。数据有变化才同步(更新),没变化不会更新 */ session.flush(); session.close();//关闭session释放资源 } @Test public void test2(){ Foo foo=new Foo(); foo.setName("tigger"); foo.setSalary(8000); foo.setMarry(true); foo.setHireDate(new Date(System.currentTimeMillis())); Session session=HibernateUtil.getSession(); Transaction tx=session.beginTransaction(); session.save(foo);//以上都是临时状态,此时由临时状态转为持久状态 foo.setSalary(10000);//修改foo持久对象的数据,也是更新操作,可不写update方法 mit();//同步、提交 session.close(); } @Test public void test3(){






