??xml version="1.0" encoding="utf-8" standalone="yes"?>
q里所丄例子Q都是基于这L一个情景,Item中包含有Images?/SPAN>
<set name="images" lazy="true" table="ITEM_IMAGE">
<key column="ITEM_ID"/>
<element type="string" column="FILENAME" not-null="true"/>
</set>
q里?/SPAN><key>定义了外?/SPAN>ITEM_IDQ?/SPAN><elememt>则声明了集合的内部数据元素?/SPAN><set>定义的集合中不可以有重复的数据?/SPAN>
<idbag name="images" lazy="true" table="ITEM_IMAGE">
<collection-id type="long" column="ITEM_IMAGE_ID">
<generator class="sequence"/>
</collection-id>
<key column="ITEM_ID"/>
<element type="string" column="FILENAME" not-null="true"/>
</idbag>
ITEM_IMAGE_IDZ键,?/SPAN>ITEM_ID则ؓ外键Q?/SPAN><element>元素不变Q这允讔R合中的数据重复?/SPAN>
<list name="images" lazy="true" table="ITEM_IMAGE">
<key column="ITEM_ID"/>
<index column="POSITION"/>
<element type="string" column="FILENAME" not-null="true"/>
</list>
q里?/SPAN>ITEM_IMAGE表添加了indexQ整个表的主键是ITEM_ID?/SPAN>POSITION构成的复合主键,它允讔R合中的数据根?/SPAN>POSITIONq行排序?/SPAN>
<map name="images"
lazy="true"
table="ITEM_IMAGE"
sort="natural">
<key column="ITEM_ID"/>
<index column="IMAGE_NAME" type="string"/>
<element type="string" column="FILENAME" not-null="true"/>
</map>
q里?/SPAN>ITEM_IMAGE表添加了indexQ整个表的主键是ITEM_ID?/SPAN>POSITION构成的复合主键,它允讔R合中的数据根?/SPAN>sort指定的内容进行排序,q里允许用户?/SPAN>sortq行定制?/SPAN>
<map name="images"
lazy="true"
table="ITEM_IMAGE"
order-by="IMAGE_NAME asc">
<key column="ITEM_ID"/>
<index column="IMAGE_NAME" type="string"/>
<element type="string" column="FILENAME" not-null="true"/>
</map>
q里采用的是orbder-byQ注?/SPAN>sort?/SPAN>order-by的区别?/SPAN>Sort是在内存中?/SPAN>comparatorq行排序Q?/SPAN>order-by则是在数据库中?/SPAN>SQL语句q行排序?/SPAN>
<set name="images"
lazy="true"
table="ITEM_IMAGE"
order-by="IMAGE_NAME asc">
<key column="ITEM_ID"/>
<composite-element class="Image">
<property name="name" column="IMAGE_NAME" not-null="true"/>
<property name="filename" column="FILENAME" not-null="true"/>
<property name="sizeX" column="SIZEX" not-null="true"/>
<property name="sizeY" column="SIZEY" not-null="true"/>
</composite-element>
</set>
q里定义了一个新?/SPAN>Imagec,它不是实体类(entity class)Q而是一个简单的值类?/SPAN>(value type)Q仅仅是父实体的一?/SPAN>componentQ其生命周期完全由包含其的父实体 (parent entity) ITEM控制。这?/SPAN>ImagecM包含有数?/SPAN>ITEM_ID?/SPAN> IMAGE_NAME?/SPAN>FILENAME?/SPAN>SIZEX、和SIZEY?/SPAN>
<set name="images"
lazy="true"
table="ITEM_IMAGE"
order-by="IMAGE_NAME asc">
<key column="ITEM_ID"/>
<composite-element class="Image">
<parent name="item"/>
<property name="name" column="IMAGE_NAME" not-null="true"/>
<property name="filename" column="FILENAME" not-null="true"/>
<property name="sizeX" column="SIZEX" not-null="true"/>
<property name="sizeY" column="SIZEY" not-null="true"/>
</composite-element>
</set>
前面定义?/SPAN>IMAGEcMQƈ不包含有对父实体ITEM的引用。也是_只可以从ITEM中导航到IMAGE中,但是?/SPAN>IMAGE无法得到ITEM。这可以通过<parent>实现。这?/SPAN>IMAGEcM含有一个数据成?/SPAN>itemQ指向其父实体?/SPAN>
但是Q它是无法做到真正的双向D的。例如:通过SQL查询直接得到IMAGE后,但是如同普通的值类型一PIMAGE对象?/SPAN>item?/SPAN>nullQ你无法?/SPAN>component如此直接得到其父对象Q这必须由通过Ӟ子关pȝentity完成?/SPAN>
In our CaveatEmptor application, both the user who posted a comment and any system administrator can open an Edit Comment screen to delete or edit the text of a comment. Suppose two different administrators open the edit screen to view the same comment simultaneously. Both edit the comment text and submit their changes. At this point, we have three ways to handle the concurrent attempts to
write to the database:
?Last commit wins?/SPAN>Both updates succeed, and the second update overwrites the changes of the first. No error message is shown.
?First commit wins?/SPAN>The first modification is persisted, and the user submitting the second change receives an error message. The user must restart the business process by retrieving the updated comment. This option is often called optimistic locking.
?SPAN style="FONT: 7pt 'Times New Roman'"> Merge conflicting updates?/SPAN>The first modification is persisted, and the second modification may be applied selectively by the user.
在我们的CavertEmptor中,用户和系l管理员都可以打开“编?/SPAN>cooment”功能,删除或者编辑评论。假讑时有两个理员对同一个评论打开~辑功能Q他们都对评行编辑然后提交。这是,我们发现可能有三U方式对此ƈ发操作进行处理、写入数据库Q?/SPAN>
后者提交获胜-两次更新都成功,但是W二ơ更新将会覆盖第一ơ的更新Q不昄M错误?/SPAN>
前者提交获胜-W一个提交者成功,更新数据库,后一个提交失败,q返回错误信息。用户必重新开始整个业务过E,取得被更新的评论。者就是常说的“乐观锁”?/SPAN>
合ƈ更新冲突Q第一个更新被保存Q而第二个更新则由用户选择是否保存?/SPAN>
The first option, last commit wins, is problematic; the second user overwrites the changes of the first user without seeing the changes made by the first user or even knowing that they existed. In our example, this probably wouldn’t matter, but it would be unacceptable for some other kinds of data. The second and third options are usually acceptable for most kinds of data. From our point of view, the third option is just a variation of the second—instead of showing an error message, we show the message and then allow the user to manually merge changes. There is no single best solution. You must investigate your own business requirements to decide among these three options.
上面的第一个方案是有问题的Q第二个用户在不知道改变、甚至不知道W一个用户已l操作的情况下,直接重写了数据库。在我们的例子中Q也许是没关pȝQ但是对于某些应用是不可接受的。第二个和第三个Ҏ对于l大部分数据是可以结束的。按照我们的观点Q第三个是第二个的变U,它不是显式错误信息,而是l用戯行选择Q允许用h工合q对数据的修攏V这里ƈ没有一个最完美的解x案。你必须Ҏ你自己项目的需要自行选择Ҏ?/SPAN>
The first option happens by default if you don’t do anything special in your application?/SPAN>On the other hand, Hibernate can help you implement the second and third strategies, using managed versioning for optimistic locking.
如果在你的项目中不采取Q何措施,那么会默认用第一个方案。但是,hibernate可以通过使用对“乐观锁”用“管理版本”,帮助你实现第二个或第三个Ҏ?/SPAN>
Managed versioning relies on either a version number that is incremented or a timestamp that is updated to the current time, every time an object is modified. For Hibernate managed versioning, we must add a new property to our Comment class and map it as a version number using the <version> tag. First, let’s look at the changes to the Comment class:
public class Comment {
...
private int version;
...
void setVersion(int version) {
this.version = version;
}
int getVersion() {
return version;
}
}
“管理版本”用版本号或者时间戳Ҏ据进行标讎ͼ每次数据在更新的时候,它都会被更新。对?/SPAN>hibernateQ我们必ȝCommentc,d新的属性,然后使用<version>标签对其q行映射。首先让我们来看一?/SPAN>Commentcȝ改变Q?/SPAN>
public class Comment {
...
private int version;
...
void setVersion(int version) {
this.version = version;
}
int getVersion() {
return version;
}
}
You can also use a public scope for the setter and getter methods. The <version> property mapping must come immediately after the identifier property mapping in the mapping file for the Comment class:
<class name="Comment" table="COMMENTS">
<id ...
<version name="version" column="VERSION"/>
...
</>
你可以?/SPAN>public?/SPAN>setter?/SPAN>getterҎQ这里的<version>映射的定义必ȝ跟在映射文g?/SPAN>commentcȝid后面?/SPAN>
<class name="Comment" table="COMMENTS">
<id ...
<version name="version" column="VERSION"/>
...
</>
You don’t need to set the value of the version or timestamp property yourself; Hibernate will initialize the value when you first save a Comment, and increment or reset it whenever the object is modified. Whenever Hibernate updates a comment, it uses the version column in the SQLWHERE clause:
update COMMENTS set COMMENT_TEXT='New comment text', VERSION=3
where COMMENT_ID=123 and VERSION=2
你无需自己手工M改这里的版本h者时间戳Q?/SPAN>hibernate会在你保存Commment的时候对其初始化Q然后在对象每次被更新的时候即使更新其版本号和旉戟뀂无?/SPAN>hibernate何时更新comment,它都会在SQL语句?/SPAN>where后面加上version列:
update COMMENTS set COMMENT_TEXT='New comment text', VERSION=3
where COMMENT_ID=123 and VERSION=2
If another application transaction would have updated the same item since it was read by the current application transaction, the VERSION column would not contain the value 2, and the row would not be updated. Hibernate would check the row count returned by the JDBC driver—which in this case would be the number of rows updated, zero—and throw a StaleObjectStateException. Using this exception, we might show the user of the second application transaction an error message (“You have been working with stale data because another user modified it!? and let the first commit win. Alternatively, we could catch the exception and show the second user a new screen, allowing the user to manually merge changes between the two versions.
如果某个事务已经更新了某个数据,而另外一个事务也准备更新此数据,但是当时它取得数据的版本h2Q而此时由于该数据已经被更新过Q新版本h3Q因此将无法更新数据库中的Q何的数据。这时?/SPAN>hibernate会?/SPAN>jdbc的返回行敎ͼ如果未有M数据行被更新Q则q回0Q因此将会抛?/SPAN>StaleObjectStateException异常?/SPAN>
Using this exception, we might show the user of the second application transaction an error message (“You have been working with stale data because another user modified it!? and let the first commit win. Alternatively, we could catch the exception and show the second user a new screen, allowing the user to manually merge changes between the two versions.
SessionQ会话)接口是Hibernate应用使用的主要接口。会话接口的实例是轻量的ƈ且创Z销毁的代h也不昂贵。这很重要因Z的应用可能始l在创徏与销毁会话,可能每一ơ请求都会如此。Hibernate会话q不是线E安全的因此应该被设计ؓ每次只能在一个线E中使用?o:p>
Hibernate会话是一个介于连接和事务之间的概c?FONT color=#ff1493>你可以简单地认ؓ会话是对于一个单独的工作单元已装载对象的~存或集合。Hibernate可以到q个工作单元中对象的改变?/FONT>我们有时也将会话UCؓ持箋性管理器Q因为它也是与持l性有关的操作例如存储和取出对象的接口。注意,Hibernate会话与Web层的HttpSession没有M关系。当我们在本书中使用会话Ӟ我们指的是Hibernate会话。ؓ了区别,有时我们HttpSession对象UCؓ用户会话?o:p>
SessionFactory接口
应用?/SPAN>SessionFactoryQ会话工厂)里获得会话实例。与会话接口相比Q这个对象不够o人兴奋?/SPAN>
会话工厂当然不是轻量U的Q它打算在多个应用线E间q行׃n。典型地Q?FONT color=#ff1493>整个应用只有唯一的一个会话工厂——例如在应用初始化时被创建。然而,如果你的应用使用Hibernate讉K多个数据库,你需要对每一个数据库使用一个会话工厂?o:p>
The cascade attribute tells Hibernate that you want operations performed on a 'parent' object to be transitively applied to its 'child' or 'dependent' objects. It's applicable to all forms of collections and associations. There are several possible values to choose among. The most common are none (the default), save-update, delete, and all (which combines save-update and delete). You can also change the default from none to save-update throughout your entire mapping document by supplying a default-cascade attribute in the hibernate-mapping tag itself.
U联QcascadeQ属性告诉hibernateQ所有对父对象的操作都将付诸于子对象和依赖对象上。联可以应用于各种形式的集合和兌中。这里联的具体取值可以有多个Q最常用的有Qnone(默认?Qsave-update,delete和allQ其中包含了save-update和deleteQ。你可以在hibernate-mappiong tag中通过讄default-casade属性,修改q里的casade的默认|例如修改光认gؓsave-update?BR>
In our example, we want the tracks owned by an album to be automatically managed by the album, so that when we delete the album, its tracks are deleted. Note that we need to apply the cascade attribute both to the tracks collection and its constituent track element to achieve this. Also, by using a cascade value of all, we eliminate the need to explicitly save any Track objects we create for the album—the addAlbumTrack() method of Example 5-7 no longer needs the line:
session.save(track);
在我们的例子中,我们希望track完全由album自动掌管Q当我们删除albumӞ它的track也会被删除。注意,q里我们需要在两处讄casade属性实现此dQtracks集合和它的组成元素track。同P׃我们讲casade讄为allQ那么不再需要显式的保存为album创徏的track对象Q?-7例子中的addAlbumTrackҎ不再需要调用session.save(track)Ҏ?BR>
By telling Hibernate that it's fully responsible for the relationship between an album and its track, we enable it to persist tracks when they're added to the album as well as delete them when the album itself is deleted.
When it comes time to actually perform persistence, we ask the SessionFactory to open a Session for us (line 27), which establishes a JDBC connection to the database, and provides us with a context in which we can create, obtain, manipulate, and delete persistent objects. As long as the session is open, a connection to the database is maintained, and changes to the persistent objects associated with the session are tracked so they can be applied to the database when the session is closed. Conceptually you can think of a session as a 'large scale transaction' between the persistent objects and the database, which may encompass several database-level transactions. Like a database transaction, though, you should not think about keeping your Hibernate session open over long periods of application existence (such as while you're waiting for user input). A single session is used for a specific and bounded operation in the application, something like populating the user interface or making a change that has been committed by the user. The next operation will use a new session. Also note that Session objects are not thread safe, so they cannot be shared between threads. Each thread needs to obtain its own session from the factory.
现在开始讲q“持久化操作”。在W?7行,使用SessionFactory打开了SessionQ徏立了数据库JDBC的连接,Q该q接已经q行了初始化Q,我们可以借用它进行一pd的操作,例如Q创建、获取、操Uc删除持久化对象。只要session处于open状态,那么Ҏ据库的连接就一直徏立着Q所有对已经和sessionl定的持久化对象的操作都会被记录下来Qƈ且在session关闭的时候,所有的q些操作׃被更新到数据库中。从概念上理解,你可以将session看作一个在持久化对象和数据库之间的“大规模的事务”,它可以跨几个数据库事务。正如数据库事务那样Q你不该在程序中hibernate session长时间的打开着Q例如当E序在等待用戯入的时候)Q程序中的每个单独的操作都该采用单独的sessionQ例如在“弹出用L面,或者将用户的修Ҏ交到数据库”中Q就该用两个session。同样需要注意的是,session是线E不安全的,因此无法在线E之间共享sessionQ每个线E都应该使用SessionFactory建立自己的sess?BR>
We need to look more closely at the lifecycle of mapped objects in Hibernate, and how this relates to sessions, because the terminology is rather specific and the concepts are quite important. A mapped object such as an instance of our Track class moves back and forth between two states with respect to Hibernate: transient and persistent. An object that is transient is not associated with any session. When you first create a Track instance using new(), it is transient; unless you tell Hibernate to persist it, the object will vanish when it is garbage collected or your application terminates.
下面我们对hibernate中被映射对象的声明周期进行说明,因ؓ被映对象相关的术语是相当特别的、它的概念也是很重要的。在hibernate中,被映对象(例如E序中的Track对象Q会在两个状态中不断的来回切换:临时QtransientQ状态和持久QpersistentQ状态。未曾与sessionl定的对象就处于临时状态,例如E序中刚开始new出来的Track对象Q它处于时状态。除非你通知hibernate对它q行持久化,否则整个对象׃在垃圾收集器回收或者程序结束的时候消逝?BR>
Passing a transient mapped object to a Session's save() method causes it to become persistent. It will survive garbage collection and termination of the Java VM, staying available until it is explicitly deleted. (There is a related distinction between entities and values discussed at the beginning of Appendix A. Mapped objects that have been persisted are called entities, even if they do not currently exist as an instance in any virtual machine.) If you've got a persistent object and you call Session's delete() method on it, the object transitions back to a transient state. The object still exists as an instance in your application, but it is no longer going to stay around unless you change your mind and save it again; it's ceased being an entity.
但是Q将一个时对象用session的saveҎ保存之后Q它处于持久状态,即在垃圑֛事或者Java VMl束之后Q它都一直存在,知道该对象被明确删除为止Q在附录A开头有相关“entities和values区别”的讨论Q即使其在虚拟机中ƈ不真实存在着一个对象与之相对应Q。当你对持久化对象调用session.delete()ҎӞ该对象又变ؓ临时状态。虽然该对象仍然在程序中做ؓ一个实例存在着Q但是除非你改变L再次其持久化,那么它将很快的消逝,它的“实?entity)”也愕然而之?/P>
On the other hand, and this point is worth extra emphasis, if you haven't deleted an object (so it's still persistent), when you change its properties there is no need to save it again for those changes to be reflected in the database. Hibernate automatically tracks changes to any persistent objects and flushes those changes to the database at appropriate times. When you close the session, any pending changes are flushed.
另一斚wQ需要强调的是:如若你不删除某持久化对象Q那么当你改变其属性时Qƈ不需要显C的对其的改变进行保存。hibernate会自动的跟t到你对持久化对象的改变Q然后在适当的时候将q些改变填入到数据库中?BR>
An important but subtle point concerns the status of persistent objects you worked with in a session that has been closed, such as after you run a query to find all entities matching some criteria (you'll see how to do this in the upcoming section, 'Finding Persistent Objects'). As noted above, you don't want to keep this session around longer than necessary to perform the database operation, so you close it once your queries are finished. What's the deal with the mapped objects you've loaded at this point? Well, they were persistent while the session was around, but once they are no longer associated with an active session (in this case because the session has been closed) they are not persistent any longer. Now, this doesn't mean that they no longer exist in the database; indeed, if you run the query again (assuming nobody has changed the data in the meantime), you'll get back the same set of objects; they're still entities. It simply means that there is not currently an active correspondence being maintained between the state of the objects in your virtual machine and the database. It is perfectly reasonable to carry on working with the objects. If you later need to make changes to the objects and you want the changes to 'stick,' you will open a new session and use it to save the changed objects. Because each entity has a unique ID, Hibernate has no problem figuring out how to link the transient objects back to the appropriate persistent state in the new session.
下面讨论“在session关闭后,和sessionl定的持久化对象的状态”的问题。例如当你执行了一个查询,得到了你所需要的实体对象之后Q正如前面所q的那样Q你不应该将session保持q久Q之超出数据库操作的范_q时候你会关闭session。那么此旉些已l蝲入的映射对象处于什么状态呢Q答案是q样的,在session的打开的过E中Q它是处于持久化状态,但是它一旦不在和active sessionQ因为此时session已经关闭Q绑定,那么它就处于持久化状态。但是,qƈ不意味着他们在数据库中就不再存在Q事实上Q如果你再次执行查询Q假设此间无ZҎ据)Q那么你可以得到同L集合数据。也是_虚拟Z的对象所处的状态和数据库中的实体数据的状态之间ƈ没有必然的联pR这时候,你可以对那些对象q行自己的操作,如果你改变了一些对象的数据Qƈ惛_其存储到数据库中Q那么你必须重新建立一个sessionQ用它保存那些l过改变的数据。因为每个实体都有自q唯一IDQ因此hibernate可以很容易的在新的session中计出如何时对象重新{换ؓ相应的持久对象?BR>
Armed with these concepts and terms, the remainder of the example is easy enough to understand. Line 31 sets up a database transaction using our open session. Within that, we create a few Track instances containing sample data and save them in the session (lines 33-50), turning them from transient instances into persistent entities. Finally, line 53 commits our transaction, atomically (as a single, indivisible unit) making all the database changes permanent. The try/catch/finally block wrapped around all this shows an important and useful idiom for working with transactions. If anything goes wrong, lines 56-60 will roll back the transaction and then bubble out the exception, leaving the database the way we found it. The session is closed in the finally portion at line 63, ensuring that this takes place whether we exit through the 'happy path' of a successful commit, or via an exception that caused rollback. Either way, it gets closed as it should.
通过上面的概늚讲述和讨论,那么例子中的其他部分也就很好理解了。第31行,使用打开的session建立了数据库事务。在事务中,33Q?0行创Z一个Track对象Qƈ在session中将其保存,其从时对象变为持久对象。最后,53行提交事务,原子性的执行数据库改变。这里的try/catch/finally装了事务处理中常出现的idom。一旦出CQ何问题,56Q?0行就会回滚事务,然后抛出异常Q保证数据库l持原状。无论如何,都会执行finnally块中?3行,无论在成功执行或者出现错误回滚的情况下,都会关闭session?BR>
At the end of our method we also close the session factory itself on line 67. This is something you'd do in the 'graceful shutdown' section of your application. In a web application environment, it would be in the appropriate lifecycle event handler. [3.1] In this simple example, when the main() method returns, the application is ending.
在方法的最?7行,会关闭session factory。这是你的应用程序正帔R出时应该执行的操作。在WebE序中,你则需要用一定的生命周期事g处理器,完成此操作。在我们的例子中Q在main退出的时候,也就是程序中止的旉?BR>
You may be wondering why the taskdef for the schema update tool is inside our schema target, rather than at the top of the build file, next to the one for hbm2java. Well, I wanted it up there too, but I ran into a snag that's worth explaining. I got strange error messages the first time I tried to build the schema target, complaining there was no hibernate.properties on the class path and our compiled Track class couldn't be found. When I ran it again, it worked. Some detective work using ant -verbose revealed that if the classes directory didn't exist when the taskdef was encountered, Ant helpfully removed it from the class path. Since a taskdef can't have its own dependencies, the solution is to move it into the schema target, giving it the benefit of that target's dependencies, ensuring the classes directory exists by the time the taskdef is processed.
也许你会奇怪,Z找了schema更新工具的taskdef会在schema的target中进行定义,而不是在build文g的顶部,紧接着hbm2javaq行定义。刚开始我也是那样想的Q这里我需要解释一下这里的问题。当W一ơ构建schemaӞ我得C许多莫名奇妙的错误,报错信息是:在classpath中找不到hibernate.properties和TrackcR但是再ơ构建的时候,可以了。当使用命o“ant -verbose”时Q你可以发现其中的l节之所在。如果在ant解析到taskdefӞbuild文g中所使用的class目录q不存在Q那么ant׃自己此目录从classpath中移除。但是taskdef又无法定义自qdependenciesQ因此这里不得不其Ud到schema target中,q样也就可以使用到target的dependenciesQ保证在ant处理taskdef的时候,class目录必然已经存在?/P>
3Q新建hibernate的mapping文gQFile >> New >> Other >> Hibernate >> Mapping File.
q里需要选择mapping文g的存放位|?BR> 填入你的数据库帐P然后refreshQ选择你的数据库表。这里的table pattern,schema pattern 自己知道哦?BR> 填写你的包名Q然后确认?BR>
4Q生成各java文g?BR> 右键单击mapping文gQ然后选择hibernate synchronize-> synchronize files?q就生成各项java文g了?
如果你看不到Q那refresh整个工程的文件夹?BR>
5Q编写测试程序,使用那生成的java文gq行数据库操作?BR>
试E序中所使用的两个接口是Q{tablename}.java , {tablename}DAO.java
例如Q如果你的表名是PersonQ那么你用Person.java, PersonDAO.java
q里的PersonDAO装了hibernate的操作,更加方便了?BR>
自己观察一下吧Q会有更多收L?/P>