返回介绍

使用双向关联

发布于 2025-04-21 21:42:12 字数 5495 浏览 0 评论 0 收藏

在新建数据的代码中,我们简单地通过把 Java 对象添加到适当的集合,就建立起曲目对象到艺人对象的链接。Hibernate 会把这些关联和对象分组转化成它为此所创建的连接表中必要的隐含项。这样,我们就能用简单易读的代码来建立和维护这些关联关系。但是要记住,我们在这里创建的这个关联是双向的,Artist 类中也有一个内含一些 Track 对象集合的关联。不用烦恼应该在那存储什么。

这种设计的方便性在于,只有当对“主要映射”(primary mapping)做出修改时,才会对修改传播到反向映射端。如果只对反向映射端做出修改(就此例而言,就是修改 Artist 对象中保存曲目的 Set 集合对象),那么修改结果不会保存下来。可惜,这就要求你在代码中要注意哪个映射是反向映射端。

好消息是我们不用为此而必须做些什么。因为我们在 Artist 映射文档中将这个关联标识为反向映射(inverse="true"),Hibernate 知道当为一个 Track 对象新增一个 Artist 关联时,另一层没说的话就是也把该 Track 作为关联而添加给了那个 Artist。

我们来构建一个简单的交互式图形应用程序,以帮助检查艺人对曲目的关联链接是否正确。这个应用程序可以让你输入艺人的姓名,然后显出与该艺人相关联的曲目。大部分代码和第一个查询测试程序非常类似。现在,创建 QueryTest2.java 文件,并输入例 4-12 所示的程序代码。

例 4-12:QueryTest2.java 的源代码

package com.oreilly.hh;

import org.hibernate.*;

import org.hibernate.cfg.Configuration;

import com.oreilly.hh.data.*;

import java.sql.Time;

import java.util.*;import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

/**

*Provide a user interface to enter artist names and see their tracks.

*/

public class QueryTest2 extends JPanel{

JList list;//Will contain tracks associated with current artist

DefaultListModel model;//Lets us manipulate the list contents

/**

*Build the panel containing UI elements

*/

public QueryTest2(){

setLayout(new BorderLayout());

model=new DefaultListModel();

list=new JList(model);

add(new JScrollPane(list),BorderLayout.SOUTH);

final JTextField artistField=new JTextField(28);

artistField.addKeyListener(new KeyAdapter(){❶

public void keyTyped(KeyEvent e){❷

SwingUtilities.invokeLater(new Runnable(){❸

public void run(){

updateTracks(artistField.getText());

}

});

}

});

add(artistField, BorderLayout.EAST);

add(new JLabel("Artist:"),BorderLayout.WEST);

}

/**

*Update the list to contain the tracks associated with an artist

*/

private void updateTracks(String name){❹

model.removeAllElements();//Clear out previous tracks

if(name.length()<1)return;//Nothing to do

try{

//Ask for a session using the JDBC information we've configured

Session session=sessionFactory.openSession();❺

try{

Artist artist=CreateTest.getArtist(name, false, session);

if(artist==null){//Unknown artist

model.addElement("Artist not found");

return;

}

//List the tracks associated with the artist

for(Track aTrack:artist.getTracks()){❻

model.addElement("Track:\""+aTrack.getTitle()+

"\","+aTrack.getPlayTime());

}

}finally{❼

//No matter what, close the session

session.close();

}

}catch(Exception e){

System.err.println("Problem updating tracks:"+e);

e.printStackTrace();

}

}

private static SessionFactory sessionFactory;//Used to talk to Hibernate

/**

*Set up Hibernate, then build and display the user interface.

*/

public static void main(String args[])throws Exception{

//Load configuration properties, read mappings for persistent classes

Configuration config=new Configuration();❽

config.configure();

//Get the session factory we can use for persistence

sessionFactory=config.buildSessionFactory();

//Set up the UI

JFrame frame=new JFrame("Artist Track Lookup");❾

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setContentPane(new QueryTest2());

frame.setSize(400,180);

frame.setVisible(true);

}

}

注意:没错,这是露骨的推销。

示例中有一大段新增加的代码用于建立一个 Swing 用户界面,这实际上还是个相当原始的界面,无法漂亮地调整窗口大小。但是,如果要处理这些界面效果上的细节,程序会变得更长,其实这些内容也不是本书应该讨论的范围。如果你想看一些如何建立丰富、高质量的 Swing 界面的例子,可以参考《Java Swing》(O'Reilly, Second Edition),这是一本很厚的巨著,介绍了 Swing 界面开发的方方面面。

❶在构造函数中我想重点介绍的只有 KeyListener,要将它加到 artistField。这段代码相当有技巧,它创建了一个匿名(anonymou)类,无论用户何时在艺人文本框中输入信息,都会调用这个匿名类的 keyTyped()方法。

❷这个方法检查输入文本框当前是否包含一个可识别的艺人名字,并更新相应的曲目显示。

❸不幸的是,在调用这个方法时,还没有更新文本框以反映最新的键盘输入,所以我们不得不通过 SwingUtilities 的 invokeLater()方法将实际的显示更新推迟到另一个匿名类(Runnable 的实例)中。这种技巧可以在 Swing“有时间处理来处理”时,再进行更新。在我们这个例子中意味着文本输入框将自己完成更新。

❹在用户输入艺人名字时调用的 updateTracks()方法正是有趣的 Hibernate 处理发生的地方。首先,这个方法清理干净列表,也就是清除之前显示过的任何曲目。如果艺人名字为空,就做到这里为止。

❺否则,这个方法会打开一个 Hibernate 会话,试着用我们在 CreateTest 里所写的 getArtist()方法查找艺人信息。这一次我们告诉这个方法,如果找不到相应的艺人,就不要创建艺人对象,所以如果用户没有输入已知艺人的名字,就会得到一个 null。

❻另一方面,如果确实找到一个 Artist 记录,则遍历该艺人的所有关联曲目集合中找到的 Track 记录,并显示每个曲目的相关信息。这将测试逆向(inverse)关联是否按照我们的预想来工作。

❼最后(这里不是故意用双关语),要确保退出该方法时关闭了会话,即使是因为异常而退出。你肯定不会希望发生会话泄露,不过,如果你想搞垮整个数据库环境的话,这就是个好方法。

❽main()方法的开始还是相同的 Hibernate 配置步骤,我们以前就看过了。

❾接着,会创建并显示用户界面框架,再设置当关闭用户界面时就结束程序。显示这个界面框架后,main()返回,从那时起,就由 Swing 事件循环控制后续流程。

在创建(或下载)好代码文件以后,还需要在 build.xml(Ant 构建文件)的末尾新增加一个构建目标(如例 4-13 所示),才能够调用这个新创建的类。

注意:这非常类似于现有的 qtest 构建目标,直接复制、调整一下就可以了。

例 4-13:运行新的查询测试程序的 Ant 构建目标

<target name="qtest2"description="Run a simple Artist exploration GUI"

depends="compile">

<java classname="com.oreilly.hh.QueryTest2"fork="true">

<classpath refid="project.class.path"/>

</java>

</target>

现在,你可以输入 ant qtest2 命令来启动程序,自己体验一下这个程序。图 4-3 是运行中的程序界面,显示了示例数据中一位艺人的曲目数据。

图 4-3 非常简单的艺人曲目浏览器

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。