NetBeans 快速开发 Web Application(摘录)

来源:互联网 发布:云安全软件怎么样 编辑:程序博客网 时间:2024/05/22 09:42
使用NetBeans快速开发Web Application
在Java EE 5里包含了一个新的Web架构,Java Server faces,简称JSF。JSF的核心是在Web开发中引入了类似于Swing/AWT的组件结构,所有我们在浏览器里看到的HTML控件在服务器端都有一个Java对象与其对应。由于Web应用中,客户端(浏览器)和服务器端(应用服务器)分处在不同的运行环境中,客户端的输入值(如:inputText)和事件(如:点击button)不能象Swing/AWT一样直接在内存中传输,只能通过HTML表单的传送来和服务器交互,也就形成了传统的request/reponse模式。JSF从某种意义上可以说是颠覆了这种模式,由于服务器端组件的引入,我们在开发过程中可以忽略服务器和客户端不同运行环境的问题,假装我们的Web程序是运行在同一台机器上,可以象Swing/AWT一样方便地从组件取值并进行事件响应的处理。至于用户界面是如何转化成HTML,HTML又是如何转化为Java组件则由JSF框架来完成,多数情况下我们都不用知道。
NetBeans Visual Web Pack(简称NVWP)是基于JSF技术的一个工具,就象它的名字暗示的一样,你可以用它来进行图像化的Web程序开发,就像以拖拽的形式开发Swing/AWT程序一样,NVWP还对JSF进行了简化,提供了DataProvider方便地把组件和数据联系在一起,还提供了图形化界面以方便地提取数据和定义页面流程。本文将引导你开发一个网络程序,以少量的编程,快速地开发一个功能齐全的网络程序来显示数据库数据。
下载安装NetBeans 5.5和Sun Java Application Server 9(SJAS9)
 1.下载并安装NetBeans5.5,你可选择单独下载NetBeans或者是下载NetBeans和SJAS9的bundle。网址http://www.netbeans.info/downloads/index.php
 2.下载并安装NetBeans Visual Web Pack。网址http://www.netbeans.info/downloads/index.php?rs=11&p=9
 3.若你选择单独下载SJAS9,可到以下网址下载:http://www.sun.com/software/products/appsrvr_pe/index.xml
TravelAgent Web应用程序开发
在这个例子中我们要开发一个公司职员旅行清单的程序,一共有两个页面,第一页可通过一个下拉菜单来选择公司职员,同时显示这个职员所有的旅程,当一个旅程被选中就会切换到第二页显示和这个旅程相关的航班信息和旅馆信息。程序所用的数据都存于数据库,为了开发方便,NetBeans集成了一个JavaDB(Sun版的Apache Derby),NVWP安装时会建立一个travel数据库,我们的例子就使用这个数据库的数据。如果用传统的编程方式,虽然这个程序很简单也会至少花掉一个资深工程师大约半天的时间,但使用NVWP,所有时间加起来不会超过半小时。好!废话少说,开发开始。
 1          启动NetBeans
 2          建立一个新项目: “文件” -> “新建项目”
 2.1 在对话框中选择如图所示,点击”下一步”
 2.2               作如下修改后,点击“完成”
 2.3      NetBeans建立“TravelAgent“项目,并打开Page1.jsp和相关的”组件面板“。在NVWP里Page1.jsp就是Web应用的第一页,NVWP已在web.xml做了相应的改变。同时NVWP建立了三个JSF受管Bean(受管Bean定义详见JSF资料)对应于Request, Session和Application,用于传送相应scope的变量。
 2.4      在屏幕右方的“组件面板“里定义了一系列的JSF组件,在开发时可象Swing组件一样以拖拽的形式放到用户界面里。其中”Standard“目录下定义的是JSF specification里规定的标准组件,”Basic”目录下是NetBeans提供的增强型的组件,我们会用”Basic“目录下的组件来开发这个程序。
 2.5      从“Basic“目录里拖一个”Image“组件放在界面左上端。
 2.6      右点“<Image>“,在弹出窗口选”Set Image“,选择提供的”header.jpg“(本文开头就是啦)
 2.7      调整好图形后,拖入一个“Label“组件,并把Label显示文字改成Employee:
 2.8      接着拖入一个“Drop down list“和一个”Table“组件到界面中
 2.9      右击“Item1“,在弹出菜单中选”Auto-submit on Change“。这一步的目的是用户在下拉菜单中选择另一雇员时自动刷新界面。
 2.10  接下来我们要做的是把界面中的下拉菜单组件和表组件跟数据联系起来。我们的数据来自于NetBeans集成的数据库。
 2.11  切换到“运行环境“。右击连接”Travel“数据库,数据库密码和用户名相同。
 
 2.12  打开数据库表,把”Person”表拖到下拉菜单组件上,把”Trip“拖到表组件上。注意下拉菜单不再显示”Item1“而是显示”abc“,说明它已和数据源连接。表组件也有改变,现在显示的是”Trip“表的内容。
 
 2.13  表组件要显示的是下拉菜单中所选的雇员的旅行清单,所以表组件需要知道下拉菜单中选中的雇员,这个由后面的简单编程完成,同时表组件要根据选中的雇员来提取旅行信息。在前一步我们连接数据源时,NVWP在SessionBean1(也就是Session)放入了一个tripRowSet对象,这个对象用于从数据库中提取数据然后传给表组件,我们需要给它加一点约束条件。NVWP提供了一个图像化的SQL编辑器可帮助我们做这件事。在“Outline“窗口双击”tripRowSet“,打开SQL编辑器。在”PERSONID“一行加入Criteria:”=?“并回车,如图所示。” WHERE TRAVEL.TRIP.PERSONID = ?“应被加到SQL语句中。
 2.14  回到“Page1“的界面,接下来要做的是加入源程序来响应下拉菜单值改变时产生的事件。双击下拉菜单,NetBeans切换到Page1的Java视窗,并把光标停在dropDown1_processValueChanged方法。
 
 2.15  在dropDown1_processValueChange方法里加入以下程序(拷贝以下程序即可)。若你得到出错信息,先不管它,我们还有两个变量没有定义。这段源程序做的事很简单,它先从下拉菜单取得所选雇员的ID,然后把这个ID传给SessionBean1和tripRowSet(参看我们在2.13里定义的约束条件),最后更新一下tripDataProvider。tripDataProvider是给表组件提供数据的,它的更新使表组件显示新的数据。
     public void dropDown1_processValueChange(ValueChangeEvent event) {
        try {
            Integer selectedPersonID = (Integer) dropDown1.getSelected();
            getSessionBean1().setPersonID(selectedPersonID);           
            getSessionBean1().getTripRowSet().setObject(1, selectedPersonID);
            tripDataProvider.refresh();
        } catch (Exception e) {
            error("Cannot switch to person " + personDataProvider.getValue("PERSON.PERSONID"));
            log("Cannot switch to person " + personDataProvider.getValue("PERSON.PERSONID"), e);
        }
    }
 2.16  在源程序里往上翻,找到prerender方法,这个方法会在每次该页面渲染(render)前被调用,用于执行页面渲染前的预处理。
 
 2.17  在prerender方法里敲入以下程序。这段程序首先查看是否有选中的雇员,若没有(第一次访问该页)则从personDataProvider里取出第一个值,并把这个值传给tripRowSet,然后更新tripDataProvider,从而更新表组件的显示。若Session里已有选中的雇员(之前已访问过该页),则确保下拉菜单显示的是该雇员。你会得到出错信息,SessionBean1里还需要定义两个变量。
    public void prerender() {
        if (getSessionBean1().getPersonID() == null ) {
            try {
                personDataProvider.cursorFirst();
                getSessionBean1().getTripRowSet().setObject(1, personDataProvider.getValue("PERSON.PERSONID"));
                tripDataProvider.refresh();
            } catch (Exception e) {
                error("Cannot switch to person " + personDataProvider.getValue("PERSON.PERSONID"));
                log("Cannot switch to person " + personDataProvider.getValue("PERSON.PERSONID"), e);
            }
        } else {
            dropDown1.setSelected(getSessionBean1().getPersonID());
        }
     }
 2.18  这一步我们要在SessionBean1里加入两个变量,2.15和2.17两步里出现的错误就是因为这两个变量还没有定义造成的。我们先加入这两个变量的定义,再在下一步用重构把它们封装起来。加入如下变量定义,注意别敲错了。
Integer tripID;
Integer personID;
 2.19  重构:在源代码编辑器里点右键,选择 “重构” ᄄC> “封装字段”;然后在对话框选择和tripID和personID相关的方法;预览修改后,点击“进行重构”,如下面几幅图所示。NetBeans会自动帮你加入getters和setters方法。
 2.20   到这一步我们几乎已完成一半了,右键“TravelAgent”项目,选择“Run Project”。试着改变下拉菜单,你会看到表单会显示相应雇员的旅行清单。若你没有看到更新,可能是你错过了2.9那一步。接下去我们要对表单做三个修改:
 2.21      表单的抬头直接来自数据库,需要改成有意义的名称,比如:DEPCITY改成 From
 2.22      隐藏一些用户不想看到的列,比如:TRIPID和LASTUPDATED
 2.23      把TRIPID列改成button或者是hyperlink,用户可以点击进入下一页。
 2.24  在“Design”窗口,右键表组件,选择“Table Layout
 
 2.25  在弹出窗口,移除“PERSONID”,”TRIPTYPEID”和“LASTUPDATED”。
 
 2.26  然后把TRIPID的“Header Text”改成“Trip ID”, 并把“Component Type”改成“Button”
 
 2.27  把“DEPDATE”改成“Departure Date”, “DEPCITY”改成“From”, “DESTCITY”改成“To”, 然后点击“确定
 
 2.28  表单界面变成如下:
 
 2.29  当用户点击“TripID” button,我们需要把所选的“TripID”设给SessionBean1.tripID变量,以便下一页使用。双击“123”Button,在显示切换到源代码编辑器后,在button1_action方法里加入以下程序:
    public String button1_action() {
        Integer tripID = (Integer) button1.getValue();
        getSessionBean1().setTripID(tripID);
        return null;
    }   
 2.30   到这,第一页的开发完成,运行项目,你已经可以看见一个运行完好的应用程序
 3 现在我们来做第二页,这一页显示的是用户在上一页所选旅行的细节,象旅馆信息和航班信息。右键”TravelAgent”项目,选择“新建” ->“Page…”,NetBeans会生成Page2.jsp并将其打开。
 
 3.1       旅馆信息和航班信息来自数据库中的Hotel和Flight表
 
 3.2      作为练习,请用在步骤2中学到的方法做以下工作
 3.2.1 用拖拽的方式生成下图所示界面,别忘了页面底端有一个Button。
 3.2.2 把表单和相应的数据库连起来, 参看步骤2.12。
 3.2.3 给hotelRowSet和flightRowSet中加入tripID的约束条件,参看步骤2.13
 3.2.4 修改表单的抬头如图所示。参看步骤2.27
 
 3.3      在prerender()方法里加入以下code,这些code做的事情和步骤2.17里做的相同。
    public void prerender() {
        try {           
            Integer tripID = getSessionBean1().getTripID();
            getSessionBean1().getFlightRowSet().setObject(1, tripID);
            flightDataProvider.refresh();
            getSessionBean1().getHotelRowSet().setObject(1, tripID);
            hotelDataProvider.refresh();
        } catch (DataProviderException ex) {
            ex.printStackTrace();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }        
    }
 3.4      最后要做的就是定义流程,怎么从第一页到第二页,又怎么从第二页回到第一页。NVWP为此提供了一个图像化的界面可以让你很方便定义流程。回到“Design”窗口,然后在空白处点右键,选择“Page Navigation”。
 
 3.5      在跳出的Navigation窗口,Page1中的button拖到Page2即可,同理将page2中的button拖到Page1.
 
 3.6      恭喜,你已完成了一个网络应用程序的开发。运行程序,可看到以下界面:
 
 4  常见问题:
 4.1     问:数据库的数据是直接拖过来的,我怎么才知道用的是什么数据源?
      答:在我们拖数据源的时候,NVWP自动设置了数据源,数据源的定义可在“服务器资源”找到。如图所示:
 4.2      问:在Page1里面,下拉菜单显示的是雇员名称,可值是雇员ID,这是怎么回事?
       答:下拉菜单的显示和值是可以设的。右键Page1里的下拉菜单,选择“Bind To Data”,可得到如下的弹出窗口,这里你可选择下拉菜单的显示和值
 
 4.3      问:下拉菜单的值只能对应数据表格中的一列吗? 如果名字在数据表格中不是一列而被分成姓,名两列怎么办?
答: 这个问题可以解决,但不能通过图像化的方式,要手动修改给下拉菜单提供数据的personRowSet的SQL语句。打开personRowSet。
手动将SQL语句改成如下,然后再通过4.2介绍的方法重新选择显示和值。
SELECT ALL TRAVEL.PERSON.PERSONID,
           TRAVEL.PERSON.NAME||TRAVEL.PERSON.JOBTITLE AS display
FROM TRAVEL.PERSON 
 4.4      问:DataProvider只能连接数据库表吗?
       答:用DataProvider连接Web服务,EJB在NVWP的前身Sun Java Studio Creator里是可以的,目前我还没有在NVWP看到这些功能。待这些功能加入到NVWP后我会另写文章详述。
 

NetBeans Visual Web Pack提供了一个拖拽的方式可用来非常方便地开发网络运用程序。开发人员可以直接把数据库里的数据表格和用户界面的JSF组件联系起来,参见“使用NetBeans快速开发Web Application()。但很多时候把数据提取逻辑放在表现层里是违反设计规范的,特别是如果项目本身已具有数据层,那把JSF组件直接和数据表格连系在一起就更应该避免。在这种情况下,用户可以通过自定义DataProvider来利用现有的数据提取逻辑实现和JSF组件相连。这里提到的数据逻辑的实现方式可以是EJBWeb Service或者是一般的Java类。本文将解释如何自定义基于一般Java类的DataProvider


这里用的例子是“使用NetBeans快速开发Web Application(一)”中提到的TravelAgent运用的第一页(下图)。开发分五步,第一步是启动NetBeans,第二步是新建项目并以托拽的方式建立用户界面,第三步旨在建立一个基于DAOData Access Object)设计模式的数据层,第四步解释如何开发用户自己的DataProvider,第五步将介绍如何把这些DataProvider和用户界面里的JSF组件联系起来。


  1. 启动NetBeans

  2. 建立一个新项目: “文件” -> “新建项目”

    1. 在对话框中选择如图所示,点击”下一步”

    1. 作如下修改后,点击“完成”

    1. NetBeans建立“TravelAgent“项目,并打开Page1.jsp和相关的”组件面板“。NVWPPage1.jsp就是Web应用的第一页,NVWP已在web.xml做了相应的改变。同时NVWP建立了三个JSF受管Bean(受管Bean定义详见JSF资料)对应于Request, SessionApplication,用于传送相应scope的变量。

    1. 从“组件面板”中“基本”组里拖拽“图像”,“标签”,“下拉列表”和“表”组件到Page1的设计窗口,如图所示。你需要把“图像”组件指向本文开头的图形的本地拷贝。详见使用NetBeans快速开发Web Application()

  1. 在这一步里我们进行数据逻辑的编程,和“使用NetBeans快速开发Web Application()一文不一样我们不希望把JSF组件和数据库直接相连。这里的数据逻辑运用DAOData Access Object)的编程模式,并运用Java Persistence API来做底层实现,你可以把底层实现改成Hibernate或者是JDBC

    1. 建立域建模(Data Model)NetBeans提供了一个工具可以让你根据现有的数据表格生成域建模,这是一个非常方便的工具,大大简化了编程.

      1. 切换到“运行环境”,启动运用服务器。

      1. 切换到“项目”,右键“TravelAgent”项目,选择“新建”->“文件/文件夹”

      1. 在跳出的对话框,选择“持久性”,“通过数据库生成实体类”。

      1. 选择“全部添加”

      1. 接受自动生成的类名,把包名改为“entities”.

      1. 点击“完成”后,NetBeans在”源包”目录下生成相应的Java Persistence entities。我们将运用这些entities作为本例子的域建模。

      1. entities以外,JPA还需要一个持久性单元,JPA EntityManager使用持久性单元来和数据库建立联系。右键“TravelAgent”项目,选择“新建”->”文件/文件夹”。

      1. 在跳出窗口选择“持久性”,“持久性单元”

      1. 数据源选择为“jdbc/travel”, “表生成策略”为“无”. “表生成策略”若为“创建”,则在部署程序时,运用服务器会试图去创建和entities对印的数据库表格。若为“删除并创建”,运用服务器会在卸载程序时删除数据表格并在部署时再创建。因为我们的数据表格时现成的所以不需要运用服务器去对表格做操作。

      1. NetBeans生成persistence.xml并将其放在源包目录下。

    1. DAOData Access Object)对象的编程。这里我们需要两个DAOPersonDAO用来提取Person信息和TripDAO用来提取Trip信息。这里的DAO类的实现使用了JPA,你可以把JPA替换成任何其它实现比如Hibernate或者直接使用JDBC

      1. 修改web.xml放入以下配置信息。运用服务器启动时,会根据这些配置生成资源,这些资源在程序运行时可通JNDI来查找并访问。

        <persistence-context-ref>

        <persistence-context-ref-name>persistence/travel</persistence-context-ref-name>

        <persistence-unit-name>TravelAgentPU</persistence-unit-name>

        </persistence-context-ref>

        <resource-ref>

        <res-ref-name>UserTransaction</res-ref-name>

        <res-type>javax.transaction.UserTransaction</res-type>

        <res-auth>Container</res-auth>

        </resource-ref>

      2. 新建Java包名为“dao”,如图所示:

      1. 新建Java类,名为“PersonDao”PersonDao的内容如下。

        package dao;



        import entities.Person;

        import java.util.List;

        import javax.persistence.EntityManager;

        import javax.persistence.Query;



        public class PersonDao {

        public List getPersonList() {

        EntityManager em = ServiceLocator.getEntityManager();

        Query query = em.createQuery("select p from Person p");

        List personList = query.getResultList();

        return personList;

        }

         

        public Person getPersonById(Integer id){

        EntityManager em = ServiceLocator.getEntityManager();

        Person person = em.find(Person.class, id);

        return person;

        }

        }

      2. 新建Java类,名为“TripDAO”,其内容如下:

        package dao;



        import entities.Person;

        import java.util.List;

        import javax.persistence.EntityManager;

        import javax.persistence.Query;



        public class TripDao {

        public List getTripByPerson(Person person){

        EntityManager em = ServiceLocator.getEntityManager();

        Query query = em.createQuery("select t from Trip t where t.personid = :personid");

        query.setParameter("personid", person);

        List tripList = query.getResultList();

        return tripList;

        }

        }

      3. 上述的DAO类使用了ServiceLocator,其内容如下:

package dao;



import javax.naming.Context;

import javax.naming.InitialContext;

import javax.naming.NamingException;

import javax.persistence.EntityManager;



public class ServiceLocator {

public static EntityManager getEntityManager(){

try {

Context ctx = new InitialContext();

EntityManager em = (EntityManager) ctx.lookup("java:comp/env/persistence/travel"); //需和3.2.1对应

return em;

} catch (NamingException ex) {

ex.printStackTrace();

throw new RuntimeException(ex);

}

}

}



  1. 前三步做的是准备工作,这一步我们言归正传,来创建我们自己的DataProviderPersonDataProvider用来给页面的下拉菜单组件提供数据。TripDataProvider给表组件提供数据。你将看到实际上这一步的源程序及其简单。

    1. 新建“dataprovider”Java包。

    2. 新建“PersonDataProvider”Java类。内容如下。

      package dataprovider;



      import … //使用修复导入来导入必要的Java



      public class PersonDataProvider extends ObjectListDataProvider{ //extends ObjectListProvider

      List personList = new ArrayList(); //data holder

      public PersonDataProvider() {

      personList.add(new Person()); //初始DataProvider时装入,给IDE提供Design Time信息

      setList(personList);//用自己的data list来替换缺省data list

      }

      public void loadList(){ //调用时装入所有Person

      personList.clear();

      personList.addAll(new PersonDao().getPersonList());

      }

      }

    3. 新建“TripDataProvider”Java类,内容如下:

      package dataprovider;



      import … //使用修复导入来导入必要的Java



      public class TripDataProvider extends ObjectListDataProvider{ // extends ObjectListProvider

      List tripList = new ArrayList(); //data holder

      public TripDataProvider() {

      tripList.add(new Trip()); //IDE提供Design Time信息

      setList(tripList);

      }

      public void refreshTripByPersonId(Integer personId){

      tripList.clear();

      Person person = new PersonDao().getPersonById(personId);

      tripList.addAll(new TripDao().getTripByPerson(person));

      }

      }

    4. 自此,DataProvider编程完毕,需要注意的是自定义的DataProvider扩展了ObjectListDataProvider,比较特殊的是在DataProvider初始时装入了一个空的数据对象给IDE提供Design Time信息,其它都是最普通的Java编程。



  1. 将自定义的DataProvider运用于用户界面上的JSF组件

    1. 双击打开“会话Bean”

    1. 在“会话Bean”的尾部加入如下源程序。这些源程序定义了我们自己的PersonDataProviderTripDataProvider变量和相应的getterssetters

      private PersonDataProvider personDP = new PersonDataProvider();

      private TripDataProvider tripDP = new TripDataProvider();

      public PersonDataProvider getPersonDP() {

      return personDP;

      }

      public void setPersonDP(PersonDataProvider personDP) {

      this.personDP = personDP;

      }

      public TripDataProvider getTripDP() {

      return tripDP;

      }

      public void setTripDP(TripDataProvider tripDP) {

      this.tripDP = tripDP;

      }

    2. 右键“TravelAgent” 项目,选择“生成项目”,IDE将对所改动的文件进行编译。

    3. 右键“TravelAgent”项目,关闭此项目,并重新将这个项目打开。这一步比较奇怪,目的是使IDE的设计环境能看到我们在上一步加到“会话Bean”中的两个DataProvider变量。希望以后NetBeans能对此会有所改善。

    4. 在“TravelAgent”重新打开后,打开“Web页”目录下的Page1.jsp

    5. 右键“下拉菜单”,选择“更改时自动提交”



    1. 右键“下拉菜单”,选择“绑定到数据…”

    1. 在弹出窗口,切换到“绑定到数据提供器”,从数据提供器的下拉菜单中选择“personDP”,“值字段”选择personid,“显示字段”选择“name”。然后“确定”。

    1. 在“dropDown1”的属性里找到“select”并按右边的…键

    1. 在跳出窗口中将dropDown1的“Selected”属性绑定到SessionBean1.personDP.value[‘personid’]。如图所示:

    1. 接下来,右键“旅行清单”表组件,选择“绑定到数据…”.

    1. 在跳出窗口“获取数据来自(G):”选择“tripDP(SessionBean1)”,然后用“<”键去除不需要的字节,如图所示:

    1. 双击下拉菜单,IDE切换到源程序窗口,加入以下程序. 这两行程序做的是在选择不同雇员时更新表组件的数据。

      public void dropDown1_processValueChange(ValueChangeEvent event) {

      Integer personId = (Integer) dropDown1.getSelected();

      getSessionBean1().getTripDP().refreshTripByPersonId(personId);

      }

    2. init()方法中加入以下程序,这段程序是在页面初始化时调用DataProvider装入雇员信息和第一个雇员的旅行清单。

public void init() {

getSessionBean1().getPersonDP().loadList();

Person firstPerson = (Person) getSessionBean1().getPersonDP().getList().get(0);

dropDown1.setSelected(firstPerson.getPersonid());

getSessionBean1().getTripDP().refreshTripByPersonId(firstPerson.getPersonid());

}



总结:开发到此结束,运行程序。可以看出自助式的DataProvider编程是很简单的,可以方便地和现有的数据层结合在一起,从而大大简化用户界面的编程,提高运用程序的开发速度。