📄 objectstate.html
字号:
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>第 11 章 与对象共事</title><link rel="stylesheet" href="../shared/css/html.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="HIBERNATE - 符合Java习惯的关系数据库持久化"><link rel="up" href="index.html" title="HIBERNATE - 符合Java习惯的关系数据库持久化"><link rel="previous" href="inheritance.html" title="第 10 章 继承映射(Inheritance Mappings)"><link rel="next" href="transactions.html" title="第 12 章 事务和并发"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">第 11 章 与对象共事</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="inheritance.html">上一页</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="transactions.html">下一页</a></td></tr></table><hr></div><div class="chapter" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title"><a name="objectstate"></a>第 11 章 与对象共事</h2></div></div><div></div></div><p> Hibernate是完整的对象/关系映射解决方案,它提供了对象<span class="emphasis"><em>状态管理(state management)</em></span>的功能,使开发者不再需要理会底层数据库系统的细节。 也就是说,相对于常见的JDBC/SQL持久层方案中需要<tt class="literal">管理SQL语句</tt>,Hibernate采用了更自然的面向对象的视角来持久化Java应用中的数据。 </p><p> 换句话说,使用Hibernate的开发者应该总是关注对象的<span class="emphasis"><em>状态(state)</em></span>,不必考虑SQL语句的执行。 这部分细节已经由Hibernate掌管妥当,只有开发者在进行系统性能调优的时候才需要进行了解。 </p><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="objectstate-overview"></a>11.1. Hibernate对象状态(object states)</h2></div></div><div></div></div><p> Hibernate定义并支持下列对象状态(state): </p><div class="itemizedlist"><ul type="disc"><li><p> <span class="emphasis"><em>瞬时(Transient)</em></span> - 由<tt class="literal">new</tt>操作符创建,且尚未与Hibernate <tt class="literal">Session</tt> 关联的对象被认定为瞬时(Transient)的。瞬时(Transient)对象不会被持久化到数据库中,也不会被赋予持久化标识(identifier)。 如果程序中没有保持对瞬时(Transient)对象的引用,它会被垃圾回收器(garbage collector)销毁。 使用Hibernate <tt class="literal">Session</tt>可以将其变为持久(Persistent)状态。(Hibernate会自动执行必要的SQL语句) </p></li><li><p> <span class="emphasis"><em>持久(Persistent)</em></span> - 持久(Persistent)的实例在数据库中有对应的记录,并拥有一个持久化标识(identifier)。 持久(Persistent)的实例可能是刚被保存的,或刚被加载的,无论哪一种,按定义对象都仅在相关联的<tt class="literal">Session</tt>生命周期内的保持这种状态。 Hibernate会检测到处于持久(Persistent)状态的对象的任何改动,在当前操作单元(unit of work)执行完毕时将对象数据(state)与数据库同步(synchronize)。 开发者不需要手动执行<tt class="literal">UPDATE</tt>。将对象从持久(Persistent)状态变成瞬时(Transient)状态同样也不需要手动执行<tt class="literal">DELETE</tt>语句。 </p></li><li><p> <span class="emphasis"><em>脱管(Detached)</em></span> - 与持久(Persistent)对象关联的<tt class="literal">Session</tt>被关闭后,对象就变为脱管(Detached)的。 对脱管(Detached)对象的引用依然有效,对象可继续被修改。脱管(Detached)对象如果重新关联到某个新的<tt class="literal">Session</tt>上, 会再次转变为持久(Persistent)的(Detached其间的改动将被持久化到数据库)。 这个功能使得一种编程模型,即中间会给用户思考时间(user think-time)的长时间运行的操作单元(unit of work)的编程模型成为可能。 我们称之为<span class="emphasis"><em>应用程序事务</em></span>,即从用户观点看是一个操作单元(unit of work)。 </p></li></ul></div><p> 接下来我们来细致的讨论下状态(states)及状态间的转换(state transitions)(以及触发状态转换的Hibernate方法)。 </p></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="objectstate-makingpersistent"></a>11.2. 使对象持久化</h2></div></div><div></div></div><p> Hibernate认为持久化类(persistent class)新实例化的对象是<span class="emphasis"><em>瞬时(Transient)</em></span>的。 我们可将瞬时(Transient)对象与session关联而变为<span class="emphasis"><em>持久(Persistent)</em></span>的。 </p><pre class="programlisting">DomesticCat fritz = new DomesticCat();fritz.setColor(Color.GINGER);fritz.setSex('M');fritz.setName("Fritz");Long generatedId = (Long) sess.save(fritz);</pre><p> 如果<tt class="literal">Cat</tt>的持久化标识(identifier)是<tt class="literal">generated</tt>类型的, 那么该标识(identifier)会自动在<tt class="literal">save()</tt>被调用时产生并分配给<tt class="literal">cat</tt>。 如果<tt class="literal">Cat</tt>的持久化标识(identifier)是<tt class="literal">assigned</tt>类型的,或是一个复合主键(composite key), 那么该标识(identifier)应当在调用<tt class="literal">save()</tt>之前手动赋予给<tt class="literal">cat</tt>。 你也可以按照EJB3 early draft中定义的语义,使用<tt class="literal">persist()</tt>替代<tt class="literal">save()</tt>。 </p><p> 此外,你可以用一个重载版本的<tt class="literal">save()</tt>方法。 </p><pre class="programlisting">DomesticCat pk = new DomesticCat();pk.setColor(Color.TABBY);pk.setSex('F');pk.setName("PK");pk.setKittens( new HashSet() );pk.addKitten(fritz);sess.save( pk, new Long(1234) );</pre><p> 如果你持久化的对象有关联的对象(associated objects)(例如上例中的<tt class="literal">kittens</tt>集合) 那么对这些对象(译注:pk和kittens)进行持久化的顺序是任意的(也就是说可以先对kittens进行持久化也可以先对pk进行持久化), 除非你在外键列上有<tt class="literal">NOT NULL</tt>约束。 Hibernate不会违反外键约束,但是如果你用错误的顺序持久化对象(译注:在pk持久之前持久kitten),那么可能会违反<tt class="literal">NOT NULL</tt>约束。 </p><p> 通常你不会为这些细节烦心,因为你很可能会使用Hibernate的 <span class="emphasis"><em>传播性持久化(transitive persistence)</em></span>功能自动保存相关联那些对象。 这样连违反<tt class="literal">NOT NULL</tt>约束情况都不会出现了 - Hibernate会管好所有的事情。 传播性持久化(transitive persistence)将在本章稍后讨论。 </p></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="objectstate-loading"></a>11.3. 装载对象</h2></div></div><div></div></div><p> 如果你知道某个实例的持久化标识(identifier),你就可以使用<tt class="literal">Session</tt>的<tt class="literal">load()</tt>方法 来获取它。 <tt class="literal">load()</tt>的另一个参数是指定类的.class对象。 本方法会创建指定类的持久化实例,并从数据库加载其数据(state)。 </p><pre class="programlisting">Cat fritz = (Cat) sess.load(Cat.class, generatedId);</pre><pre class="programlisting">// you need to wrap primitive identifierslong pkId = 1234;DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) );</pre><p> 此外, 你可以把数据(state)加载到指定的对象实例上(覆盖掉该实例原来的数据)。 </p><pre class="programlisting">Cat cat = new DomesticCat();// load pk's state into catsess.load( cat, new Long(pkId) );Set kittens = cat.getKittens();</pre><p> 请注意如果没有匹配的数据库记录,<tt class="literal">load()</tt>方法可能抛出无法恢复的异常(unrecoverable exception)。 如果类的映射使用了代理(proxy),<tt class="literal">load()</tt>方法会返回一个未初始化的代理,直到你调用该代理的某方法时才会去访问数据库。 若你希望在某对象中创建一个指向另一个对象的关联,又不想在从数据库中装载该对象时同时装载相关联的那个对象,那么这种操作方式就用得上的了。 如果为相应类映射关系设置了<tt class="literal">batch-size</tt>, 那么使用这种操作方式允许多个对象被一批装载(因为返回的是代理,无需从数据库中抓取所有对象的数据)。 </p><p> 如果你不确定是否有匹配的行存在,应该使用<tt class="literal">get()</tt>方法,它会立刻访问数据库,如果没有对应的行,会返回null。 </p><pre class="programlisting">Cat cat = (Cat) sess.get(Cat.class, id);if (cat==null) { cat = new Cat(); sess.save(cat, id);}return cat;</pre><p> 你甚至可以选用某个<tt class="literal">LockMode</tt>,用SQL的<tt class="literal">SELECT ... FOR UPDATE</tt>装载对象。 请查阅API文档以获取更多信息。 </p><pre class="programlisting">Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);</pre><p> 注意,任何关联的对象或者包含的集合都<span class="emphasis"><em>不会</em></span>被以<tt class="literal">FOR UPDATE</tt>方式返回, 除非你指定了<tt class="literal">lock</tt>或者<tt class="literal">all</tt>作为关联(association)的级联风格(cascade style)。 </p><p> 任何时候都可以使用<tt class="literal">refresh()</tt>方法强迫装载对象和它的集合。如果你使用数据库触发器功能来处理对象的某些属性,这个方法就很有用了。 </p><pre class="programlisting">sess.save(cat);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -