最近做的一个在线气象观测网站要实现一个需求:使用图表展示最近五天温湿度等气象要素的曲线变化
具体效果参考:
http://www.weatherobserve.com/showInfoIndex.jsp
下面就详述一下实现过程吧(注:相较于原网页我隐去了很多内容,本实现过程就只专注于Echarts图表实现)
一:
HTML页面部分,代码如下:
二:
Servlet部分,客户端请求提交到 ShowInfoIndex 处,先在 web.xml 里配置一下Servlet映射:
BlogExample ShowInfoIndexServlet EchartsExample.ShowInfoIndexServlet ShowInfoIndexServlet /ShowInfoIndexServlet
关于ShowInfoIndexServlet,简单说一下请求-响应中间的过程:
客户端发送图表请求给Servlet,Servlet接收到请求后先获取客户端请求查看的气象站名称,然后从数据库(SqlServer2005 Express版)中获取最近五天内该气象站所有的采集数据(装在List中),再用Gson工具将该List转换成Json对象返回给客户端,客户端接收到返回的Json对象后对其进行解析并将相应数据填入Echarts中,然后作显示;
其中Record.java是只对外提供get/set方法的用于封装数据的普通实体类,DBUtil.java是JDBC方式下专门提供Connection、Statement、ResultSet等的数据库工具类。
(原本的连接数据库并获取数据过程需经过业务逻辑层与数据访问层,较为复杂,这里隐去这两层,直接在Servlet内连接数据库并拿取数据)
Ps:墙裂建议使用PreparedStatement进行参数化查询,这样可以有效避免SQL注入!
ShowInfoIndexServlet代码如下:
其中用到了Google的Gson工具包(提供java-json对象间的转换,下载后放在 WRB-INF/lib 目录下即可)
下载地址:http://mvnrepository.com/artifact/com.google.code.gson/gson
package EchartsExample; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.text.SimpleDateFormat; import java.util.*; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.gson.Gson; /** * 响应观测记录展示页的Echarts图表数据请求(使用json格式返回客户端需要的数据) * @author zhong * */ public class ShowInfoIndexServlet extends HttpServlet { /** * */ private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); //设定客户端提交给servlet的内容按UTF-8编码 response.setCharacterEncoding("UTF-8"); //设定servlet传回给客户端的内容按UTF-8编码 response.setContentType("text/html;charset=UTF-8"); //告知浏览器用UTF-8格式解析内容 String name = request.getParameter("name"); //获取台站名参数 //获取当天在内的五天以前的0点格式字符串(用于数据库查询) Calendar cal = Calendar.getInstance(); cal.add(Calendar.DATE, -4); //获取当天在内的五天以前的日期时间 SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd 00:00:00"); //设定日期格式 String fiveDaysAgoStr = sdf1.format(cal.getTime()); //将五天前的日期时间按指定格式转换成字符串 //获取当前时间并将其转换成指定格式的字符串(用于数据库查询) Date now = new Date(); SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String nowStr = sdf2.format(now); //System.out.println(nowStr); //======================================连接数据库操作============================================================================================ /* * 连接数据库并获取五天内该名称的气象站的所有采集数据 */ Listrecords = new ArrayList (); //用一个ArrayList来盛装封装了各气象数据的对象 Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { conn = DBUtil.getConnection(); //获取与数据库的连接 String sql = "select * from alldata where data_taizhan_num = ? and data_date >= ? and data_date <= ? order by data_date asc"; //初始化SQL查询语句 pstmt = conn.prepareStatement(sql); //创建preparedStatement语句对象 pstmt.setString(1, name); //设定查询参数 pstmt.setString(2, fiveDaysAgoStr); pstmt.setString(3, nowStr); rs = pstmt.executeQuery(); //获取查询到的结果集 while (rs.next()) { //封装Record对象 Record r = new Record(); r.setTaizhan_num(rs.getString(1)); r.setDate(rs.getTimestamp(2)); r.setTem(rs.getString(3)); r.setHum(rs.getString(4)); r.setPa(rs.getString(5)); r.setRain(rs.getString(6)); r.setWin_dir(rs.getString(7)); r.setWin_sp(rs.getString(8)); //将时间转换成给定格式便于echarts的X轴日期坐标显示 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String str = sdf.format(rs.getTimestamp(2)); r.setDateStr(str); //System.out.println(r.getTem()+" | "+r.getHum()+" | "+r.getPa()+" | "+r.getRain()+" | "+r.getWin_dir()+" | "+r.getWin_sp()); records.add(r); //将封装好的Record对象放入列表容器中 } } catch (SQLException e) { System.out.println("查询出错,操作未完成!"); e.printStackTrace(); } finally { //查询结束后释放资源 DBUtil.close(rs); DBUtil.close(pstmt); DBUtil.close(conn); } //======================================连接数据库操作(完)============================================================================================ //将list中的对象转换为Json格式的数组 Gson gson = new Gson(); String json = gson.toJson(records); //System.out.println(json); //将json数据返回给客户端 response.setContentType("text/html; charset=utf-8"); response.getWriter().write(json); } }
alldata表部分数据截图:
Record类代码如下:
package EchartsExample; import java.sql.*; /** * 封装气象数据信息 * @author zhong * */ public class Record { private String taizhan_num; //台站名 private String tem; //温度 private String hum; //湿度 private String pa; //压强 private String rain; //雨量 private String win_dir; //风向 private String win_sp; //风速 private String dateStr; //观测日期(用于Echarts显示格式) private Timestamp date; //观测日期(原始格式) /** * 获取观测日期(用于echarts图表展示); * @return 观测日期值 */ public String getDateStr() { return dateStr; } /** * 设置观测日期(用于echarts图表展示); * @param dateStr 待设置观测日期值 */ public void setDateStr(String dateStr) { this.dateStr = dateStr; } /** * 获取产生该观测记录的台站名称; * @return 台站名称 */ public String getTaizhan_num() { return taizhan_num; } /** * 设置产生该观测记录的台站名称; * @param taizhan_num 待设置台站名称 */ public void setTaizhan_num(String taizhan_num) { this.taizhan_num = taizhan_num; } /** * 获取温度; * @return 温度值 */ public String getTem() { return tem; } /** * 设置温度; * @param tem 待设置温度值 */ public void setTem(String tem) { this.tem = tem; } /** * 获取湿度; * @return 湿度值 */ public String getHum() { return hum; } /** * 设置湿度; * @param hum 待设置湿度值 */ public void setHum(String hum) { this.hum = hum; } /** * 获取压强; * @return 压强值 */ public String getPa() { return pa; } /** * 设置压强; * @param pa 待设置压强值 */ public void setPa(String pa) { this.pa = pa; } /** * 获取雨量; * @return 雨量值 */ public String getRain() { return rain; } /** * 设置雨量; * @param rain 待设置雨量值 */ public void setRain(String rain) { this.rain = rain; } /** * 获取风向; * @return 风向值 */ public String getWin_dir() { return win_dir; } /** * 设置风向; * @param win_dir 待设置风向值 */ public void setWin_dir(String win_dir) { this.win_dir = win_dir; } /** * 获取风速; * @return 风速值 */ public String getWin_sp() { return win_sp; } /** * 设置风向; * @param win_sp 待设置风向值 */ public void setWin_sp(String win_sp) { this.win_sp = win_sp; } /** * 获取观测日期; * @return 观测日期 */ public Timestamp getDate() { return date; } /** * 设置观测日期; * @param date 观测日期值 */ public void setDate(Timestamp date) { this.date = date; } }
DBUitl类(数据库工具类)代码如下:
package EchartsExample; import java.sql.*; import org.apache.tomcat.jdbc.pool.DataSource; import org.apache.tomcat.jdbc.pool.PoolProperties; /** * 数据库工具类(采用了tomcat jdbc pool) * @author zhong * */ public class DBUtil { private static DataSource ds; static { //配置tomcat jdbc pool (连接池) PoolProperties p = new PoolProperties(); p.setUrl("jdbc:sqlserver://localhost:1433; DatabaseName=weather"); //设置连接的url p.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); //载入数据库驱动 p.setUsername("sa"); //用于远程连接的用户名 p.setPassword("2003NianDeDiYiChangXue"); //密码 p.setJmxEnabled(true); p.setTestWhileIdle(false); p.setTestOnBorrow(true); p.setValidationQuery("SELECT 1"); p.setTestOnReturn(false); p.setValidationInterval(30000); p.setTimeBetweenEvictionRunsMillis(30000); p.setMaxActive(100); p.setInitialSize(10); p.setMaxWait(10000); p.setRemoveAbandonedTimeout(60); p.setMinEvictableIdleTimeMillis(30000); p.setMinIdle(10); p.setLogAbandoned(true); p.setRemoveAbandoned(true); p.setJdbcInterceptors( "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+ "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"); ds = new DataSource(); ds.setPoolProperties(p); } private DBUtil() {} /** * 获取一个数据库连接(Connection); * @return Database Connection */ public static Connection getConnection() { Connection conn = null; try { conn = ds.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return conn; } /** * 关闭传入的Connection; * @param conn 待关闭的Connection */ public static void close(Connection conn) { try { if (conn != null) { conn.close(); conn = null; } } catch (SQLException e) { e.printStackTrace(); } } /** * 关闭传入的Statement; * @param stmt 待关闭的Statement */ public static void close(Statement stmt) { try { if (stmt != null) { stmt.close(); stmt = null; } } catch (SQLException e) { e.printStackTrace(); } } /** * 关闭传入的ResultSet; * @param rs 待关闭的ResultSet */ public static void close(ResultSet rs) { try { if (rs != null) { rs.close(); rs = null; } } catch (SQLException e) { e.printStackTrace(); } } }
——————————————————————————我是小小分割线
关于远程连接(包括使用Eclipse连接)SqlServer2005我再多嘴两句:
当确认连接的URL,驱动加载,用户名,密码都配置正确时,仍然抛出 java.lang.NullPointerException 的话,请打开开始菜单Microsoft SQL Server 2005软件目录下的SQL Server配置管理器:
①:启用SQL Server 2005网络配置 下的 SQLEXPRESS协议 内的 TCP/IP 协议,并右键TCP/IP协议选择属性,确保IP地址一栏最末的TCP端口为1433,具体见下图
②:启用 SQL Native Client配置 下的 客户端协议 内的 TCP/IP 协议,并右键查看TCP/IP 属性,确保端口为1433,具体见下图
③:重新启动 SQL Server 2005服务 下的 SQL Server服务,详情见下图
至此,应该就解决了java.lang.NullPointerException这个错误了。
————————————————————————我是小小分割线————————————————————————————————————
让我们再回到原来的Echarts图表显示过程上,你可以试着在后台打印看看转换出的Json字符串,关于Json的使用这里我不再多言解释