用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]>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 中调试通过,程序运行结果如下图所示:
   
    
  
 
  |