Hibernate的系统 学习

一、Hibernate的介绍

 

1.什么是Hibernate?

首先,hibernate是数据持久层的一个轻量级框架。数据持久层的框架有很多比如:iBATIS,myBatis,Nhibernate,Siena等等。

并且Hibernate是一个开源的orm(object relations mapping)框架,提供了查询获取数据的方法,用面向对象的思想来操作数据库,节省了我们开发处理数据的时间。

2.那使用Hibernate的优点呢?

1.使用简介的hql语句(Hibernate query language)。可以不使用传统的insert,update等sql语句。比如insert一个对象,原来的做法是:insert into 表名称 alue(值1,值2,值3,……),而现在的做法是:save(对象)。

2.使用or映射。对象到关系数据库之间的映射。是从对象的角度操作数据库,再次体现了面向对象思想。原来的实体抽取方法:首先有了表,然后表映射实体对象。而现在Hibernate做法是:直接由对象映射到表。

3.没有侵入性,移植性比较好。什么是没有侵入性?就是Hibernate采用了pojo对象。所谓的pojo对象就是没有继承Hibernate类或实现Hibernate接口。这样的话,此类就是一个普通的Java类,所以移植性比较好。

4.支持透明持久化。透明是针对上层而言的。三层架构的理念是上层对下层的依赖,只是依赖接口不依赖具体实现。而Hibernate中的透明是指对业务逻辑层提供了一个接口session,而其他的都封装隐藏。持久化是指把内存中的数据存放到磁盘上的文件中。

3.当然一个事物,不可能十全十美,即使如此优秀的Hibernate也有自己的弱点。比如:若是大量数据批量操作。则不适合使用Hibernate。并且一个持久化对象不能映射到多张表中。

4.Hibernate中核心5个接口

1.Configuration接口:负责配置及启动Hibernate,用来创建sessionFactory

2.SessionFactory接口:一个SessionFactory对应一个数据源存储,也就是一个数据库对应一个SessionFactory。SessionFactory用来创建Session对象。并且SessionFactory是线程安全的,可以由多个线程访问SessionFactory共享。

3.Session接口:这个接口是Hibernate中常用的接口,主要用于对数据的操作(增删改查)。而这个Session对象不是线程安全的。不能共享。

4.Query接口:用于数据库的查询对象。

5.Transaction接口:Hibernate事务接口。它封装了底层的事务操作,比如JTA(;Javatranscation architecture)所有的数据操作,比如增删改查都写在事务中。

基本的概念以及核心接口已经介绍,那Hibernate又是如何应用的呢?下篇博客将会介绍如何使用Hibernate?

 二、详解Hibernate的搭建及使用
  

1.创建普通的Java项目。

因为hibernate是一个轻量级的框架,不像servlet,还必须需要tomcat的支持,Hibernate只要jdk支持即可。

2.引入jar包。

可以在项目中直接引入jar包,在:项目--->属性--->然后如下图:

另一种办法就是引入库,相当于一个文件夹,把所有的jar包放到自己新建的文件夹中。在:窗体-->选项-->然后如下图:

3.提供Hibernate的配置文件。hibernate.cfg.xml文件。完成相应的配置。

  1. <hibernate-configuration>
  2. <session-factory>
  3. <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
  4. <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_first</property>
  5. <property name="hibernate.connection.username">root</property>
  6. <property name="hibernate.connection.password">bjpowernode</property>
  7. <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  8. </session-factory>
  9. </hibernate-configuration>

在这里连接MySQL数据库,解释一下上面的标签。按照顺序来依次解释:第一个是连接mySql的驱动;第二个是连接的url;url后面的hibernate_first是数据库名字;第三个是和第四个分别是用户名和密码。第五个是方言。因为 hibernate对数据库封装,对不同的数据库翻译成不同的形式,比如drp中的分页,若是使用Oracle数据库,则翻译成sql语句三层嵌套。若是使用mySql数据库,则翻译成limit语句。

4.建立实体User类:

  1. package com.bjpowernode.hibernate;
  2. import java.util.Date;
  3. public class User {
  4. private String id;
  5. private String name;
  6. private String password;
  7. private Date createTime;
  8. private Date expireTime;
  9. public String getId() {
  10. return id;
  11. }
  12. public void setId(String id) {
  13. this.id = id;
  14. }
  15. public String getName() {
  16. return name;
  17. }
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21. public String getPassword() {
  22. return password;
  23. }
  24. public void setPassword(String password) {
  25. this.password = password;
  26. }
  27. public Date getCreateTime() {
  28. return createTime;
  29. }
  30. public void setCreateTime(Date createTime) {
  31. this.createTime = createTime;
  32. }
  33. public Date getExpireTime() {
  34. return expireTime;
  35. }
  36. public void setExpireTime(Date expireTime) {
  37. this.expireTime = expireTime;
  38. }
  39. }

5.建立User.hbm.xml,此文件用来完成对象与数据库表的字段的映射。也就是实体类的那些字段需要映射到数据库表中呢。

  1. <?xml version="1.0"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  5. <hibernate-mapping>
  6. <class name="com.bjpowernode.hibernate.User">
  7. <id name="id">
  8. <generator class="uuid"/>
  9. </id>
  10. <property name="name"/>
  11. <property name="password"/>
  12. <property name="createTime"/>
  13. <property name="expireTime"/>
  14. </class>
  15. </hibernate-mapping>

6.我们也映射完毕了,但是hibernate怎么知道我们映射完了呢,以及如何映射的呢?这就需要我们把我们自己的映射文件告诉hibernate,即:在hibernate.cfg.xml配置我们的映射文件。

  1. <mapping resource="com/bjpowernode/hibernate/User.hbm.xml"/>

7.生成数据库表。大家也看到了我们上述还没有新建数据表呢,在第三步我们只是新建了数据库而已。按照我们普通的做法,我们应该新建数据表啊,否则实体存放何处啊。这个别急,数据库表这个肯定是需要有的,这个毋庸置疑,但是这个可不像我们原来需要自己亲自动手建立哦,现在hibernate需要帮我们实现哦,如何实现嗯,hibernate会根据配置文件hibernate.cfg.xml和我们的映射文件User.hbm.xml会自动给我们生成相应的表,并且这个表的名字也给我们取好:默认是User。那如何生成表呢?

  1. //默认读取hibernate.cfg.xml文件
  2. Configuration cfg = new Configuration().configure();
  3. SchemaExport export = new SchemaExport(cfg);
  4. export.create(true, true);

8.那我们就开始进行操作啦,我们添加一个用户对象,看看hibernate是如何添加的呢?跟我们以前的做法有什么不同呢?

  1. public class Client {
  2. public static void main(String[] args) {
  3. //读取hibernate.cfg.xml文件
  4. Configuration cfg = new Configuration().configure();
  5. //建立SessionFactory
  6. SessionFactory factory = cfg.buildSessionFactory();
  7. //取得session
  8. Session session = null;
  9. try {
  10. session = factory.openSession();
  11. //开启事务
  12. session.beginTransaction();
  13. User user = new User();
  14. user.setName("张三");
  15. user.setPassword("123");
  16. user.setCreateTime(new Date());
  17. user.setExpireTime(new Date());
  18. //保存User对象
  19. session.save(user);
  20. //提交事务
  21. session.getTransaction().commit();
  22. }catch(Exception e) {
  23. e.printStackTrace();
  24. //回滚事务
  25. session.getTransaction().rollback();
  26. }finally {
  27. if (session != null) {
  28. if (session.isOpen()) {
  29. //关闭session
  30. session.close();
  31. }
  32. }
  33. }
  34. }
  35. }

第八步,我们可以看到,没有我们熟悉的insert into表的sql语句了,那怎么添加进去的呢,到底添加了没?让我真实滴告诉你,确实添加进去了,不信的,可以自己尝试哦,这也是hibernate的优点,对jdbc封装的彻底,减少了我们对数据的操作时间哈。

 三、Hibernate的总结
  

看一下hibernate中整体的内容:

我们一一介绍其中的内容。

  1. Hibernate出现的原因上篇博客已经介绍,可以参考《Hibernate介绍》
  2. Hibernate中的核心五大接口,在上篇博客中也已经介绍,可以参考《Hibernate介绍》
  3. 如何搭建Hibernate,请参考《八步详解Hibernate的搭建及使用》
  4. 持久化对象的三种状态。

分别为:瞬时状态(Transient),持久化状态(Persistent),离线状态(Detached)。三种状态下的对象的生命周期如下:

三种状态的区别是:瞬时状态的对象:没有被session管理,在数据库没有;持久化状态的对象:被session管理,在数据库存在,当属性发生改变,在清理缓存时,会自动和数据库同步;离线状态:没有被session管理,但是在数据库中存在。

5.测试工具Juit。

测试类需要继承TestCase,编写单元测试方法,方法名称必须为test开头,方法没有参数没有返回值,采用public修饰。其中在测试中,查询对象时,使用get或者load两种方法进行加载,这种方法的区别:get不支持延迟加载,而load默认情况下是支持延迟加载。并且get查询对象不存在时,返回null;而load查询对象不存在时,则抛出ObjectNotFoundException异常。

6.悲观锁和乐观锁解释。

悲观锁为了解决并发性,跟操作系统中的进程中添加锁的概念一样。就是在整个过程中在事务提交之前或回滚之前,其他的进程是无法访问这个资源的。悲观锁的实现方式有两种:一种使用数据库中的独占锁;另一种是在数据库添加一个锁的字段。hibernate中声明锁如下:

Account account = (Account)session.get(Account.class, 1, LockMode.UPGRADE);而net.sf.hibernate.LockMode类表示锁模式,当取值LockMode.UPGRADE时,则表示使用悲观锁for update;而乐观锁是为了解决版本冲突的问题。就是在数据库中添加version字段,每次更新时,则把自己的version与数据库中的version进行比较,若是版本相比较低,则不允许进行修改更新。

7.H ibernate中的缓存机制。

缓存是什么呢?缓存是应用程序和数据库之间的内存的一片区域。主要的目的是:为了减少对数据库读取的时间。当查询数据时,首先在缓存中查询,若存在,则直接取出,若不存在,然后再向数据库中查询。所以应该把经常访问数据库的数据放到缓存中,至于缓存中的数据如何不断的置换,这也需要涉及一种淘汰数据的算法

谈到这个hibernate中的缓存,你想到了什么呢?刚才叙述缓存时,是否感觉很熟悉,感觉从哪也听过似的。嗯呢,是呢,是很熟悉,写着写着就很熟悉,这个刚才的缓存以及缓存的置换算法就和计算机组成中的cache类似。

好吧,来回到我们hibernate中的缓存。

hibernate中的缓存可以分为两种:一级缓存,也称session缓存;二级缓存,是由sessionFactory管理。

那一级缓存和二级缓存有什么区别呢?区别的关键关于:缓存的生命周期,也就是缓存的范围不同。

那首先介绍一下缓存的生命周期,也就是缓存的范围。

1.事务缓存,每个事务都有自己的缓存,当事务结束,则缓存的生命周期同样结束,正如上篇博客中我们提到,对数据库的操作,增删改查都是放到事务中的,和事务保持同步,若是事务提交完毕,一般是不允许是再次对数据库进行操作。所以session是属于事务缓存的。

2.应用缓存,一个应用程序中的缓存,也就是应用程序中的所有事务的缓存。只有当应用程序结束时,此时的缓存的额生命周期结束。二级缓存就是应用缓存。

3.集群缓存,被一台机器或多台机器的进程共享。

这下明白了一级缓存和二级缓存的区别了吧。那一级缓存和二级缓存的共同点是:都是缓存实体属性,

二级缓存一般情况都是由第三方插件实现的。第三方插件如:

EHCache,JbossCache(是由Jboss开源组织提供的),osCache(open symphony),swarmCache。前三种对hibernate中的查询缓存是支持的,后一种是不支持hibernate查询缓存。

那什么是hibernate查询缓存呢?

查询缓存是用来缓存普通属性的,对于实体对象而言,是缓存实体对象的id。

8.hql查询。

hibernate query language。hql查询中关键字不区分大小写,但是类和属性都是区分大小写的。

1.简单属性查询。

单一属性查询,返回属性结果集列表,元素类型和实体类的相应的类型一致。

  1. List students = session.createQuery("select name from Student").list();
  2. for (Iterator iter=students.iterator(); iter.hasNext();) {
  3. String name = (String)iter.next();
  4. System.out.println(name);
  5. }

//返回结果集属性列表,元素类型和实体类中的属性类型一致

多个属性查询,多个属性查询返回数组对象,对象数组的长度取决于属性的个数,对象数组中的元素类型与实体类中属性一致。

  1. List students = session.createQuery("select id, name from Student").list();
  2. for (Iterator iter=students.iterator(); iter.hasNext();) {
  3. Object[] obj = (Object[])iter.next();
  4. System.out.println(obj[0] + ", " + obj[1]);
  5. }

2.实体对象查询

List students = session.createQuery("from Student").list();

当然这种hql语句,可以使用别名,as可以省去,如:from Student as s,若是使用select关键字,则必须使用别名。如:select s from Student as s.但是不支持select * from Student格式。

查询中使用list和Iterate区别:

list查询是直接运行查询的结果,所以只有一句sql语句。而iterate方法则有可能会产生N+1条sql语句。这是怎么回事呢?要理解N+1条语句,首先得弄明白iterate是如何执行查询的?

首先发出一条查询对象ID的语句,然后根据对象的ID到缓存(缓存的概念上篇博客已经提到)中查找,若是存在查询出此对象的其他的属性,否则会发出N条语句,此时的N语句,是刚才第一次查询的记录条数。这种现象就是N+1sql语句。

其中list是默认情况下都发出sql语句,查询出的结果会放到缓存中,但是它不会利用缓存,即使放进去,下次执行时,仍然继续发出sql语句。

而:iterate默认情况下会利用缓存,若是缓存中有则不会发出N+1条语句。

3.条件查询。

这种方式就是传入参数,使用参数占位符“?”。也可以使用“:参数名”

Java代码如下:

  1. List students = session.createQuery("select s.id, s.name from Student s where s.name like ?")
  2. .setParameter(0, "%0%")
  3. .list();
  4. List students = session.createQuery("select s.id, s.name from Student s where s.name like :myname")
  5. .setParameter("myname",  "%0%")
  6. .list();

4.使用原生sql语句。

和咱们原先写入的sql语句一样。在此不介绍了。

5.外置命名查询。

这个听起来有点晦涩,怎么理解呢?其实通俗的说就是把hql语句写在外面,写在映射文件中。使用标签:

  1. <query name="queryStudent">
  2. <![CDATA[
  3. select s from Student s where s.id <?
  4. ]]>
  5. </query>

那在程序中是如何使用此标签的呢?使用session.getNameQuery(),并进行赋值,代码如下:

  1. List students = session.getNamedQuery("queryStudent")
  2. .setParameter(0, 10)
  3. .list();

6.查询过滤器。

这个是什么意思呢?过滤器大家很熟悉吧,不熟悉的可以参考我的以前博客<>.原来我们接触过编码过滤器,编码过滤器就是为了避免当时每个页面需要设置编码格式而提出的。这个查询过滤器其实也是这个意思。若是代码都需要某一句sql语句的话,可以考虑使用它。这样可以避免每次都写查询语句。

使用如下:首先在映射文件中配置标签:

  1. <filter-def name="testFilter">
  2. <filter-param type="integer" name="myid"/>
  3. </filter-def>

然后程序中如下使用并进行赋值:

  1. session.enableFilter("testFilter")
  2. .setParameter("myid", 10);

7.分页查询。

分页查询,这个肯定不陌生,因为在做drp项目时,做的最多的是分页,当时使用Oracle数据库,分页查询涉及到三层嵌套。直接传入的参数为:每页的大小(记录数),页号。

Hibernate中给我们已经封装好了,只要设置开始的页号以及每页的大小即可,不用亲自动手写嵌套的sql语句。

代码如下:

  1. List students = session.createQuery("from Student")
  2. .setFirstResult(1)
  3. .setMaxResults(2)
  4. .list();

8.对象导航查询。

这个什么意思呢?这个只要是用于一个类的属性是另一个类的引用。比如:student类中有一个classes属性。其中的classes也是一个类Class的引用。

当我们查询的时候可以这样使用:

  1. List students = session.createQuery("from Student s where s.classes.name like '%t%'")
  2. .list();

相当于:s.getClasses.getName(),直接使用get后面的属性,然后首字母小写。

这种语法,是不是很熟悉?想想我们在哪是不是也用过?想起来了吗?估计你猜出来啦,呵呵,是JSTL(jsp standard tag library)中。若是想进一步了解,可以参考我的博客哈,当时是转载滴貌似。

9.连接查询。

连接分为:内连接和外连接,其中外连接分为左连接,右连接,完全连接。这个跟数据库中的左右连接其实是一样的。我们通俗解释一下:

左连接:以左边为准,右边即使没哟匹配的,也要把这条记录查询出来,此时没有匹配的右边以null填充。

右连接:以右边为准,左边即使没有匹配的,也要把这条记录查询出来,此时没有匹配的左边以null填充。

完全连接:只要一方存在即可。

内连接:必须两方都存在才可以查询提取此记录。

10.统计查询。

其实就是查询count的记录数。其中查询出来的额count是long类型。

11.DML风格的操作。

DML?其实DML=Data Manipulate Language(数据操作语言),举个例子:

  1. session.createQuery("update Student s set s.name=? where s.id<?")
  2. .setParameter(0, "王斌")
  3. .setParameter(1, 2)
  4. .executeUpdate();

假若原来的名字是:李四,更新完数据库后变成王斌,若是我们此时取出数据,其姓名是李四还是王斌?按照道理应该是王斌,但是结果确实李四,若不信,可以自己去实践一下。

这个原因,是因为更新了数据库,但是缓存中没有更新,才会造成这种数据库和缓存不同步的问题。

所以,我们应该尽量不使用这种形式。扬其长避其短嘛。

 

最新文章

  1. android bundle存放数据详解
  2. 关于解决 Failed to prepare partial IU:
  3. iOS如何把导航默认的返回按钮设置成“返回”
  4. sublime2 Ctags 快捷键
  5. AIX filesystemcache引发的Oracle事故
  6. 使用vue-cli脚手架搭建简单项目框架
  7. ElasticSearch 学习记录之ES查询添加排序字段和使用missing或existing字段查询
  8. 做了一个web版的 MyBatis Generator
  9. JS常用正则表达式备忘录
  10. logstash/conf.d文件编写
  11. SQL SERVER 一个SQL语句的执行顺序
  12. Idea设置类注释模板
  13. hiberante 二级缓存设置
  14. asp.net 通用的连接数据库实例代码
  15. CSS之Flex 布局:语法篇
  16. FreeRTOS 二值信号量,互斥信号量,递归互斥信号量
  17. Glide实现查看图片和保存图片到手机
  18. win7利用winSCP上传文件到ubuntu server
  19. map泛型 map不指定泛型 与 Map&lt;Object,Object&gt;的区别
  20. MySQL四种类型日志:Error Log、General Query Log、Binary Log、Slow Query Log

热门文章

  1. Swift3 访问权限fileprivate和 open
  2. 表单格式化插件jquery.serializeJSON
  3. 批量自动更新SVN版本库 - Windows
  4. java 生产者 与 消费者的案例
  5. Python进阶 - 命名空间与作用域
  6. String,StringBuffer与StringBuilder
  7. Chrome控制台使用详解
  8. [图形学] 习题8.12 NLN二维线段裁剪算法实现
  9. JanaScript数据类型
  10. Python基础入门教程,Python学习路线图