65.9K
CodeProject 正在变化。 阅读更多。
Home

dataGlue - 更快地构建,更智能地工作 - 事件驱动,组件化,数据库绑定的 Java Web 应用程序

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.47/5 (12投票s)

2003年10月9日

4分钟阅读

viewsIcon

46822

使用 Echo Web 框架和 JDO 构建事件驱动、组件化、数据库绑定的 Java Web 应用程序。

引言

Martin Fowler 曾说过,让项目更快的最佳方法是提高代码中 9/10 时间消耗的那 1/10 部分的速度。让我们将这个原则应用于项目开发。考虑到我们的大多数项目主要由与数据库关联的表单和表格构成,如果我们能加快这些组件的开发速度,那么整个项目的开发速度就会更快。

为此,我创建了一些组件,它们可以使 Web(未来还有 Swing)表单的使用更加容易,并通过使用 JDO 使数据库访问变得透明。表格的构建和处理也变得更加容易。

更具体地说

您不想像这样构建一个表单吗

而不必担心数据检索、插入或更新,以及保存到数据库?或者您不想用几行代码就构建一个像这样的分页、多选、可按列排序的表格吗?

对于第一个版本,该项目扩展了 Echo Web 框架 (http://www.nextapp.com/products/echo),其组件项目 Echopoint (http://echopoint.sf.net/) 和 TJDO (http://tjdo.sf.net/)。

开始之前

本教程假设您具备 Java 编程语言的基本工作知识。如果您不具备,在开始使用 dataGlue 之前,您需要阅读 Sun 的 Java 教程。本教程还假定您熟悉 Echo 和 JDO 的概念(Swing 也会有所帮助)。

您可以在 这里这里 找到 Echo 教程。JDO 信息开源 TJDO

dataGlue petstore 示例应用程序安装

只需下载 petstore.war(下载详情在本教程底部),然后像安装任何 Java Web 应用程序一样安装它。如果您使用的是 Tomcat,只需将其复制到 CATALINA_HOME/webapps 目录中。在 petstore.war/WEB-INF/jdo.properties 中更改数据库 URL、用户名和密码。默认设置为 MySQL,在 localhost 上,使用“test”数据库,用户名为“root”,密码为空。

第一步

  1. 创建您的应用程序实例,该实例将创建您的主窗口。要做到这一点,请扩展 dataglue.application.BaseInstance
    import dataglue.petstore.views.*;
    import nextapp.echo.*;
    import dataglue.application.*;
    
    public class ApplicationInstance extends BaseInstance {
      
    public Window init() {
      Window window = new Window();
      ContentPane content = new ContentPane();
      //set font for the application
      content.setFont(new Font(Font.ARIAL, Font.PLAIN, 8));
      content.add(Filler.createVerticalStrut(30));
      //instantiate the panel that shall be first diplayed on the window
      content.add(new Main(this));
      window.setContent(content);
      return window;
     }
    
    }
  2. 创建必须扩展 nextapp.echo.EchoServer 的控制器 Servlet
    public class Tutorial extends nextapp.echoservlet.EchoServer {
    
    //register echopoint components
    static {
    echopoint.ui.Installer.register();
    }
    
    //instatiate application instance that creates your main Window
    public EchoInstance newInstance() {
    return new dataglue.petstore.application.ApplicationInstance();
    }
  3. 编辑您的 web.xml 文件

    您必须添加您的控制器 Servlet 和您的 jdo.properties 文件的路径,在其中指定所有数据库 URL、驱动程序和其他属性。默认文件名为 jdo.properties,位于 /WEB-INF/ 目录中。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, 
                 Inc.//DTD Web Application 2.3//EN" 
                 "http://java.sun.com/dtd/web-app_2_3.dtd">
    <web-app>
    
    <context-param>
     <param-name>internationalisation</param-name>
     <param-value />
     </context-param>
     <context-param>
    <param-name>jdo.properties</param-name>
    <param-value>/WEB-INF/jdo.properties</param-value>
    </context-param>
    
    <servlet>
      <servlet-name>tutorial</servlet-name>
      <servlet-class>dataglue.tutorial.Tutorial</servlet-class>
    </servlet>
    <servlet-mapping>
      <servlet-name>tutorial</servlet-name>
      <url-pattern>/tutorial</url-pattern>
    </servlet-mapping>
    
    </web-app>
  4. 编辑您的数据库属性

    修改您的 jdo.properties 文件

    javax.jdo.PersistenceManagerFactoryClass=
             com.triactive.jdo.PersistenceManagerFactoryImpl
    javax.jdo.option.ConnectionDriverName=org.gjt.mm.mysql.Driver
    javax.jdo.option.ConnectionURL=jdbc\:mysql\:///test
    javax.jdo.option.ConnectionUserName=root
    javax.jdo.option.ConnectionPassword=
    javax.jdo.option.NontransactionalRead=true
    com.triactive.jdo.autoCreateTables=true

    注意:javax.jdo.option.NontransactionalRead 必须设置为 true。此示例文件适用于 TJDO 实现。

  5. 创建您的应用程序面板,例如
    public class TutorialPanel extends Panel{
    //.. application logic
    }
  6. 部署、测试等。

表单

首先,让我们做一个非常简单的例子:我们有一个 POJO Customer.java

public class Customer{

private String firstName;
private String lastName;
private String email;

private String username;
private String password;

private String status;
private String addr1;
private String addr2;
private String city;
private String state;
private String zip;
private String country;
private String phone;
private String langPref;

//get/set accesors for all properties
}

假设您熟悉 JDO 概念,我们创建其 JDO 描述文件 Customer.jdo,进行编译,然后使用 TJDO 方法进行增强。

<?xml version="1.0"?>
<!DOCTYPE jdo SYSTEM "file://javax/jdo/jdo.dtd">

<jdo>
<package name="">
<class name="Customer">
<field name="username">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="password">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="langPref">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="email">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="firstName">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="lastName">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="status">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="addr1">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="addr2">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="city">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="state">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="zip">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="country">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
<field name="phone">
<extension vendor-name="triactive" key="length" value="max 32"/>
</field>
</class>
</package>
</jdo>

最终,我们得到一个增强后的类,名为 Customer.class,我们将使用它。

现在,让我们创建一个表单,负责在数据库中创建和更新客户。我们扩展 dataglue.UI.DataForm 组件。DataForm 设置为 XYLayout,这意味着组件在浏览器中是绝对/相对定位的。我们必须指定要绑定到表单的持久对象类(Customer.class)及其宽度和高度(540,480)像素。

import nextapp.echo.*;
import dataglue.UI.*

public class CustomerForm extends DataForm {

我们创建两个构造函数。一个用于创建,另一个用于通过 ID 检索客户,填充表单并更新更改,这非常简单。

public CustomerForm(BaseInstance instance) {
super(instance, Customer.class, 540,480);
}

public CustomerForm(BaseInstance instance,Object id) {
super(instance, id, Customer.class, 540,480);
}

现在,我们声明在这个表单中使用的组件。

TextField usernameField =null;
TextField passwordField =null;
TextField fname =null;
TextField lname =null;
TextField street =null;
TextField city =null;
TextField code =null;
TextField phone =null;
TextField email = null;

SelectField state =null;
SelectField country =null;
SelectField language =null;

PushButton saveButton =null;
PushButton cancelButton =null;

我们现在必须覆盖 DataForm 中的 initComponents() 方法,该方法负责组件的初始化。

protected void initComponents()
{
//initialise
usernameField = new TextField();
passwordField = new TextField();
fname = new TextField();
lname = new TextField();
email = new TextField();
street = new TextField();
city = new TextField();
code = new TextField();
phone = new TextField();

saveButton = new PushButton(" Save ");
cancelButton = new PushButton(" Cancel ");

state = new SelectField(new String[]{"N/A","CA","FL","NY"});
country = new SelectField(new String[]{"US","UK","Romania","France"});
language = new SelectField(new String[]
   {"English (US)","English (UK)","French (FR)","French (CA)","Romanian"});


//place components on form (relative to left top corner)

this.add(cancelButton, new XYConstraints(462, 449));
this.add(saveButton, new XYConstraints(395, 449));
this.add(new Label("Username:"), new XYConstraints(150, 60));
this.add(new Label("Telephone:"), new XYConstraints(155, 322));
this.add( new Label("Country:"), new XYConstraints(167, 298));
this.add(new Label("State:"), new XYConstraints(180, 268));
this.add(new Label("City"), new XYConstraints(190, 237));
this.add(new Label("Street Address:"), new XYConstraints(131, 209));
this.add(new Label("Last Name:"), new XYConstraints(161, 185));
this.add(new Label("First Name:"), new XYConstraints(154, 162));
this.add(new Label("Password:"), new XYConstraints(147, 83));
this.add(usernameField, new XYConstraints(231, 57));
this.add(passwordField, new XYConstraints(231, 80));
this.add(fname, new XYConstraints(231, 159));
this.add(lname, new XYConstraints(231, 182));
this.add(street, new XYConstraints(231, 206));
this.add(city, new XYConstraints(231, 234));
this.add(state, new XYConstraints(231, 265));
this.add(new Label("Postal Code:"), new XYConstraints(318, 268));
this.add(code, new XYConstraints(390, 265));
this.add(new Label("User information"), new XYConstraints(51, 25));
this.add(new Label("Preferences"), new XYConstraints(51, 363));
this.add(new Label("Address information"), new XYConstraints(51, 132));
this.add(country, new XYConstraints(231, 295));
this.add(phone, new XYConstraints(231, 319));
this.add(new Label("Email:"), new XYConstraints(114, 403));
this.add(email, new XYConstraints(231, 400));
this.add(new Label("Language:"), new XYConstraints(157, 379));
this.add(language, new XYConstraints(231, 376));

现在是最重要的一部分。组件与持久类属性之间的绑定。

//simple bindings
this.addSimpleDataBinding(usernameField, "username");
this.addSimpleDataBinding(passwordField, "password");
this.addSimpleDataBinding(fname, "firstName");
this.addSimpleDataBinding(lname, "lastName");
this.addSimpleDataBinding(street, "addr1");
this.addSimpleDataBinding(city, "city");
this.addSimpleDataBinding(code, "zip");
this.addSimpleDataBinding(phone, "phone");
this.addSimpleDataBinding(email, "email");

//list bindings
this.addListDataBinding(state,"state",new String[]{"N/A","CA","FL","NY"});
this.addListDataBinding(country,"country",new String[]
  {"US","UK","Romania","France"});
this.addListDataBinding(language,"langPref",new String[]
  {"English (US)","English (UK)","French (FR)","French (CA)","Romanian"});

现在,我们必须告诉我们的保存按钮在单击时做什么。

this.saveButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
formSave();
notifyFormSaved();
}
});

this.cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
notifyFormClosed();
}
});
}
  • formSave() 会透明地在数据库中执行适当的创建或更新操作。
  • notifyFormSaved() - 通知所有监听器表单已保存。
  • notifyFormClosed() - 通知所有监听器表单未保存但已取消。

就是这样了!

简单吧?我认为这大大缩短了创建与数据库绑定的表单所需的时间。

DataTable 组件。

此组件的目的是非常轻松地在表格中显示数据库查询结果。此表格还必须支持自动排序、分页、单选/多选、自动更新和空结果集通知。

为了在 dataGlue 中执行查询,我们使用

QueryHandler qh = persistenceService.newQuery(Customer.class,"email!=\"\"",
                                                 "firstName ascending");

这应该会返回所有电子邮件不为空的客户,并按名字升序排序。

现在,让我们创建一个 DataTable 来显示它,并创建一个监听器来监听其中的选择事件。

TableRowSelectionListener tableRowListener = new TableRowSelectionListener()
{public void rowSelected(TableRowSelectionEvent e)
{
doSomethingWithSelectedCustomer((Customer)e.getSelectedObject());
}
});

DataTable dataTable = new DataTable(qh, null, Customer.class,
       "There are no customer with email.",10,new DataColumn[] {
new DataColumn("Customer Username","username", 25, 
                     Button.class,EchoConstants.LEFT),
new DataColumn("First Name","firstName", 25, 
                     Label.class, EchoConstants.LEFT),
new DataColumn("Last Name","lastName", 25, 
                     Label.class, EchoConstants.LEFT),
new DataColumn("Email","email", 25, Label.class, EchoConstants.LEFT)
},tableRowListener);

不需要关闭 QueryHandler,因为它在信息加载后会被 dataTable 关闭。您应该看到这样的结果。

默认情况下,dataTable 启用了多选,每页有 10 条结果,如果结果集为空,将显示消息“”而不是空表。

如果您想获取所有选定的行,只需使用

Collection selectedCustomers = dataTable.getSelectedObjects();

很简单,不是吗?这就是整个想法,简化事情。

下载演示、源代码和二进制文件

许可证

dataglue 是开源软件,根据 GNU LGPL 许可条款分发。

© . All rights reserved.