怎么样使用数据库连接池和jdbc调用存储过程?

怎么样使用数据库连接池和jdbc调用存储过程?

数据库连接池:
数据库连接不仅仅是在应用服务器与数据库之间建立一个Socket Connection,连接建立之后,还需要交换若干次数据(比如验证用户密码,权限等),然后,数据库开始初始化连接会话句柄,记录联机日志,为此连接分配相应的处理进程和系统资源。系统如此繁忙,如果我们只是简单的扔过去两个SQL语句,然后就将此连接抛弃,实在可惜,数据库连接池正是解决了这个问题。其基本原理就是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回的方法。

下面用几个小例子来讨论一下最常用的几个DataSource:
先建一个jdbc.properties属性文件:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///it315
username=root
password=

再建一个XML配置文件,这里面有三种方法可以获取DataSource,包括Jakarta的BasicDataSource,ibatis的SimpleDataSource和Spring自带的DriverManagerDataSource:
<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<!--使用PropertyPlaceholderConfigurer类,它告诉Spring从外部属性文件去装载一些配置信息-->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>jdbc.properties</value>
</property>
</bean>

<!--使用org.apache.commons.dbcp.BasicDataSource-->
<!-- bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>${driverClassName}</value>
</property>
<property name="url">
<value>${url}</value>
</property>
<property name="username">
<value>${username}</value>
</property>
<property name="password">
<value>${password}</value>
</property>
</bean-->

<!-- 使用com.ibatis.common.jdbc.SimpleDataSource-->
<!--
<bean id="dataSource" class="com.ibatis.common.jdbc.SimpleDataSource">
<constructor-arg>
<props>
<prop key="JDBC.Driver">${driverClassName}</prop>
<prop key="JDBC.ConnectionURL">${url}</prop>
<prop key="JDBC.Username">${username}</prop>
<prop key="JDBC.Password">${password}</prop>
</props>
</constructor-arg>
</bean>
-->

<!-- 使用org.springframework.jdbc.datasource.DriverManagerDataSource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${driverClassName}</value>
</property>
<property name="url">
<value>${url}</value>
</property>
<property name="username">
<value>${username}</value>
</property>
<property name="password">
<value>${password}</value>
</property>
</bean>

</beans>

再建一个使用此连接池的测试类,这个类的代码几乎可以固定,要用不同的连接池只要改上面的XML文件就行了,运行结果都是一样的:
package cn.it315;

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

import javax.sql.DataSource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DataSourceExample {
public void showAll() {
Connection conn = null;
PreparedStatement pstmt = null;
DataSource ds = null;
ResultSet rs = null;
String sql = "select * from student";
ApplicationContext application = new ClassPathXmlApplicationContext(
"/applicationContext.xml");
try {
ds = (DataSource) application.getBean("dataSource");//从配置文件中读取出一个DataSource
conn = ds.getConnection();
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
System.out.println("ID/tName/tAddress/n");
while (rs.next()) {
System.out.println(rs.getInt(1) + "/t" + rs.getString(2) + "/t"
+ rs.getString(3));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rs != null)
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
if (pstmt != null)
try {
pstmt.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
if (conn != null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) {
DataSourceExample ds = new DataSourceExample();
ds.showAll();
}

}

上面是用XML配置文件获取数据库连接池的方法。下面再介绍两种手工写代码的方法,当然我们还是用读属性文件的方式:
首先得有一个属性文件,这里用的就是上面那个jdbc.properties.
再写一个类来获取数据库连接池并使用它,这里用的是BasicDataSourceFactory:

package cn.itcast;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class DataSourceExample {
public Properties getProps() {
Properties props = new Properties();
InputStream ips = this.getClass()
.getResourceAsStream("jdbc.properties");
try {
props.load(ips);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ips != null)
try {
ips.close();
} catch (IOException e) {
e.printStackTrace();
}
}

return props;

}

public void showAll() {
Properties props = getProps();
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "select * from student";

DataSource dataSource = null;
try {

// 使用BasicDataSourceFactory获取数据源
dataSource = BasicDataSourceFactory.createDataSource(props);
conn = dataSource.getConnection();
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
System.out.println("ID/tName/tAddress/n");
while (rs.next()) {
System.out.println(rs.getInt(1) + "/t" + rs.getString(2) + "/t"
+ rs.getString(3));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rs != null)
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
if (pstmt != null)
try {
pstmt.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
if (conn != null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) {
DataSourceExample ds = new DataSourceExample();
ds.showAll();
}
}

再看一个Hibernate的DriverManagerConnectionProvider获取数据库连接池的方法:
package cn.itcast;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.hibernate.connection.DriverManagerConnectionProvider;

public class DataSourceExample {
public Properties getProps() {
Properties props = new Properties();
InputStream ips = this.getClass().getResourceAsStream(
"hibernate.properties");
try {
props.load(ips);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ips != null)
try {
ips.close();
} catch (IOException e) {
e.printStackTrace();
}
}

return props;

}

public void showAll() {
Properties props = getProps();
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "select * from student";

DriverManagerConnectionProvider dataSource = new DriverManagerConnectionProvider();
try {
dataSource.configure(props);
conn = dataSource.getConnection();
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
System.out.println("ID/tName/tAddress/n");
while (rs.next()) {
System.out.println(rs.getInt(1) + "/t" + rs.getString(2) + "/t"
+ rs.getString(3));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rs != null)
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
if (pstmt != null)
try {
pstmt.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
if (conn != null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) {
DataSourceExample ds = new DataSourceExample();
ds.showAll();
}
}


这几种方法的运行结果都是一样的。


使用CallableStatement调用存储过程,我们也用小例子来学习:
package cn.itcast;


import javax.sql.DataSource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;

public class MainClass {

//获取一个连接。现学现用,我们这里就用上面从数据库连接池获取连接的方法。
//配置文件和属性文件这里就省略不写了,参考上面的例子:
public static Connection getConnection() throws SQLException {
ApplicationContext application = new ClassPathXmlApplicationContext(
"/applicationContext.xml");
DataSource ds = (DataSource) application.getBean("dataSource");//从配置文件中读取出一个DataSource
return ds.getConnection();
}

public static void main(String[] args) {
Connection conn = null;
CallableStatement cstmt = null;
ResultSet rs = null;

try {
conn = getConnection();

// 调用mySql函数,用这种方式:{返回值=call 函数名(参数列表)}
/*
cstmt = conn.prepareCall("{?=call getName(?,?)}");
//注册输出参数,这里是代表第一个问号。这里要注意,第一个参数的索引也是1。
cstmt.registerOutParameter(1,Types.VARCHAR);

cstmt.setInt(1,1);//设置第一个参数的值,注意:是第一个参数,并不是第一个问号,在这里是第二个问号
cstmt.setString(2,"a");//设置第二个参数的值。
cstmt.execute();
System.out.println(cstmt.getString(1));
*/

// 调用mysql存储过程,这种格式:{call 存储过程名(参数列表(包括输出和输入参数))}
cstmt = conn.prepareCall("{call search(?,?,?)}");
cstmt.setInt(3, 4);
//设置第三个问号的值,因为这个参数是输入参数。其他两个是输出参数。
//我们在下面用getXxx()的方法获取出来。
cstmt.execute();//执行
System.out.println("Name = " + cstmt.getString(1));//获取第一个输出参数
System.out.println("Address = " + cstmt.getString(2));//获取第二个输出参数
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rs != null)
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
if (cstmt != null)
try {
cstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
if (conn != null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

}
}

两个小概念:
粗粒度:一个函数干很多事情
细粒度:一个函数干一件事,甚至一件事分为几个函数去干。