用Java在Web页面上输出统计图

  在Internet和Intranet的应用中,数据库和Web技术的结合是传统MIS系统移植到Internet(Intrant)环境的关键,已有不少厂商推出了各自的产品,但这些产品基本上是实现数据的html格式输出。在实际应用中,我们经常需要把数据以统计图的形式表现出来,例如股票行情曲线图的输出。传统的方法是把统计图作为一个图形文件放到Web服务器的目录中。这种做法虽然简单,但有明显的局限性:一是图形文件要占用较大存储空间;二是难以适应灵活复杂的查询要求;三是图形文件随数据库的变化而更新,加重了服务器的负担,也容易造成图形和数据库的不一致。
  显然,要实现上述要求,需要两个关键环节:一是从数据库中读出数据;二是根据读出的数据在Web页面中绘图。我们采用JDBC访问数据库,在Web页面中绘图则使用Java.awt包中提供的Graphics类实现。
  为便于表述,建立数据表如下:
  项目
  指标
  水产养殖
  60
  工程管理
  89
  抗旱防汛
  100
  财务
  200
  办公室
  350
  勘测设计
  80
  我们的目标就是把上表用统计图表现出来(本文用水平柱状图)。本文中所指数据库均为如上形式的表,其字段1为字符形字段,表示项目信息;字段2为数值形字段(用浮点形读出),表示指标数。实际应用中,对程序稍作修改,就可使其更加灵活通用。
  为了绘制各种形式的图表,先定义一个Graph抽象类(注意不是Graphics):
  import java.sql.*;
  import java.awt.*;
  abstract class Graph{
  int height,width;//绘图区域的高和宽
  int maxRow=50,row=0;
  //可容纳的最大记录数和实有记录数
  Color color=new Color(50,50,200);//默认绘图颜色
  float scale;//比例尺
  String[]name;//项目名缓冲区
  float[]value;//指标值缓冲区
  public Graph(Dimension d,int maxRows,Color fColor){
  height=d.height;
  width=d.width;
  name=new String[maxRows];
  value=new float[maxRows];
  color=fColor;
  }
  public void setResult(ResultSet result)
  {//把查询结果读入缓冲区
  try{
  row=0;
  while(result.next()&&row<maxRow){
  name[row]=result.getString(1);
  value[row]=result.getFloat(2);
  row++;
  }
  }
  catch(Exception ex){
  System.out.println(“\n failure!"+ex.getMessage());}
  }
  //绘制统计图的抽象方法,在子类中实现
  }
  在这个类中,定义了图表的一般特性,如颜色、比例尺、记录缓冲区等,并实现了把查询结果置入缓冲区的setResult方法。因为各种图表的绘制方法完全不同,故把draw方法定义为抽象方法,有待其子类实现。
  限于篇幅,本文仅介绍实现绘制水平柱状图的子类GraphPost,其主要功能是实现draw方法。其他子类与其类似。
  import java.sql.*;
  import java.awt.*;
  public class GraphPost extends Graph{
  float interval=0;
  //柱间空白在柱宽度(含柱间空白)
  中所占比例,0<interval0)interval=ival;
  }
  void draw(Graphics g){
  FontMetrics fontMetrics=g.getFontMetrics();
  try{
  Color bgColor=new Color(255,255,255);
  g.setColor(bgColor);
  g.fillRect(0,0,width,height);//填充背景色
  g.setColor(color);
  int maxLen=0;
  float maxValue=0;
  for(int i=0;imaxLen)
  maxLen=fontMetrics.stringWidth(name<i>);
  if(value<i>&gt;maxValue)
  maxValue=value<i>;
  }
  xMargin=maxLen+10;//
  yMargin=fontMetrics.getHeight()+10;
  int cHeight=fontMetrics.getHeight();
  int step=getStep(maxValue);
  //计算x坐标刻度单位
  scale=(width-xMargin)/maxValue;
  eHight=(height-yMargin)/row;
  g.drawRect(xMargin-1,0,width-xMargin,height
  -yMargin);//绘出图形外框
  for(int i=1;i*step10){
  mo=mo*10;
  st=(int)(value/10)/mo;
  }
  return(st+1)*mo;
  }
  }
  对数据库的查询在applet主类中用creatResultSet方法实现:
  private static ResultSet creatResultSet
  (String dStr,String sqlStr)
  /*根据给定的数据源和
  sql查询语句读取数据库*/{......}
  在applet主类的paint()方法中,调用类GraphPost的draw方法,即可实现统计图的输出。为把查询结果保存在GraphPost的数据缓冲区中供paint()方法多次使用,需要把一个GraphPost对象定义为applet主类的成员。在applet主类的init()方法中,对GraphPost对象初始化,并完成JDBC驱动程序注册。在applet主类的start()方法中,连接数据库,执行查询,并把查询结果存放到GraphPost对象的数据缓冲区中。这样,在每次回到包含该applet的页面时,都要重新查询数据库,保证用户阅读到数据库的最新信息。
  下面是applet主类代码:
  import java.applet.Applet;
  import java.sql.*;
  import java.awt.*;
  public class WebGraph extends Applet{
  GraphPost graphPost;
  public void init(){
  try{
  Color color=new Color(20,20,230);
  Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
  //注册驱动程序
  graphPost=new GraphPost(size(),30,color,0.3f);
  //初始化graphPost对象
  }
  catch(Exception e){
  System.out.println("\n"+“init error"+e.getMessage());
  }
  }
  public void start(){
  try{
  ResultSet rs;
  String dSourName="FoxPro Files";
  String sqlStr="SELECT*FROM TEST";
  rs=creatResultSet(dSourName,sqlStr);
  //从数据库中提取数据
  graphPost.setResult(rs);
  //把查询结果发送给graphPost
  rs.close();
  }
  catch(Exception e){
  System.out.println("\n"+“start error"
  +e.getMessage());
  }
  }
  public void paint(Graphics g){
  graphPost.draw(g);
  //调用graphPost的draw方法绘制柱状统计图
  }
  private static ResultSet creatResultSet(String dStr,String sqlStr)
  throws SQLException{
  /*根据给定的数据源和sql
  查询语句读取数据库*/
  String datasr=dStr;
  Connection con1=DriverManager.getConnection
  ("jdbc:odbc:"+datasr);//连接数据库
  Statement stmt1=con1.createStatement();
  return stmt1.executeQuery(sqlStr);//执行查询
  }
  }
  为了正确使用JDBC,需要安装JDBC类库(已包含在JDK1.1中)和JDBC驱动程序。这方面资料很多,笔者不再赘述。本文为使用ODBC操作FoxPro数据库,采用了JDBC-ODBC桥接方式。在网络环境下,建议使用专门的JDBC驱动程序。
  本程序在Java WorkShop 2.0中调试通过