Java Web数据源详解

Java Web连接数据库一般有直接JDBC和数据源两种方式,

1、JDBC:

在MySQL中创建数据库:

drop    database   if   exists  login;
create   database  login;
use  login;
create   table   user (
   username        varchar ( 50 )    not   null ,
   password        varchar ( 50 ) ,
    primary   key    (username)
);

insert   into   user  (username,password)  values  ("CoderDream", " 12345678 ");

测试代码:

import  java.sql.Connection;
import  java.sql.DriverManager;
import  java.sql.ResultSet;
import  java.sql.Statement;

public   class  Test {
     public   static   void  main(String[] args) {
         try  {
             //  A: 1、2 都可以
             //  Class.forName("org.gjt.mm.mysql.Driver");  //  1
            Class.forName( " com.mysql.jdbc.Driver " );  //  2

             //  B: 连接数据库,用户名为:root,密码为空
            Connection conn  =  DriverManager.getConnection(
                     " jdbc:mysql://localhost:3306/login " ,  " root " ,  "" );

             //  C: 创建 Statement
            Statement stmt  =  conn.createStatement();

             //  D: 查询数据库中用户 CoderDream 的密码字段
            String sql  =   " select password from user where username ='CoderDream' " ;

             //  E: 得到结果集
            ResultSet rs  =  stmt.executeQuery(sql);

             //  F: 处理结果集,简单的输出password
             while  (rs.next()) {
                System.out.print(rs.getString( " password " )); // 输出结果为 12345678
            }

             //  G: 关闭资源
            rs.close();
            stmt.close();
            conn.close();
        }  catch  (Exception e) {
            e.printStackTrace();
        }
    }
}

在Struts中一般会写一个公用类,用于连接数据库:

A、创建数据库:ADDRESSBOOKSAMPLE.sql

DROP   DATABASE   IF   EXISTS  ADDRESSBOOKSAMPLE;
CREATE   DATABASE  ADDRESSBOOKSAMPLE;
USE  ADDRESSBOOKSAMPLE;
CREATE   TABLE  ADDRESSBOOK_TABLE(ID  INT ( 4 ) AUTO_INCREMENT  NOT   NULL   PRIMARY   KEY ,NAME  VARCHAR ( 25 ), PHONE  VARCHAR ( 10 ), ADDRESS  VARCHAR ( 50 ));
INSERT   INTO  ADDRESSBOOK_TABLE  VALUES ( 1 , ' Wang ' , ' 56671234 ' , ' Beijing,Haidian ' );
INSERT   INTO  ADDRESSBOOK_TABLE  VALUES ( 2 , ' Zhang ' , ' 45664568 ' , ' Shanghai,Pudong ' );
INSERT   INTO  ADDRESSBOOK_TABLE  VALUES ( 3 , ' Cheng ' , ' 56643456 ' , ' Tianjing ' );
INSERT   INTO  ADDRESSBOOK_TABLE  VALUES ( 4 , ' Zhao ' , ' 56789988 ' , ' Hainan ' );
INSERT   INTO  ADDRESSBOOK_TABLE  VALUES ( 5 , ' Cao ' , ' 56498543 ' , ' Heibei ' );

B、公用类:DbUtil.java

package  addressbook.model;

import  java.sql.Connection;
import  java.sql.DriverManager;

/**
 * <strong>DbUtil</strong> is a utility class to create a connection to our
 * sample database.
  */
public   class  DbUtil {
     static  String driverName  =   " com.mysql.jdbc.Driver " ;
     static  String dbUrl  =   " jdbc:mysql:// " ;

     public  DbUtil() {

    }

     public   static  Connection connectToDb(String hostName, String databaseName)
             throws  Exception {
        Connection connection  =   null ;
        String connName  =  dbUrl  +  hostName  +   " :3306 "   +   " / "   +  databaseName;
        Class.forName(driverName).newInstance();
        connection  =  DriverManager.getConnection(connName,  " root " ,  "" );
         return  connection;
    }

     public   static  Connection connectToDb(String databaseName)  throws  Exception {
         return  (connectToDb( " localhost " , databaseName));
    }

     public   static  Connection connectToDb()  throws  Exception {
         return  (connectToDb( " localhost " ,  " addressbooksample " ));
    }
}

C、JavaBean中调用公用类:

     public   void  insert()  throws  Exception {
        Connection con = DbUtil.connectToDb();
        PreparedStatement pStmt  =   null ;
         try  {
            pStmt  =  con.prepareStatement( " INSERT INTO  "   +  Constants.TABLENAME
                     +   "  (name,phone,address) "   +   "  values(?,?,?) " );
            con.setAutoCommit( false );

            pStmt.setString( 1 , name);
            pStmt.setString( 2 , phone);
            pStmt.setString( 3 , address);
            pStmt.executeUpdate();
            con.commit();
        }  catch  (Exception ex) {
             try  {
                con.rollback();
            }  catch  (SQLException sqlex) {
                sqlex.printStackTrace(System.out);
            }
             throw  ex;
        }  finally  {
             try  {
                pStmt.close();
                con.close();
            }  catch  (Exception e) {
                e.printStackTrace();
            }
        }
    }

     public   static  Vector search(String strSql)  throws  Exception {
        Vector addressbookBeans  =   new  Vector();
        Connection con = DbUtil.connectToDb();
        PreparedStatement pStmt  =   null ;
        ResultSet rs  =   null ;
         try  {
            pStmt  =  con.prepareStatement(strSql);
            rs  =  pStmt.executeQuery();
             while  (rs.next()) {
                addressbookBeans.add( new  AddressBookBean(rs.getString( " NAME " ),
                        rs.getString( " PHONE " ), rs.getString( " ADDRESS " )));
            }
             return  addressbookBeans;
        }  finally  {
             try  {
                rs.close();
                pStmt.close();
                con.close();
            }  catch  (Exception e) {
                e.printStackTrace();
            }
        }
    }

2、一般数据源配置

在struts-config.xml的<data-sources>标签中加入:

    <!--  ============ Data Source ===================================  -->
     < data-sources >
         < data-source  type ="org.apache.commons.dbcp.BasicDataSource" >
             < set-property  property ="autoCommit"  value ="true"   />
             < set-property  property ="description"
                value ="MySQL Data Source"   />
             <set-property property="driverClassName"
                value ="com.mysql.jdbc.Driver" />
             < set-property  property ="maxCount"  value ="10"   />
             < set-property  property ="minCount"  value ="2"   />
             < set-property  property ="username"  value ="root"   />
             < set-property  property ="password"  value =""   />
             < set-property  property ="url"
                value ="jdbc:mysql://localhost:3306/addressbooksample"   />
         </ data-source >
     </ data-sources >

同时导入以下包:

mysql-connector-java-5.1.0-bin.jar commons-dbcp-1.2.2.jar

commons-pool-1.3.jar 


注意:这里有个一个属性“

property ="driverClassName"”千万要 写成"driverClassName",而不是“driverClass”,否则会找不到!

孙卫琴的《精通Struts》就是漏掉了Name,让我调试了一个下午。

    protected   synchronized  DataSource createDataSource()
         throws  SQLException {

        

         //  Load the JDBC driver class
         if (driverClassName != null ) {
             try  {
                Class.forName(driverClassName);
            }  catch  (Throwable t) {
                String message  =   " Cannot load JDBC driver class ' "   +
                    driverClassName  +   " ' " ;
                logWriter.println(message);
                t.printStackTrace(logWriter);
                 throw   new  SQLNestedException(message, t);
            }
        }

         //  Create a JDBC driver instance
        Driver driver  =   null ;
         try  {
            driver  =  DriverManager.getDriver(url);
        }  catch  (Throwable t) {
            String message  =   " Cannot create JDBC driver of class ' "   +
                (driverClassName  !=   null   ?  driverClassName :  "" )  +  
                 " ' for connect URL ' "   +  url  +   " ' " ;
            logWriter.println(message);
            t.printStackTrace(logWriter);
             throw   new  SQLNestedException(message, t);
        }
}

从源代码我们可以看到,如果不是“driverClassName”,就会得不到相应的类名,后面的getDriver(url)就会抛出异常:

[ERROR] ActionServlet  -  Initializing application data source org.apache.struts.action.DATA_SOURCE  < org.apache.commons.dbcp.SQLNestedException: Cannot create JDBC driver of  class   ''   for  connect URL  ' jdbc:mysql://localhost:3306/addressbooksample ' > org.apache.commons.dbcp.SQLNestedException: Cannot create JDBC driver of  class ' ' for connect URL 'jdbc:mysql://localhost:3306/addressbooksample'
    at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java: 1150 )
    at org.apache.commons.dbcp.BasicDataSource.setLogWriter(BasicDataSource.java: 959 )
    at org.apache.struts.action.ActionServlet.initModuleDataSources(ActionServlet.java: 778 )
    at org.apache.struts.action.ActionServlet.init(ActionServlet.java: 331 )
    at javax.servlet.GenericServlet.init(GenericServlet.java: 212 )
    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java: 1139 )
    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java: 966 )
    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java: 3956 )
    at org.apache.catalina.core.StandardContext.start(StandardContext.java: 4230 )
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java: 760 )
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java: 740 )
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java: 544 )
    at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java: 920 )
    at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.java: 883 )
    at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java: 492 )
    at org.apache.catalina.startup.HostConfig.start(HostConfig.java: 1138 )
    at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java: 311 )
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java: 120 )
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java: 1022 )
    at org.apache.catalina.core.StandardHost.start(StandardHost.java: 736 )
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java: 1014 )
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java: 443 )
    at org.apache.catalina.core.StandardService.start(StandardService.java: 448 )
    at org.apache.catalina.core.StandardServer.start(StandardServer.java: 700 )
    at org.apache.catalina.startup.Catalina.start(Catalina.java: 552 )
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java: 39 )
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java: 25 )
    at java.lang.reflect.Method.invoke(Method.java: 597 )
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java: 295 )
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java: 433 )
Caused by: java.sql.SQLException: No suitable driver
    at java.sql.DriverManager.getDriver(DriverManager.java: 264 )
    at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java: 1143 )
      30  more

提示class ' '不能连接URL!


数据源配置好了以后,我们就可以在Action中得到DataSource。

在InsertAction的execute()方法中:

            DataSource ds  =  getDataSource(request);
            Connection con  =  ds.getConnection();
                        
            bean.insert(con);

这样,改写Bean中的insert()方法和search()方法,传入参数增加一个“Connection con”:

     public   void  insert(Connection con)  throws  Exception {
        PreparedStatement pStmt  =   null ;
         try  {
            pStmt  =  con.prepareStatement( " INSERT INTO  "   +  Constants.TABLENAME
                     +   "  (name,phone,address) "   +   "  values(?,?,?) " );
            con.setAutoCommit( false );

            pStmt.setString( 1 , name);
            pStmt.setString( 2 , phone);
            pStmt.setString( 3 , address);
            pStmt.executeUpdate();
            con.commit();
        }  catch  (Exception ex) {
             try  {
                con.rollback();
            }  catch  (SQLException sqlex) {
                sqlex.printStackTrace(System.out);
            }
             throw  ex;
        }  finally  {
             try  {
                pStmt.close();
                con.close();
            }  catch  (Exception e) {
                e.printStackTrace();
            }
        }
    }

     public   static  Vector search(Connection con, String strSql)  throws  Exception {
        Vector addressbookBeans  =   new  Vector();
        PreparedStatement pStmt  =   null ;
        ResultSet rs  =   null ;
         try  {
            pStmt  =  con.prepareStatement(strSql);
            rs  =  pStmt.executeQuery();
             while  (rs.next()) {
                addressbookBeans.add( new  AddressBookBean(rs.getString( " NAME " ),
                        rs.getString( " PHONE " ), rs.getString( " ADDRESS " )));
            }
             return  addressbookBeans;
        }  finally  {
             try  {
                rs.close();
                pStmt.close();
                con.close();
            }  catch  (Exception e) {
                e.printStackTrace();
            }
        }
    }

这样就不需使用数据库工具类 DbUtil 了。

3、插件方式,任何地方都可以得到数据源。

以一般方式配置的数据源有一个局限性,就是只能在Action中得到,因为要用到HttpServletRequest作为参数来得到数据源。


解决的办法是写一个插件,这个插件实现了Struts的PlugIn接口。


通过这个插件,我们可以在任何类中(包括Action、JavaBean和其他类)直接得到数据源,并建立连接:

 

package  addressbook.plug;

import  java.sql.Connection;
import  java.sql.PreparedStatement;
import  java.sql.ResultSet;
import  java.sql.SQLException;
import  java.sql.Statement;

import  javax.sql.DataSource;

import  org.apache.struts.action.ActionServlet;
import  org.apache.struts.action.PlugIn;
import  org.apache.struts.config.ModuleConfig;

public   class  Conn  implements  PlugIn {

     private   static  DataSource dataSource  =   null ;
     private  Connection conn  =   null ;
     private  PreparedStatement preStmt  =   null ;
     private  Statement stmt  =   null ;

     //  得到数据源
     public   void  init(ActionServlet servlet, ModuleConfig config) {
        dataSource  =  (DataSource) servlet.getServletContext().getAttribute(
                 " org.apache.struts.action.DATA_SOURCE " );
    }

     public  Conn()  throws  SQLException {
         if  (dataSource  !=   null ) {
            conn  =  dataSource.getConnection();
        }
    }

     public  ResultSet executeQuery(String sql) {
        ResultSet rs  =   null ;

         try  {
             if  (stmt  ==   null ) {
                stmt  =  conn.createStatement();
            }

            rs  =  stmt.executeQuery(sql);
        }  catch  (SQLException e) {
            e.printStackTrace();
        }

         return  rs;
    }

     public   void  executeUpdate(String sql)  throws  SQLException {
         if  (stmt  ==   null ) {
            stmt  =  conn.createStatement();
        }

        stmt.executeUpdate(sql);
    }

     public  Connection getConn() {
         return  conn;
    }

     public   void  prepareStatement(String sqlStr)  throws  SQLException {
        preStmt  =  conn.prepareStatement(sqlStr);
    }

     public   void  setString( int  index, String value)  throws  SQLException {
        preStmt.setString(index, value);
    }

     public   void  setInt( int  index,  int  value)  throws  SQLException {
        preStmt.setInt(index, value);
    }

     public   void  setBoolean( int  index,  boolean  value)  throws  SQLException {
        preStmt.setBoolean(index, value);
    }

     public   void  setLong( int  index,  long  value)  throws  SQLException {
        preStmt.setLong(index, value);
    }

     public   void  setFloat( int  index,  float  value)  throws  SQLException {
        preStmt.setFloat(index, value);
    }

     public   void  setBytes( int  index,  byte [] value)  throws  SQLException {
        preStmt.setBytes(index, value);
    }

     public   void  clearPreStmt()  throws  SQLException {
        preStmt.clearParameters();
        preStmt  =   null ;
    }

     public  ResultSet executeQuery()  throws  SQLException {
         if  (preStmt  !=   null ) {
             return  preStmt.executeQuery();
        }  else  {
             return   null ;
        }
    }

     public   void  executeUpdate()  throws  SQLException {
         if  (preStmt  !=   null ) {
            preStmt.executeUpdate();
        }
    }

     public   void  close() {
         try  {
             if  (stmt  !=   null ) {
                stmt.close();
                stmt  =   null ;
            }

             if  (preStmt  !=   null ) {
                preStmt.close();
                preStmt  =   null ;
            }

             if  (conn  !=   null ) {
                conn.close();
                conn  =   null ;
                System.out.println( " **** a connection is closed **** " );
            }
        }  catch  (Exception e) {
            System.err.println(e.getMessage());
        }
    }

     public   void  destroy() {
    }
}

在struts-config.xml中配置数据源:

     <!--  ============ Data Source ===================================  -->
     < data-sources >
         < data-source  key ="org.apache.struts.action.DATA_SOURCE"
            type ="org.apache.commons.dbcp.BasicDataSource" >
             < set-property  property ="autoCommit"  value ="true"   />
             < set-property  property ="description"
                value ="MySQL Data Source"   />
             < set-property  property ="driverClassName"
                value ="com.mysql.jdbc.Driver"   />
             < set-property  property ="maxCount"  value ="10"   />
             < set-property  property ="minCount"  value ="2"   />
             < set-property  property ="username"  value ="root"   />
             < set-property  property ="password"  value =""   />
             < set-property  property ="url"
                value ="jdbc:mysql://localhost:3306/addressbooksample"   />
         </ data-source >
     </ data-sources >

同时在struts-config.xml文件的最后配置PlugIn

     <!--  ========== PlugIn Definitions ==============================  -->
     < plug-in  className ="addressbook.plug.Conn" ></ plug-in >

这样,我们在JavaBean和其他类中就可以直接得到数据源的Connection了:

Connection con  =   new  Conn().getConn();

注意:
    在Struts1.3中已经取消了<data-sources>标签,也就是说只能在1.2版中配置,因为Apache不推荐在struts-config.xml中配置数据源。


参考资料:
1、在struts中以无参数的javabeans的方式调用struts-config.xml中配置的数据源2、struts数据源管理器
3、struts 数据源问题