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);
}
}
|
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());
}
}
|
Integer tripID;
Integer personID;
|
public String button1_action() {
Integer tripID = (Integer) button1.getValue();
getSessionBean1().setTripID(tripID);
return null;
}
|
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();
}
}
|
SELECT ALL TRAVEL.PERSON.PERSONID,
TRAVEL.PERSON.NAME
||TRAVEL.PERSON.JOBTITLE AS display
FROM TRAVEL.PERSON
|
NetBeans Visual Web Pack提供了一个拖拽的方式可用来非常方便地开发网络运用程序。开发人员可以直接把数据库里的数据表格和用户界面的JSF组件联系起来,参见“使用NetBeans快速开发Web Application(一)”。但很多时候把数据提取逻辑放在表现层里是违反设计规范的,特别是如果项目本身已具有数据层,那把JSF组件直接和数据表格连系在一起就更应该避免。在这种情况下,用户可以通过自定义DataProvider来利用现有的数据提取逻辑实现和JSF组件相连。这里提到的数据逻辑的实现方式可以是EJB,Web Service或者是一般的Java类。本文将解释如何自定义基于一般Java类的DataProvider。
这里用的例子是“使用NetBeans快速开发Web Application(一)”中提到的TravelAgent运用的第一页(下图)。开发分五步,第一步是启动NetBeans,第二步是新建项目并以托拽的方式建立用户界面,第三步旨在建立一个基于DAO(Data Access Object)设计模式的数据层,第四步解释如何开发用户自己的DataProvider,第五步将介绍如何把这些DataProvider和用户界面里的JSF组件联系起来。
启动NetBeans
建立一个新项目: “文件” -> “新建项目”
在对话框中选择如图所示,点击”下一步”
作如下修改后,点击“完成”
NetBeans建立“TravelAgent“项目,并打开Page1.jsp和相关的”组件面板“。在NVWP里Page1.jsp就是Web应用的第一页,NVWP已在web.xml做了相应的改变。同时NVWP建立了三个JSF受管Bean(受管Bean定义详见JSF资料)对应于Request, Session和Application,用于传送相应scope的变量。
从“组件面板”中“基本”组里拖拽“图像”,“标签”,“下拉列表”和“表”组件到Page1的设计窗口,如图所示。你需要把“图像”组件指向本文开头的图形的本地拷贝。详见“使用NetBeans快速开发Web Application(一)”。
在这一步里我们进行数据逻辑的编程,和“使用NetBeans快速开发Web Application(一)”一文不一样我们不希望把JSF组件和数据库直接相连。这里的数据逻辑运用DAO(Data Access Object)的编程模式,并运用Java Persistence API来做底层实现,你可以把底层实现改成Hibernate或者是JDBC。
建立域建模(Data Model)。NetBeans提供了一个工具可以让你根据现有的数据表格生成域建模,这是一个非常方便的工具,大大简化了编程.
切换到“运行环境”,启动运用服务器。
切换到“项目”,右键“TravelAgent”项目,选择“新建”->“文件/文件夹”
在跳出的对话框,选择“持久性”,“通过数据库生成实体类”。
选择“全部添加”
接受自动生成的类名,把包名改为“entities”.
点击“完成”后,NetBeans在”源包”目录下生成相应的Java Persistence entities。我们将运用这些entities作为本例子的域建模。
除entities以外,JPA还需要一个持久性单元,JPA EntityManager使用持久性单元来和数据库建立联系。右键“TravelAgent”项目,选择“新建”->”文件/文件夹”。
在跳出窗口选择“持久性”,“持久性单元”
数据源选择为“jdbc/travel”, “表生成策略”为“无”. “表生成策略”若为“创建”,则在部署程序时,运用服务器会试图去创建和entities对印的数据库表格。若为“删除并创建”,运用服务器会在卸载程序时删除数据表格并在部署时再创建。因为我们的数据表格时现成的所以不需要运用服务器去对表格做操作。
NetBeans生成persistence.xml并将其放在源包目录下。
DAO(Data Access Object)对象的编程。这里我们需要两个DAO,PersonDAO用来提取Person信息和TripDAO用来提取Trip信息。这里的DAO类的实现使用了JPA,你可以把JPA替换成任何其它实现比如Hibernate或者直接使用JDBC。
修改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> |
新建Java包名为“dao”,如图所示:
新建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; } } |
新建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; } } |
上述的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); } } } |
前三步做的是准备工作,这一步我们言归正传,来创建我们自己的DataProvider。PersonDataProvider用来给页面的下拉菜单组件提供数据。TripDataProvider给表组件提供数据。你将看到实际上这一步的源程序及其简单。
新建“dataprovider”Java包。
新建“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()); } } |
新建“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)); } } |
自此,DataProvider编程完毕,需要注意的是自定义的DataProvider扩展了ObjectListDataProvider,比较特殊的是在DataProvider初始时装入了一个空的数据对象给IDE提供Design Time信息,其它都是最普通的Java编程。
将自定义的DataProvider运用于用户界面上的JSF组件。
双击打开“会话Bean”
在“会话Bean”的尾部加入如下源程序。这些源程序定义了我们自己的PersonDataProvider和TripDataProvider变量和相应的getters和setters。
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; } |
右键“TravelAgent” 项目,选择“生成项目”,IDE将对所改动的文件进行编译。
右键“TravelAgent”项目,关闭此项目,并重新将这个项目打开。这一步比较奇怪,目的是使IDE的设计环境能看到我们在上一步加到“会话Bean”中的两个DataProvider变量。希望以后NetBeans能对此会有所改善。
在“TravelAgent”重新打开后,打开“Web页”目录下的Page1.jsp。
右键“下拉菜单”,选择“更改时自动提交”
右键“下拉菜单”,选择“绑定到数据…”
在弹出窗口,切换到“绑定到数据提供器”,从数据提供器的下拉菜单中选择“personDP”,“值字段”选择personid,“显示字段”选择“name”。然后“确定”。
在“dropDown1”的属性里找到“select”并按右边的…键
在跳出窗口中将dropDown1的“Selected”属性绑定到SessionBean1.personDP.value[‘personid’]。如图所示:
接下来,右键“旅行清单”表组件,选择“绑定到数据…”.
在跳出窗口“获取数据来自(G):”选择“tripDP(SessionBean1)”,然后用“<”键去除不需要的字节,如图所示:
双击下拉菜单,IDE切换到源程序窗口,加入以下程序. 这两行程序做的是在选择不同雇员时更新表组件的数据。
public void dropDown1_processValueChange(ValueChangeEvent event) { Integer personId = (Integer) dropDown1.getSelected(); getSessionBean1().getTripDP().refreshTripByPersonId(personId); } |
在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编程是很简单的,可以方便地和现有的数据层结合在一起,从而大大简化用户界面的编程,提高运用程序的开发速度。