评某位学员(不愿公布其姓名)使用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对象。
第四小组:由李杰做一个调用存储函数并获取存储函数返回值的实验。