📄 manipulatingdata.html
字号:
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>第 9 章 操作持久化数据(Manipulating Persistent Data)</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="第 8 章 继承映射(Inheritance Mappings)"><link rel="next" href="transactions.html" title="第 10 章 事务和并行(Transactions And Concurrency)"></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">第 9 章 操作持久化数据(Manipulating Persistent Data)</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="manipulatingdata"></a>第 9 章 操作持久化数据(Manipulating Persistent Data)</h2></div></div><div></div></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="manipulatingdata-creating"></a>9.1. 创建一个持久化对象</h2></div></div><div></div></div><p> 对象(实体的实例)对一个特定的<tt class="literal">Session</tt>来说,要么是一个<span class="emphasis"><em>瞬时(transient)</em></span>对象,要么是<tt class="literal">持久化(persistent)</tt>对象。刚刚创建的对象当然是瞬时的(注:后文中transient object也称为临时对象)。session则提供了把瞬时实例保存(持久化)的服务: </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><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> 单参数的<tt class="literal">save()</tt>方法为<tt class="literal">fritz</tt>生成了一个惟一标识符,并赋给这个对象。双参数的形式则使用给定的标识符保存<tt class="literal">pk</tt>。我们一般不鼓励使用双参数的形式,因为这可能会(隐含)使主键赋予业务含义。它有用的时候是在一些特殊场合下,比如使用Hibernate来持久化一个BMP实体bean. </p><p> 关联的对象可以用你喜欢的任何顺序持久化,除非有外键字段具有<tt class="literal">NOT NULL</tt>的约束。决不会有外键约束冲突的危险。然而,如果在<tt class="literal">save()</tt>对象的时候用错了顺序,会触犯<tt class="literal">NOT NULL</tt>约束。 </p></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="manipulatingdata-loading"></a>9.2. 装载对象</h2></div></div><div></div></div><p> 如果你已知某个持久化实例的标识符,<tt class="literal">Session</tt>的<tt class="literal">load()</tt>方法让你取出它。第一种形式使用一个类对象作为参数,会把状态装载到另一个新创建的对象中去。第二个版本允许你给出一个实例,会在其中装载状态。把实例作为参数的形式在你准备把Hibernate和BMP实体bean一起使用的时候特别有用,它就是为此设计的。你也可以发现其他的用途(比如自己实现实例池等等)。 </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><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>方法可能抛出无法恢复的exception。如果类是通过代理映射的,<tt class="literal">load()</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> 你可以用SQL<tt class="literal">SELECT ... FOR UPDATE</tt>装载对象。下一节有关于Hibernate <tt class="literal">LockMode</tt>的讨论。 </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>返回。 </p><p> 任何时候都可以使用<tt class="literal">refresh()</tt>方法重新装载对象和它的集合。如果你使用数据库触发器更改了对象的某些属性,这就很有用。 </p><pre class="programlisting">sess.save(cat);sess.flush(); //force the SQL INSERTsess.refresh(cat); //re-read the state (after the trigger executes)</pre></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="manipulatingdata-querying"></a>9.3. Querying</h2></div></div><div></div></div><p> 如果你不能确定你要寻找的对象的标示符,请使用<tt class="literal">Session</tt>的<tt class="literal">find()</tt>方法。Hibernate使用一种简单而强大的面向对象查询语言。 </p><pre class="programlisting">List cats = sess.find( "from Cat as cat where cat.birthdate = ?", date, Hibernate.DATE);List mates = sess.find( "select mate from Cat as cat join cat.mate as mate " + "where cat.name = ?", name, Hibernate.STRING);List cats = sess.find( "from Cat as cat where cat.mate.bithdate is null" );List moreCats = sess.find( "from Cat as cat where " + "cat.name = 'Fritz' or cat.id = ? or cat.id = ?", new Object[] { id1, id2 }, new Type[] { Hibernate.LONG, Hibernate.LONG });List mates = sess.find( "from Cat as cat where cat.mate = ?", izi, Hibernate.entity(Cat.class));List problems = sess.find( "from GoldFish as fish " + "where fish.birthday > fish.deceased or fish.birthday is null");</pre><p> <tt class="literal">find()</tt>的第二个参数接受一个对象或者对象数组。第三个参数接受一个Hibernate类型或者类型的数组。这些指定的类型用来把给定的对象绑定到查询中的<tt class="literal">?</tt>占位符(实际上对应的是JDBC <tt class="literal">PreparedStatement</tt>的传入参数)。就像在JDBC中一眼,你应该优先使用这种参数绑定的方式,而非组装字符串。 </p><p> <tt class="literal">Hibernate</tt>类定义了一些静态方法和常量,提供了访问大部分内置类型的手段。这些内置类型是<tt class="literal">net.sf.hibernate.type.Type</tt>的实例。 </p><p> 如果你知道你的查询会返回非常大量的对象,但是你不希望全部使用它们,你可以用<tt class="literal">iterate()</tt>方法获得更好的性能,它会返回一个<tt class="literal">java.util.Iterator</tt>。这个迭代器会在需要的时候装载对象,所使用的标识符来自一个前导的SQL查询。(一共是N+1次查询) </p><pre class="programlisting">// fetch idsIterator iter = sess.iterate("from eg.Qux q order by q.likeliness"); while ( iter.hasNext() ) { Qux qux = (Qux) iter.next(); // fetch the object // something we couldnt express in the query if ( qux.calculateComplicatedAlgorithm() ) { // delete the current instance iter.remove(); // dont need to process the rest break; }}</pre><p> 很不幸,<tt class="literal">java.util.Iterator</tt>没有声明任何exception。所以,发生的任何SQL或者Hibernate的exception都会被包装在一个<tt class="literal">LazyInitializationException</tt>中(它是<tt class="literal">RuntimeException</tt>的子类)。 </p><p> 如果你预期大部分的对象已经装载过,存在于session的缓存中了,或者查询结果包含同样的对象很多次,那么<tt class="literal">iterator()</tt>方法也会获得更好的性能。(如果没有任何数据被缓存或者重复出现,则<tt class="literal">find()</tt>总是会更快。)下面是一个应该使用<tt class="literal">iterator()</tt>调用的查询例子: </p><pre class="programlisting">Iterator iter = sess.iterate( "select customer, product " +
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -