初识Hibernate之理解持久化类

     上一篇文章我们简单介绍了Hibernate相关的一些最基本的文件及其作用,并在最后完整的搭建了Hibernate的运行环境,成功的完成了与数据库的映射。但是至于其中的一些更加细节的地方并没有很详尽的解释,本篇则主要介绍Hibernate中的一个关键元素,持久化类。主要涉及以下一些内容:

  • 定义用作持久化类的基本要求
  • 持久化对象的几种不同状态及其相互之间的转换
  • 使用Hibernate完成对数据库的crud操作

一、定义用作持久化类的基本要求
     所谓的持久化类其实本质上也就是一个普通的Java类,只不过我们通过配置文件让它与数据库中的某张表形成对应关系。虽然Hibernate号称低侵入式设计,对持久化类基本不做要求,但是实际上为了一些优化效率而言,遵守一定的规则则可以提高我们框架的运行效率。
     首先,在该类中需要提供一个无参的构造器。因为我们的持久化类和数据库中具体的数据表形成了映射,那么我们从数据库中取出的数据都会被转换成持久化类的对象返回,这里的无参构造器就是用于框架在反射时构建持久化类对象时候使用的。当然,这一点一般不用我们关心,在Java类中如果没有显式指定构造器都会有一个默认的无参构造器。
     其次,在该类中定义的属性,也就是用于与数据表的字段相对应的元素,它们需要满足Javabean规范,提供相对应的getter和setter方法。这一点毋庸置疑,和我们平常对类属性方法策略是相同的,但是如果有其他需要,也可以自定义访问策略,此处只是Hibernate建议。
     最后,该类不能不定义为final类。在Hibernate中通过生成代理对象来优化框架性能是很常见的操作,而大部分生成代理的方式是通过javassist生成持久化的子类进行代理,如果持久化被定义为final,显然是无法进行代理的。

     还有一些其他的规则需要遵守,但是由于并不是强制要求且只有在某些场景下才具有相应的应用价值,此处暂时不做介绍,等到相应的场景再进行补充。

二、持久化对象的三种不同的状态
     一个持久化对象对应于数据表中的一条记录,那么无论是对数据表的增加删除还是修改都是基于该持久化对象的。比如我想要插入一条记录到数据表中,我就可以new一个持久化对象并为其各个属性(对应于数据表的字段)赋值,然后映射到数据表中。这就是典型的以操作对象的方式操作数据库,但是这个持久化对象是有几种状态的,某个状态下对对象的修改是可以映射到数据表的,而某个状态下则不能作为持久化对象与数据表进行映射操作。而持久化对象主要有以下三个不同状态:

  • 瞬态:对象刚刚被new创建出来,只是一个普通的类对象。
  • 持久化:持久化对象与一个Hibernate Session相关联,在这个状态下,对象的所有属性值的改动,都是可以在事务结束时提交到数据库中的
  • 脱管:原本处于持久状态的对象因为其对应的Session被关闭,而失去持久化能力。此时的对象就处于脱管状态。一旦有Session愿意关联脱管对象,那么该对象就可以立马变为持久状态。

至于这三种不同状态下的相互转换可以用下面这张图很明显的表示出来:

这里写图片描述

其中,Transient表示瞬态,Persisitent表示持久化,Detached表示脱管状态。这张图同时也囊括了持久化对象的整个生命周期,至于其中各个方法的详细介绍,本篇的下一小节将陆续介绍,通过这些方法的调用来感受持久化对象的状态变化。

三、使用Hibernate完成对数据库的crud操作
     上述主要介绍了有关Hibernate持久化对象的一些基本状态等内容,但是对于上图中具体方法调用后,持久化对象状态改变情况并不是很直观。本小节就将从具体代码执行的结果看这些状态之间的切换,至于一些配置文件的内容此处不再编写(详见上篇文章,此处节约篇幅突出重点),首先我们看insert操作。

     1、持久化实体对象
     持久化实体对象也可以理解为插入一条记录到数据表中,反正最终都是让我们new出来的持久化对象和数据表中的某一行相关联。例如:

Transaction transaction = session.beginTransaction();

UserInfo user = new UserInfo();
user.setName("single");
user.setAge(21);

session.save(user);
//提交事务
transaction.commit();
session.close();

此处只列出了最核心的一部分代码。我们首先创建了一个user的持久化对象,此时该对象只是一个普通Java对象并不具备持久化能力,这个状态就是瞬态。接着我们调用save方法,这个方法就会将user对象当前各个属性的值映射到数据库中,并且在save方法调用后,user这个对象此时的状态就变成了持久化状态。所以说,我们的插入操作也是持久化实体对象的一个过程。从Navicat中可以显然的看出来,新数据已经插入:

这里写图片描述

此时的user,只要session不关闭就可以不断的通过修改user属性的值来映射数据表。例如下面一段程序:

UserInfo user = new UserInfo();
user.setName("single");
user.setAge(21);

session.save(user);

user.setName("cyy");
//提交事务
transaction.commit();
session.close();

我们在save方法之后再次对user对象的属性进行修改,然后我们看保存到数据库中的是什么。

这里写图片描述

再看看控制台的输出:

这里写图片描述

显然,调用save方法,Hibernate会为我们生成一条insert语句,我们重新修改user对象的属性值,Hibernate又为我们生成了一条update语句。
但是在没有提交事务之前,所有的Sql语句对于数据库的操作都是预操作,并不会实际改变数据库。直到事务提交的时候,所有的操作才变为实际数据表的变化。还有几个和save相关的方法在这里简单介绍下:

  • Serializable save(Object var1):这是我们上述一直在使用的save方法,var1就是我们的持久化对象,通过调用该方法,Hibernate会为我们生成一条insert语句并立即对数据库进行一次预插入操作。最后返回该对象所对应的数据表中一行的主键值。
  • void persist(Object var1):这个方法所做的事情和save方法是一样的,都是将持久化对象的各个属性值去映射到数据表中的一行数据,只是不返回对应的主键的值。

除此之外,persisit方法和save方法还有一个重要区别。save方法在调用后会立马向数据库发送一条Sql,做一次预插入操作。而perisist方法采用懒加载机制,persist如果在事务之外调用,它不会立即向数据库发送Sql语句进行预插入,而是暂时被缓存直到清除缓存的时候才向数据进行插入。这样做有一个好处就是减少了对数据库的访问次数,但是缺点就在于数据库中的数据始终没有得到更新,容易产生脏数据读取。通过个例子看看:

UserInfo user = new UserInfo();
user.setName("single");
user.setAge(21);

session.save(user);

我们将这段代码从事务中抽离出来单独执行,通过打断点可以看到在save方法调用结束之时控制台输出的信息:

这里写图片描述

显然,save方法调用结束就会立马对数据库进行预插入操作。下面我们看persisit方法:

UserInfo user = new UserInfo();
user.setName("single");
user.setAge(21);
        
session.persist(user);
session.flush();

通过断点调试,可以看到perisist方法调用结束后,控制台并没有输出任何信息,反而在flush方法调用完毕之后,控制台输出insert语句。这就是persisit的懒加载思想,平常的一般操作首选save,在一些长会话流程的时候可以选择persist方法降低数据库压力。

     2、根据主键加载持久化实体
     以上我们可以通过save方法向数据库中插入一条记录,同样我们也可以使用get方法根据主键的值从数据库中加载出来一个持久化对象。下面我们看个例子,首先展示下userinfo表中内容:

这里写图片描述

接着我们使用Hibernate取出其中某条记录:

/*这里依然只是展示部分代码,说明问题即可*/
UserInfo user = (UserInfo)session.get(UserInfo.class,2);
System.out.println("name: "+user.getName()+",age: "+ user.getAge());

//提交事务
transaction.commit();

这里的get方法主要有两个参数,第一个参数指定要加载的数据表,第二个参数指定主键值。Hibernate将根据该主键的值进行加载,最后会返回一个Object对象。运行结果如下:

这里写图片描述

从运行结果来看,显然我们成功的根据主键值加载出来一个userInfo对象。除此之外,get方法调用结束后也会立即向数据库进行访问操作,这点和save方法是类似的。当然,如果主键的值不存在,那么将返回null,否则则会返回相对应的持久化对象。这里需要注意一点的是,我们的get方法是用于加载一个持久化对象的,而对于数据库的各种查询操作将在后文介绍。

     3、更新持久化实体
     除了insert和get,我们还可以通过操作持久化对象的属性值来修改数据表中的数据内容。例如:

UserInfo user = (UserInfo)session.get(UserInfo.class, 2);
user.setName("aaaa");
user.setAge(111);

session.update(user);

transaction.commit();
session.close();

程序比较简单,从数据库的变化来看,上述程序将主键值为2的记录name和age字段的信息做了修改。程序在控制台一共输出了两条Sql语句,一条是get方法调用结束生成的,一条是commit时候生成的update更新语句。也就是说update方法调用结束后并没有立即访问数据库,而是暂时存放在缓存中,等事务提交的时候在要求数据库执行。

     3、删除持久化实体
     Hibernate中提供delete方法通过持久化对象来删除数据表中的一行记录。例如:

//删除数据库中id为3的记录
UserInfo user = (UserInfo)session.get(UserInfo.class,3);

session.delete(user);

//提交事务
transaction.commit();
session.close();

首先我们获得数据表中id为3的一条记录的引用,然后直接调用delete方法删除该记录。同样的,Hibernate为我们生成两条Sql语句,一条是get生成的,一条是delete方法产生的,但是delete方法结束后并没有立即向数据库发送Sql语句,而是等到事务提交之时。

最后还要提到两个方法,这两个方法用于清除session中的持久化对象。

  • clear:调用该方法将清除与session绑定的所有持久化对象,这些对象统统变为脱管状态,或者说游离状态
  • evict:该方法有一个参数,调用该方法将显式指定清除session中的某个具体的持久化对象,调用完毕之后,该对象将处于游离状态

至此,有关持久化类及其生成的对象的相关知识,已经简单的介绍了,下篇我们将学习映射。总结不到之处,望指出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,296评论 18 399
  • Hibernate: 一个持久化框架 一个ORM框架 加载:根据特定的OID,把一个对象从数据库加载到内存中OID...
    JHMichael阅读 1,931评论 0 27
  • 本文中我们介绍并比较两种最流行的开源持久框架:iBATIS和Hibernate,我们还会讨论到Java Persi...
    大同若鱼阅读 4,258评论 4 27
  • 输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来...
    鸿雁长飞光不度阅读 171评论 0 0
  • 文学梦想路崎崎, 炒作软文糜。 金钱肚外,风骚在内,海棠社均宜。 寻兮木易金美句, 何处觅如诗? 才疏学浅, 激情...
    舒己怀_Frank阅读 259评论 9 7