最近在项目里有一个功能点需要实现页面的菜单联动,具体场景描述:页面有三个输入:接口名称、接口版本和接口参数,前两者是下拉菜单,第三个是input标签,接口名称需要从数据库表动态生成,接口版本跟随接口名称的变化而变化,接口参数根据接口名称和接口版本来确定内容。我决定用taglib实现一个标签来满足接口名称从后台数据库读取的要求,再用ajax技术来实现菜单联动的效果。
一、taglib重写select标签
整个标签要实现上述功能,需要实现两个要点:首先当然是select标签的重写,其二就是操作数据库,提取满足需求的数据。
标签代码如下:
package com.vness.mytag;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.DynamicAttributes;
import javax.servlet.jsp.tagext.TagSupport;
import com.vness.data.pullDownMenuData;
public final class tagSelectExt extends TagSupport implements DynamicAttributes {
/**
* 控件名称
*/
private String name;
private String id;
//数据库表名字
private String dbTable;
//值列
private String valueCol;
//展示列
private String nameCol;
//数据集合
private HashMap paramMap = new HashMap() ;
//默认选中
private String defaultSelected;
//需要支持的属性标签
private String onChange;
/**
* option style
* 0: 全部
* 1: 请选择
*/
private String optStyle;
public void setDynamicAttribute(String uri, String name, Object value) throws JspException
{
if ("id".equalsIgnoreCase(name)) {
setId((String)value);
return;
}
if ("name".equalsIgnoreCase(name)) {
setName((String)value);
return;
}
if ("dbTable".equalsIgnoreCase(name)) {
setDbTable((String)value);
return;
}
if ("valueCol".equalsIgnoreCase(name)) {
setValueCol((String)value);
return;
}
if ("nameCol".equalsIgnoreCase(name)) {
setNameCol((String)value);
return;
}
if ("defaultSelected".equalsIgnoreCase(name)) {
setDefaultSelected((String)value);
return;
}
if ("optStyle".equalsIgnoreCase(name)) {
setOptStyle((String)value);
return;
}
if ("onChange".equalsIgnoreCase(name)) {
setOnChange((String)value);
return;
}
}
/*
* Setter Mothed Start!
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDbTable() {
return dbTable;
}
public void setDbTable(String dbTable) {
this.dbTable = dbTable;
}
public String getValueCol() {
return valueCol;
}
public void setValueCol(String valueCol) {
this.valueCol = valueCol;
}
public String getNameCol() {
return nameCol;
}
public void setNameCol(String nameCol) {
this.nameCol = nameCol;
}
public String getDefaultSelected() {
return defaultSelected;
}
public void setDefaultSelected(String defaultSelected) {
this.defaultSelected = defaultSelected;
}
public String getOptStyle() {
return optStyle;
}
public void setOptStyle(String optStyle) {
this.optStyle = optStyle;
}
public void setId(String id) {
this.id = id;
}
public void setOnChange(String value) {
// TODO Auto-generated method stub
this.onChange=value;
}
private void setParamMap() throws Exception {
String condition=genSql();
pullDownMenuData pdd=new pullDownMenuData();
try {
pdd.selectData(condition);
} catch (Exception e) {
// TODO Auto-generated catch block
throw new Exception("查询数据失败!"+e);
}
paramMap=pdd.getData();
}
/*
* Setter Mothed End!
*/
private String genId() {
if( id == null ) {
id = "select_" + Math.random();
}
return id;
}
public String genSql(){
StringBuffer buf = new StringBuffer();
buf.append("select distinct ");
buf.append(nameCol+", "+valueCol+" ");
buf.append("from "+ dbTable);
return buf.toString();
}
@Override
public int doEndTag() throws JspException {
//查询数据库,获取数据
try {
setParamMap();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
StringBuffer buf = new StringBuffer();
buf.append("<select ");
buf.append("id='" + genId() + "' ");
if (!(name==null||name.equalsIgnoreCase(""))) {
buf.append("name='" + name + "' ");
}
if (!(onChange==null||onChange.equalsIgnoreCase(""))) {
buf.append("onChange='" + onChange + "' ");
}
buf.append(" >");
if("0".equals(optStyle)){
buf.append("<option value=''>全部</option>");
}
else if("1".equals(optStyle)){
buf.append("<option value=''>请选择</option>");
}
if (paramMap != null) {
Iterator iter = paramMap.keySet().iterator();
while (iter.hasNext()) {
String key = (String) iter.next();
String value = (String) paramMap.get(key);
buf.append("<option value='" + value + "'");
if (value.equalsIgnoreCase(defaultSelected)) {
buf.append(" selected ");
}
buf.append(" >" + key + "</option>");
buf.append("\r\n");
}
buf.append("</select>");
}
try {
this.pageContext.getOut().write(buf.toString());
} catch (IOException e) {
throw new JspException(e);
}
name = null;
dbTable = null;
valueCol = null;
nameCol = null;
if(paramMap != null ) {
paramMap.clear();
}
return EVAL_PAGE;
}
@Override
public int doStartTag() throws JspException {
return super.doStartTag();
}
}
如果自定义标签要实现数据的动态变化,必须实现DynamicAttributes接口,重写setDynamicAttribute方法。该类的所有属性除了paramMap是作为从后台传递菜单内容到前端的数据载体外,其他属性都是标签定义的。
我还特地实现了一个数据处理类,代码如下:
package com.vness.data;
import java.sql.ResultSet;
import java.util.HashMap;
import com.vness.tool.dbTool;
public class pullDownMenuData{
private HashMap<String, String> data;
public void selectData(String sql) throws Exception{
dbTool dt= new dbTool();
dt.createConnection();
ResultSet rs = dt.executeSQL(sql);
data=new HashMap<String, String>();
while(rs.next()){
String name = rs.getString(1) ;
String value = rs.getString(2) ;
data.put(name, value);
}
}
public HashMap<String, String> getData(){
return data;
}
}
整个类很简单,就是把数据从后台读取出来,放到map中,传递给标签类。
另外,还有一个数据库工具类,因为时间原因,实现的很简单,只实现了简单的连接、查询等功能。由于我实现的这个功能是内部的一个平台,不对外开发,并发量不大,所以也没用连接池,仅仅用了最简单的jdbc。如果并发高的话,建议使用别的方式实现。
package com.vness.tool;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class dbTool {
private Connection con;
private ResultSet result;
private static String url ;
private static String username ;
private static String password ;
static{
try{
//加载MySql的驱动类
Class.forName("oracle.jdbc.driver.OracleDriver") ;
}catch(ClassNotFoundException e){
e.printStackTrace() ;
}
try {
loadInit() ;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void loadInit() throws Exception{
Properties prop = new Properties();
FileInputStream fis;
fis = new FileInputStream("dbprop.properties");
prop.load(fis);
url=prop.getProperty("url");
username=prop.getProperty("username");
password=prop.getProperty("password");
}
public Connection createConnection() throws Exception{
if(url==null||username==null||password==null){
throw new Exception("database param is error!");
}
con = DriverManager.getConnection(url , username , password ) ;
return con;
}
public void closeConnection(){
if(con!=null){
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public ResultSet executeSQL(String sql){
try {
Statement stmt=con.createStatement();
result = stmt.executeQuery(sql) ;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
public static void main (String[] x) throws Exception
{
dbTool dt= new dbTool();
Connection con=dt.createConnection();
ResultSet rs = dt.executeSQL("select api_id,api_cnm from mpoptapii ");
try {
while(rs.next()){
String name = rs.getString(1) ;
String pass = rs.getString(2) ;
}
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
dt.closeConnection();
}
}
二、标签使用的配置
最后是需要配置了,配置完了就可有使用标签了。配置主要包括三个方面:标签的定义声明、web.xml的声明和页面的引用。
hitag.tld文件:定义标签
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>2.2.3</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>test</short-name>
<tag>
<name>hiselect</name>
<tag-class>com.vness.mytag.tagSelectExt</tag-class>
<dynamic-attributes>true</dynamic-attributes>
</tag>
</taglib>
web.xml:声明标签,在web.xml的最后加入如下配置。
<taglib>
<taglib-uri>/WEB-INF/tlds/hitag.tld</taglib-uri>
<taglib-location>/WEB-INF/tlds/hitag.tld</taglib-location>
</taglib>
最后,就是页面的引入了。
<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%> <%@ taglib uri="/WEB-INF/tlds/hitag.tld" prefix="test" %> <html> <head> <script type="text/javascript"> </script> </head> <body> <tr>
<td><td>接口名称</td>
<test:hiselect name="tstNm" id="tst_nm" optStyle="1" dbTable="mpoptapii" valueCol="api_id" nameCol="api_cnm" defaultSelected="1"/> </td>
</tr>
</body>
</html>