我们在购物的时候,常常需要我们来选择自己的收货地址,先选择省份,再选择城市…,有没有发现:当我们选择完省份的时候,出现的城市全部都是根据省份来给我们选择的。这是怎么做到的呢?其实就是通过Ajax来完成的。使用Ajax技术让我们的网页看起来非常“智能”,会根据省份来给出对应的城市信息。
新建一个动态Web项目,比如ProvinceCity。接着导入该项目所须的jar包:
然后在WebContent根目录下创建一个city.jsp页面,其内容如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title heretitle>
head>
<body>
省份:
<select name="province" id="province">
<option value="">请选择option>
<option value="1">广东省option>
<option value="2">湖南省option>
<option value="3">湖北省option>
<option value="4">四川省option>
select>
城市:
<select name="city" id="city">
<option value="">请选择option>
select>
body>
html>
注意:这里省份的数据直接写死在页面中,而城市列表信息是要从数据库中读取的。还有,千万不要忘了在项目中导入jQuery的支持,所以应在WebContent根目录下新建一个js目录,并在该目录下导入jQuery的支持,如下:
最后,需要在MySQL数据库中新建一个baidu数据库,并在该数据库中新建一个city表,表中记录类似如下:
由于建库建表语句非常简单,所以这里我就省略不写了。
我们知道Ajax与服务器之间的交互常用的传输载体格式有三种:
由于省份与城市是有层级关系的,因此我们只能用XML或者JSON。我们这里首先就用XML来进行,后面会使用JSON,来看看他俩有什么不同的地方。开发环境搭建好了之后,接下来我们就要编写程序,实现省市联动这个需求了。
在com.meimeixia.domain包中创建一个封装城市信息的实体JavaBean。
package com.meimeixia.domain;
public class CityBean {
private int id;
private int pid;
private String cname;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
}
为了提升程序的数据库访问性能,我们通常应在项目开发中使用C3P0数据源。所以应在类目录下加入C3P0的配置文件:c3p0-config.xml。
c3p0-config.xml配置文件的内容如下:
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driverproperty>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/baiduproperty>
<property name="user">rootproperty>
<property name="password">liayunproperty>
<property name="initialPoolSize">10property>
<property name="maxIdleTime">30property>
<property name="maxPoolSize">100property>
<property name="minPoolSize">10property>
<property name="maxStatements">200property>
default-config>
<named-config name="oracle">
<property name="acquireIncrement">50property>
<property name="initialPoolSize">100property>
<property name="minPoolSize">50property>
<property name="maxPoolSize">1000property>
<property name="maxStatements">0property>
<property name="maxStatementsPerConnection">5property>
<user-overrides user="master-of-the-universe">
<property name="acquireIncrement">1property>
<property name="initialPoolSize">1property>
<property name="minPoolSize">1property>
<property name="maxPoolSize">5property>
<property name="maxStatementsPerConnection">50property>
user-overrides>
named-config>
c3p0-config>
也是为了简化JDBC的开发,我们使用Apache组织提供的一个开源JDBC工具类库——commons-dbutils-1.4.jar。然后在com.meimeixia.util包下创建一个工具类——JDBCUtil02.java,用于读取C3P0的xml配置文件创建数据源,该工具类的具体代码如下:
package com.meimeixia.util;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class JDBCUtil02 {
static ComboPooledDataSource dataSource = null;
static {
dataSource = new ComboPooledDataSource();
}
public static DataSource getDataSource() {
return dataSource;
}
/**
* 获取连接对象
* @return
* @throws SQLException
*/
public static Connection getConn() throws SQLException{
return dataSource.getConnection();
}
/**
* 释放资源
* @param conn
* @param st
* @param rs
*/
public static void release(Connection conn , Statement st , ResultSet rs){
closeRs(rs);
closeSt(st);
closeConn(conn);
}
public static void release(Connection conn , Statement st){
closeSt(st);
closeConn(conn);
}
private static void closeRs(ResultSet rs){
try {
if(rs != null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
rs = null;
}
}
private static void closeSt(Statement st){
try {
if(st != null){
st.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
st = null;
}
}
private static void closeConn(Connection conn){
try {
if(conn != null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
conn = null;
}
}
}
准备好以上这些工作之后,我们正式步入开发数据库访问层的阶段。在com.meimeixia.dao包下创建一个CityDao接口,该接口的具体代码如下:
package com.meimeixia.dao;
import java.sql.SQLException;
import java.util.List;
import com.meimeixia.domain.CityBean;
public interface CityDao {
List<CityBean> findCity(int pid) throws SQLException;
}
紧接着在com.meimeixia.dao.impl包中新建CityDao接口的实现类,即CityDaoImpl类,该实现类的代码如下:
package com.meimeixia.dao.impl;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import com.meimeixia.dao.CityDao;
import com.meimeixia.domain.CityBean;
import com.meimeixia.util.JDBCUtil02;
public class CityDaoImpl implements CityDao {
@Override
public List<CityBean> findCity(int pid) throws SQLException {
QueryRunner runner = new QueryRunner(JDBCUtil02.getDataSource());
String sql = "select * from city where pid = ?";
return runner.query(sql, new BeanListHandler<CityBean>(CityBean.class), pid);
}
}
得到前台带过来的数据(这里是省份ID),服务器端判断该数据是什么,返回对应的XML文件,内容类似下面:
<list>
<city>
<id>1id>
<pid>1pid>
<cname>深圳cname>
city>
<city>
<id>2id>
<pid>1pid>
<cname>东莞cname>
city>
<city>
<id>3id>
<pid>1pid>
<cname>广州cname>
city>
<city>
<id>4id>
<pid>1pid>
<cname>惠州cname>
city>
list>
试想服务器端查询出某个省份下的所有城市,想必是一个List集合,而要返回给前台的是一个XML文件,那么怎么把一个Bean对象转换成一个XML文件呢?这里就不得不简单学习一下xStream的使用了。
首先,在项目中导入xStream所须jar包,如下:
然后,简单介绍一下xStream的API,试着将一个Bean对象转换成一个XML文件。
那么如何给标签设置别名呢?可调用xStream中的alias方法设置别名。
如果我们想把id做成属性,就像下面这样子,那么该怎么做呢?
可调用xStream中的useAttributeFor方法将id做成属性。
我们已经会将一个Bean对象转换成一个XML文件了,那么把一个XML文件转换成一个Bean对象(逆向)又该怎么做呢?这里给出核心代码:
xStream的简单使用,我们已经会了,那么就可以完整地编写出服务器端的Servlet了,在com.meimeixia.servlet中新建一个CityServlet,其内容如下:
package com.meimeixia.servlet;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.meimeixia.dao.CityDao;
import com.meimeixia.dao.impl.CityDaoImpl;
import com.meimeixia.domain.CityBean;
import com.thoughtworks.xstream.XStream;
public class CityServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//1. 获取参数
int pid = Integer.parseInt(request.getParameter("pid"));
//2. 找出所有的城市
CityDao dao = new CityDaoImpl();
List<CityBean> list = dao.findCity(pid);
//3. 返回数据。手动拼一个xml文件,XStream转换bean对象成xml
XStream xStream = new XStream();
//想把id做出属性
//xStream.useAttributeFor(CityBean.class, "id");
//设置别名
xStream.alias("city", CityBean.class);
//转换一个对象成xml字符串
String xml = xStream.toXML(list);
//System.out.println(xml);
//返回数据
response.setContentType("text/xml;charset=UTF-8");
response.getWriter().write(xml);
} catch (SQLException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
后台我们已经分析完了,接着来分析前台。当用户选择了某个省份之后,就使用Ajax与服务器进行交互,那么在选择省份的时候就出现对应的城市信息。分析步骤如下:
这样,city.jsp页面的内容就要修改为:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title heretitle>
<script type="text/javascript" src="js/jquery-1.11.3.min.js">script>
<script type="text/javascript" src="js/city.js">script>
head>
<body>
省份:
<select name="province" id="province">
<option value="">请选择option>
<option value="1">广东省option>
<option value="2">湖南省option>
<option value="3">湖北省option>
<option value="4">四川省option>
select>
城市:
<select name="city" id="city">
<option value="">请选择option>
select>
body>
html>
其中,js目录下的city.js文件的内容如下:
$(function() {
//1. 找到省份的元素
$("#province").change(function() {
//2. 一旦里面的值发生了改变,那么就去请求该省份下面的城市数据
//$("#province").val();
var pid = $(this).val();
/*
1
1
深圳
2
1
东莞
3
1
广州
4
1
惠州
*/
$.post("CityServlet", {pid:pid}, function(data, status) {
//alert("回来数据了:" + data);
//先清空以前的值
$("#city").html("");
//遍历:
//从data数据里面找出所有的city元素,然后遍历所有的city元素,
//遍历到一个city元素,就执行一次function方法
$(data).find("city").each(function() {
//遍历出来的每一个city元素,取它的孩子(也即id和cname元素)
var id = $(this).children("id").text();
var cname = $(this).children("cname").text();
//alert("id = " + id + ", cname = " + cname);
//append:追加
$("#city").append(" + cname + "");
});
});
});
});
监听下拉框的变化,如果变化了,那么就使用异步操作去访问服务器,得到对应的数据返回给异步对象。异步对象解析服务器带过来的数据,使用DOM编程把数据动态添加到页面上。在编写代码的过程中,我们可能要注意以下几点:
前面我们已经使用过了XML作为数据载体在Ajax中与服务器进行交互。这次我们使用JSON作为数据载体在Ajax与服务器之间交互。
同上。
同上。
得到前台发送过来的数据(这里是省份ID),判断具体的数据是什么,给出对应的数据,然后使用工具把数据封装成JSON,返回给浏览器。JSON数据的内容类似下面这样:
[
{
"cname": "深圳",
"id": 1,
"pid": 1
},
{
"cname": "东莞",
"id": 2,
"pid": 1
}
,
...
]
试想服务器端查询出某个省份下的所有城市,想必是一个List集合,而要返回给前台的是一个JSON格式的数据,那么怎么把一个Bean对象转换成一个JSON格式的数据呢?首先,我们要在项目中导入JSON依赖的必要jar包,如下:
这里,必须要介绍两个常用类了:
JSONArray:该类能将一个Bean对象转换成一个JSON数组,例如,
[
{
"cname": "深圳",
"id": 1,
"pid": 1
},
{
"cname": "东莞",
"id": 2,
"pid": 1
}
,
...
]
其核心代码为:
JSONArray jsonArray = JSONArray.fromObject(list);
String json = jsonArray.toString();
System.out.println("json = " + json);
JSONObject:该类能将一个Bean对象转换成一个简单的JSON对象,例如,
{name:"zhangsan", age:18}
现在我们就可以完整地编写出服务器端的Servlet了,在com.meimeixia.servlet中新建一个CityServlet02,其内容如下:
package com.meimeixia.servlet;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.meimeixia.dao.CityDao;
import com.meimeixia.dao.impl.CityDaoImpl;
import com.meimeixia.domain.CityBean;
import net.sf.json.JSONArray;
public class CityServlet02 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//1. 获取参数
int pid = Integer.parseInt(request.getParameter("pid"));
//2. 找出所有的城市
CityDao dao = new CityDaoImpl();
List<CityBean> list = dao.findCity(pid);
//3. 把List集合转成一个JSON数据
//JSONArray ---> 变成数组([])或者集合
//JSONObject ---> 变成简单的数据,{name:"zhangsan", age:18}
JSONArray jsonArray = JSONArray.fromObject(list);
String json = jsonArray.toString();
//System.out.println("json = " + json);
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(json);
} catch (SQLException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
后台我们已经分析完了,接着来分析前台。当用户选择了某个省份之后,就使用Ajax与服务器进行交互,那么在选择省份的时候就出现对应的城市信息。分析步骤如下:
这样,city.jsp页面的内容就要修改为:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title heretitle>
<script type="text/javascript" src="js/jquery-1.11.3.min.js">script>
<script type="text/javascript" src="js/city_json.js">script>
head>
<body>
省份:
<select name="province" id="province">
<option value="">请选择option>
<option value="1">广东省option>
<option value="2">湖南省option>
<option value="3">湖北省option>
<option value="4">四川省option>
select>
城市:
<select name="city" id="city">
<option value="">请选择option>
select>
body>
html>
其中,js目录下的city_json.js文件的内容如下:
$(function() {
//1. 找到省份的元素
$("#province").change(function() {
//2. 一旦里面的值发生了改变,那么就去请求该省份下面的城市数据
//$("#province").val();
var pid = $(this).val();
/*
[
{
"cname": "深圳",
"id": 1,
"pid": 1
},
{
"cname": "东莞",
"id": 2,
"pid": 1
}
,
...
]
*/
$.post("CityServlet02", {pid:pid}, function(data, status) {
//先清空以前的值
$("#city").html("");
//再遍历,进行追加
$(data).each(function(index, c) {
//alert(c.cname);
$("#city").append(" + c.cname + "");
});
}, "json");
});
});
这次使用的是JSON作为数据载体与服务器进行交互,和XML本质上是没有区别的。只不过JSON是更加轻量级文本数据,JavaScript能够方便地获取返回的数据。