开始搭建新的基于struts2+spring+hibernate的技术架构,基于以前对eXtremeTable的好感,决定继续采用extremetable,而不选用displaytag和valuelist。用google搜索发现eXtremeTable的作者己不再更新eXtremeTable,其把精力转移到了新的项目-JMesa。
However, now that JMesa is up to release 2.1 I am only focusing my efforts on that library. The JMesa API has really turned into the library I have always wanted to create. With the introduction of the tags and facade in release 2.1 building tables is now easier than ever. I would encourage developers that are able to run in a JDK1.5 and JSP2.0 container to start using JMesa. If you are interested in a full explanation about how JMesa came about you can read more on the JMesa site. If you are more interested in what JMesa offers over the eXtremeTable read the features list.
基于Jmesa,从数据库表Person中查询出记录,能够实现分页、排序、导出功能。同时结合Jquery,利用ajax实现对数据的删除操作。
Jmesa: 2.3
Struts2 :2.0.11
Spring:2.5
Hibernate:3.2.5
Jquery:jquery-1.2.1.pack,jquery.bgiframe.pack
Tomcat:5.5
Mysql :5.0
数据库、页面、JVM编码统一为GBK
CREATE TABLE `person` ( `id` int(10) unsigned NOT NULL auto_increment, `firstName` varchar(45) NOT NULL, `lastName` varchar(45) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=76 DEFAULT CHARSET=latin1;
<%@ taglib prefix="s" uri="/struts-tags"%> <%@ page language="java" errorPage="/error.jsp" pageEncoding="GBK" contentType="text/html;charset=GBK" %> <html> <head> <link rel="stylesheet" type="text/css" href="<%= request.getContextPath() %>/css/web.css"></link> <link rel="stylesheet" type="text/css" href="<%= request.getContextPath() %>/css/jmesa.css"></link> <!-- 对jmesa.js脚本的应用必须放到头部,而不能放到尾部 --> <script type="text/javascript" src="<%= request.getContextPath() %>/js/jmesa.js"></script> </head> <body> <p>Jmesa表单组件使用演示样例</p> <form name="personForm" action="<%= request.getContextPath() %>/list.action" method="post"> <div id="persons"> <% out.println(request.getAttribute("myhtml")); %> </div> </form> <script type="text/javascript"> function onInvokeAction(id) { setExportToLimit(id, ''); createHiddenInputFieldsForLimitAndSubmit(id); } function onInvokeExportAction(id) { var parameterString = createParameterStringForLimit(id); location.href = '<%= request.getContextPath() %>/list.action?' + parameterString; } function delUser(tableId,rowId) { var parameterString = createParameterStringForLimit(tableId); $.get("<%= request.getContextPath() %>/ajax.action?id="+rowId+"&"+parameterString, function(data) { $("#persons").html(data) }); } </script> <script type="text/javascript" src="<%= request.getContextPath() %>/js/jquery-1.2.1.pack.js"></script> <script type="text/javascript" src="<%= request.getContextPath() %>/js/jquery.bgiframe.pack.js"></script> </body> </html>
package com.mobilesoft.esales.webapp.action; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import jxl.demo.CSV; import org.apache.commons.beanutils.BeanUtils; import org.jmesa.facade.TableFacade; import org.jmesa.facade.TableFacadeImpl; import org.jmesa.limit.Limit; import com.mobilesoft.esales.dao.hibernate.Person; import com.mobilesoft.esales.service.PersonService; import com.octo.captcha.service.CaptchaServiceException; import com.opensymphony.xwork2.Action; import static org.jmesa.limit.ExportType.CSV; import static org.jmesa.limit.ExportType.JEXCEL; import static org.jmesa.limit.ExportType.PDF; import org.jmesa.core.filter.DateFilterMatcher; import org.jmesa.core.filter.MatcherKey; import org.jmesa.facade.TableFacade; import org.jmesa.facade.TableFacadeImpl; import org.jmesa.limit.Limit; import org.jmesa.util.ItemUtils; import org.jmesa.view.component.Column; import org.jmesa.view.component.Row; import org.jmesa.view.component.Table; import org.jmesa.view.editor.BasicCellEditor; import org.jmesa.view.editor.CellEditor; import org.jmesa.view.editor.DateCellEditor; import org.jmesa.view.html.HtmlBuilder; import org.jmesa.view.html.component.HtmlColumn; import org.jmesa.view.html.component.HtmlRow; import org.jmesa.view.html.component.HtmlTable; import org.jmesa.view.html.editor.DroplistFilterEditor; import sun.text.CompactShortArray.Iterator; /** * 用于演示基于jmesa(http://code.google.com/p/jmesa/)的分页、排序组件的使用方法, * 在src/java/com/mobilesoft/esales/dao/hibernate/person.sql有Person表的测试数据 * @author <a href="mailto:[email protected]">liangchuan</a> * @since 2008-03 */ public class PersonAction extends BaseAction { private PersonService personService; private List<Person> persons; private Person person; private Integer id; private String tableId; public String execute() { this.persons = personService.findAll(); //创建id为tableId为表单 //<table id="tableId" border="0" cellpadding="0" cellspacing="0" class="table" width="600px" > TableFacade tableFacade = new TableFacadeImpl("tableId", getRequest()); //设定页面分页数据 tableFacade.setItems(persons); //设定支持的查询结果导出格式为csv,excel,pdf格式 tableFacade.setExportTypes(getResponse(), CSV, JEXCEL, PDF); tableFacade.setStateAttr("restore"); Limit limit = tableFacade.getLimit(); if (limit.isExported()) { export(tableFacade); return null; } else { String html = html(tableFacade); getRequest().setAttribute("myhtml", html); } return Action.SUCCESS; } private String html(TableFacade tableFacade) { // 设定表格属性,注意此处的url用于诸如增加、删除、修改、查询操作,并不是实际的数据库表属性, //但表单需要有对应的po对新,因此需要在Person中增加此属性 tableFacade.setColumnProperties("id", "firstName", "lastName", "url"); HtmlTable table = (HtmlTable) tableFacade.getTable(); table.setCaption("测试用户信息列表"); table.getTableRenderer().setWidth("600px"); HtmlRow row = table.getRow(); HtmlColumn id = row.getColumn("id"); id.setTitle("id"); HtmlColumn firstName = row.getColumn("firstName"); firstName.setTitle("属性1"); HtmlColumn lastName = row.getColumn("lastName"); lastName.setTitle("属性2"); HtmlColumn deleteAction = row.getColumn("url"); deleteAction.setTitle("操作"); // Using an anonymous class to implement a custom editor. // 用于演示在表格中增加超链接 firstName.getCellRenderer().setCellEditor(new CellEditor() { public Object getValue(Object item, String property, int rowcount) { Object value = new BasicCellEditor().getValue(item, property, rowcount); HtmlBuilder html = new HtmlBuilder(); html.a().href().quote().append("http://www.mobile-soft.cn") .quote().close(); html.append(value); html.aEnd(); return html.toString(); } }); // Using an anonymous class to implement a custom editor. //用于演示在表格中增加javascript操作,通过jquery来实现ajax式的删除操作 deleteAction.getCellRenderer().setCellEditor(new CellEditor() { public Object getValue(Object item, String property, int rowcount) { Object value = new BasicCellEditor().getValue(item, property, rowcount); HtmlBuilder html = new HtmlBuilder(); //取得每一行的id号 Object id = ItemUtils.getItemValue(item, "id"); String js=" onclick='javascript:del(\"tableId\","+id+") '"; html.a().append(js).href().quote().append(getRequest().getContextPath()+"/remove.action?id="+id).quote().close(); html.append("删除"); html.aEnd(); return html.toString(); } }); return tableFacade.render(); // Return the Html. } private void export(TableFacade tableFacade) { tableFacade.setColumnProperties("id", "firstName", "lastName"); Table table = tableFacade.getTable(); table.setCaption("Persons Test"); Row row = table.getRow(); Column id = row.getColumn("id"); id.setTitle("id"); Column firstName = row.getColumn("firstName"); firstName.setTitle("First Name"); Column lastName = row.getColumn("lastName"); lastName.setTitle("Last Name"); tableFacade.render(); } public String login() { Boolean isResponseCorrect = Boolean.FALSE; // remenber that we need an id to validate! String captchaId = getSession().getId(); // retrieve the response String response = getRequest().getParameter("j_captcha_response"); // Call the Service method try { isResponseCorrect = CaptchaServiceSingleton.getInstance() .validateResponseForID(captchaId, response); } catch (CaptchaServiceException e) { // should not happen, may be thrown if the id is not valid } if (!isResponseCorrect) { return Action.LOGIN; } return execute(); } public String save() { this.personService.save(person); this.person = new Person(); return execute(); } /** * 用于演示ajax方式删除操作,参看pages/list.jsp * @return */ public String remove() { String deleteId = getRequest().getParameter("id"); if (deleteId != null) { personService.remove(Integer.parseInt(deleteId)); } this.persons = personService.findAll(); TableFacade tableFacade = new TableFacadeImpl("tableId", getRequest()); tableFacade.setItems(persons); // set the items tableFacade.setExportTypes(getResponse(), CSV, JEXCEL, PDF); tableFacade.setStateAttr("restore"); Limit limit = tableFacade.getLimit(); if (limit.isExported()) { export(tableFacade); return null; } else { String html = html(tableFacade); getRequest().setAttribute("myhtml", html); } return Action.SUCCESS; } public List<Person> getPersons() { return persons; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public void prepare() throws Exception { if (id != null) person = personService.find(id); } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } /** * @return the personService */ public PersonService getPersonService() { return personService; } /** * @param personService * the personService to set */ public void setPersonService(PersonService personService) { this.personService = personService; } /** * @return the tableId */ public String getTableId() { return tableId; } /** * @param tableId * the tableId to set */ public void setTableId(String tableId) { this.tableId = tableId; } }
无特别的,省略
package com.mobilesoft.esales.dao.hibernate; /** * Person entity. * * @author <a href="mailto:[email protected]">liangchuan</a>
*/ public class Person implements java.io.Serializable { // Fields private Integer id; private String firstName; private String lastName; private String url=""; // Constructors /** default constructor */ public Person() { } /** full constructor */ public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } // Property accessors public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return this.lastName; } public void setLastName(String lastName) { this.lastName = lastName; } /** * @return the url */ public String getUrl() { return url; } /** * @param url the url to set */ public void setUrl(String url) { this.url = url; } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.objectFactory" value="spring"/> <constant name="struts.devMode" value="true"/> <constant name="struts.i18n.encoding" value="GBK"/> <constant name="struts.action.extension" value="action"/> <constant name="struts.custom.i18n.resources" value="ApplicationResources,errors"/> <constant name="struts.multipart.maxSize" value="2097152"/> <constant name="struts.multipart.saveDir" value="/resources"/> <constant name="struts.ui.theme" value="css_xhtml"/> <constant name="struts.enable.SlashesInActionNames" value="true"/> <package name="person" extends="struts-default"> <interceptors> <!-- Copied from struts-default.xml and changed validation exclude methods --> <interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servlet-config"/> <interceptor-ref name="prepare"/> <interceptor-ref name="i18n"/> <interceptor-ref name="chain"/> <interceptor-ref name="debugging"/> <interceptor-ref name="profiling"/> <interceptor-ref name="scoped-model-driven"/> <interceptor-ref name="model-driven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="static-params"/> <interceptor-ref name="params"> <param name="excludeParams">dojo\..*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">cancel,execute,delete,edit,list</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> </interceptor-stack> <interceptor-stack name="fileUploadStack"> <interceptor-ref name="fileUpload"/> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> <global-results> <result name="mainMenu" type="redirect">mainMenu.html</result> <result name="dataAccessFailure">pages/dataAccessFailure.jsp</result> </global-results> <global-exception-mappings> <exception-mapping exception="org.springframework.dao.DataAccessException" result="dataAccessFailure"/> </global-exception-mappings> <action name="list" method="execute" class="personAction"> <result name="success">pages/list.jsp</result> <result name="input">pages/list.jsp</result> </action> <action name="remove" class="personAction" method="remove"> <result name="success">pages/list.jsp</result> <result name="input">pages/list.jsp</result> </action> <action name="save" class="personAction" method="save"> <result name="success">pages/list.jsp</result> <result name="input">pages/list.jsp</result> </action> <action name="login" class="personAction" method="execute"> <result name="success">pages/list.jsp</result> <result name="login">/index.jsp</result> </action> <action name="uploadFile" class="com.mobilesoft.esales.webapp.action.FileUploadAction"> <interceptor-ref name="fileUploadStack"/> <result name="input">pages/uploadForm.jsp</result> <result name="success">pages/uploadDisplay.jsp</result> <result name="cancel" type="redirect">/index.jsp</result> </action> </package> </struts>
<TBC>
http://code.google.com/p/jmesa/w/list
http://blog.csdn.net/jockCreate/archive/2008/02/20/2110310.aspx
http://blog.csdn.net/czg18596/archive/2007/09/06/1774827.aspx