评某位学员(不愿公布其姓名)使用PrepareStatement的代码,该程序代码如下:
package cn.incast;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Logger;

public class DemoClass {
 private ConnectionConfig connectionConfig = null;
 //private Logger log = Logger.getLogger("cn.incast");
 private Connection cn = null;
 private PreparedStatement pstmt = null;
 private ResultSet rs = null;
 public boolean doQuery(String name,String pass) throws IOException, ClassNotFoundException, SQLException
 {
   cn = this.connectionConfig.foundConnection();
   pstmt = cn.prepareStatement("select id from student where user=? and pass=?");
   //如果使用Statement类那么查询语句应该是"Select id from Student where name='"+name +"'pass='"+pass"'"
   //就容易出现          
   //使用SQL Server,因为SQL Server中''是转义字符
   //name  = name.replace("'","''");
   //使用MySql ,因为MySql中/是转义字符,而java中//是字符/
   name  = name.replace("'","//'");
   pstmt.setString(1,name);
   pstmt.setString(2,pass);
   rs = pstmt.executeQuery();
   
   boolean bool = true;
   if(rs.next())
   {
    //log.info("WelCome come "+ name +" !");
    this.doClose();
    return bool;
   }
   else
   {
    //log.info("The UserName and Userpassword error !");
    bool = false;
    this.doClose();
    return bool;
   }

 }

 public void doClose()
 {
  connectionConfig.close(rs,pstmt,cn);
 }
 public ConnectionConfig getConnectionConfig() {
  return connectionConfig;
 }

 public void setConnectionConfig(ConnectionConfig connectionConfig) {
  this.connectionConfig = connectionConfig;
 }
 

}
问题:
    (1)使用PrepareStatement后,参数中的单引号(')不用再进行转义处理,该学员的name  = name.replace("'","//'");语句显然有问题,弄巧成拙!
    (2)对于boolean bool = true;这条语句:变量命名为bool,显然不如命名为bFound好;另外,boolean变量的初始值设置为false要比设置为true的感觉好得多!
    (3)该程序后面的if...else...代码块太幼稚了,if与else中的代码有许多重复,应把这些重复代码放在if...else...代码块的后面,修改成如下形式,就要优雅得多:
   boolean bFound = false;
   if(rs.next())
   {
    bFound = true;
   }
   
   this.doClose();
   return bFound;
两个小技巧:
(1)在实际项目中往数据库里存密码,应采用MD5加密
(2)如果一个函数接受的参数很多,应将这些参数打包成一个对象。
思考:
 将mysql连接串写成jdbc:mysql:///itcast:能明白什么意思吗?

复习编写mysql的存储过程,并讲解在jdbc中如何调用存储过程和如何获取存储过程的返回值。李杰实验经验:如果获取是sql server的存储过程返回值,一定要用registerOutParameter方法对返回参数注册,而我们今天在Mysql中获取输出参数的值时,没有调用registerOutParameter。

李杰完成的实验结果:网络断线多长时间,rs.next都可以,但时间较长,cn.createStatement就不行了。
原因与实际问题的思考:网络是虚连接,应考虑web server与database server不在同一台服务器上的时候,网络断线或database server重新启动的情况,这也是cn.createStatement有可能失败的原因之一。

讲解DataSource,各个驱动程序中都应该提供DataSource实现,查看sql server 和mysql的驱动jar包,可以看到它们提供的DataSource实现类。
讲解了连接池的实现原理,再次引出包装设计模式,示意代码如下:
class PooledConnection implements Connection
{
 Connection realConnection = null;
 Pool pool;
 PooledConnection(Connection realConnection,Pool pool)
 {
  this.realConnection = realConnection;
  this.pool = pool;
 }

 createStatement()
 {
  realConnection.createStatement();
 }

 prepareCall()
 {
  realConnection.prepareCall();
 }
 
 close()
 {
  //realConnection.close();
  pool.release(realConnection);
 }
}

class Pool
{
 public Connection getConnection()
 {
  Connection realConnection = xxxx;
  return new PooledConnection(realConnection,this);
 } 
}

讲解各种开源框架实现的数据源:Spring的DriverManagerDataSource,Jakarta的BasicDataSource(以及相应的数据源工厂BasicDataSourceFactory),ibatis的SimpleDataSource(以及相应的数据源工厂),hibernate的DriverManagerConnectionProvider。做ibatis实验时,发现即使密码为"",但这一部分也必须设置,找出错误的原因就是在eclipse中查看源代码。
下面是课堂现场讲解中使用spring对上面几个数据源进行实验的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
 <bean id="dsdemo" class="cn.itcast.DsDemo">
  <property name="dataSource">
   <ref bean="ds"></ref>
  </property>
 </bean>
 <bean id="ds" class="com.ibatis.common.jdbc.SimpleDataSource">
  <constructor-arg>
   <props>
    <prop key="JDBC.Driver">com.mysql.jdbc.Driver</prop>
    <prop key="JDBC.ConnectionURL">jdbc:mysql:///itcast</prop>
    <prop key="JDBC.Username">root</prop>
    <prop key="JDBC.Password"></prop>
   </props>
  </constructor-arg>
 </bean>
 <!-- bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName">
   <value>com.mysql.jdbc.Driver</value>
  </property>
  <property name="url">
   <value>jdbc:mysql:///itcast</value>
  </property>
  <property name="username">
   <value>root</value>
  </property>
 </bean-->
 <!-- bean id="ds" class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName">
   <value>com.mysql.jdbc.Driver</value>
  </property>
  <property name="url">
   <value>jdbc:mysql:///itcast</value>
  </property>
  <property name="username">
   <value>root</value>
  </property>
 </bean-->
</beans>
作业:
   第一小组:dbcp实验,请在程序中硬编码使用BasicDataSourceFactory创建出BasicDataSource对象。
   第二小组:hibernate实验,请在程序中硬编码使用Hibernate的DriverManagerConnectionProvider获取连接
   第三小组:ibatis实验,请在程序中硬编码使用SimpleDataSourceFactory创建出SimpleDataSource对象。
   第四小组:由李杰做一个调用存储函数并获取存储函数返回值的实验。