- 加载JDBC驱动,利用Java的反射机制
- 通过DriverManager获取数据库连接
- 通过Connection对象创建Statement对象
- 执行SQL语句
- 操作SQL语句返回的结果集
- 存储过程调用
- 关闭数据库资源
- DataSource数据源
JDBC编程的步骤有以下几步:
1、 加载JDBC驱动,利用Java的反射机制
2、 通过DriverManager获取数据库连接,即调用静态工厂方法创建Connection对象
3、 通过Connection对象创建Statement对象
4、 执行SQL语句
5、 操作SQL语句返回的结果集
6、 关闭数据库资源
下面详细说明每一步的具体操作。
加载JDBC驱动,利用Java的反射机制
Class.forName(“数据库厂商提供的JDBC驱动的Driver全限定类名”);
Oracle数据库驱动的Driver全限定类名为“oracle.jdbc.driver.OracleDriver”
MySQL数据库驱动的Driver全限定类名为“com.mysql.jdbc.Driver”
通过DriverManager获取数据库连接
即调用静态工厂方法创建Connection对象
DriverManager类提供了三个重载的静态工厂方法可以创建Connection对象
Connection getConnection(String url)
Connection getConnection(String url, Properties info)
Connection getConnection(String url, String user, String password)
MySQL的url写法如下:
jdbc:mysql://hostname:port/databasename
Oracle的url写法如下:
jdbc:oracle:thin:@hostname:port:databasename
通过Connection对象创建Statement对象
Connection类提供了创建三种类型Statement对象的实例工厂方法,每种类型的方法均有几个参数不同的重载方法。
// 创建Statement 对象,Statement接口提供基本执行SQL语句的能力。
Statement createStatement()
// 创建PreparedStatement对象,PreparedStatement接口继承了Statement接口,提供SQL语句接受输入参数的能力。
PreparedStatement prepareStatement(String sql)
// 创建CallableStatement对象,CallableStatement接口继承了PreparedStatement接口,提供执行存储过程的能力。
CallableStatement prepareCall(String sql)
一般来说,总是选择PreparedStatement而不是Statement执行SQL语句,因为PreparedStatement可以有效的防止SQL注入
执行SQL语句
Statement类提供了执行SQL语句的三种类型的方法。
// 可执行任意SQL语句,并返回是否执行成功,该方法有多个重载方法。
boolean execute(String sql)
// 可执行SQL查询语句,并返回查询结果集。
ResultSet executeQuery(String sql)
// 执行给定 SQL 语句,当该语句为 INSERT、UPDATE 或 DELETE 语句时,返回SQL语句改变行数,当该语句为SQL DDL 语句,返回0。该方法有多个重载方法。
int executeUpdate(String sql)
操作SQL语句返回的结果集
第4步中执行SQL查询语句时获得的ResultSet对象有两种类型的方法。
1) 移动ResultSet记录指针的方法
// 将光标移动到此 ResultSet 对象的给定行编号。
boolean absolute(int row)
// 将光标移动到此 ResultSet 对象的末尾,正好位于最后一行之后。
void afterLast()
// 将光标移动到此 ResultSet 对象的开头,正好位于第一行之前。
void beforeFirst()
// 将光标移动到此 ResultSet 对象的第一行。
boolean first()
// 将光标移动到此 ResultSet 对象的最后一行。
boolean last()
// 将光标从当前位置向前移一行。
boolean next()
// 将光标移动到此 ResultSet 对象的上一行。
boolean previous()
2) 获取当前记录指针指定列的方法
// 根据指定第columnIndex列获取当前记录指针行对应的值
getXXX(int columnIndex)
// 根据指定columnIndex列名获取当前记录指针对应的值
getXXX(String columnName)
存储过程调用
Java调用存储过程时,比如procedure_demo(IN id int(11), OUT student_name varchar(255))这个存储过程
我们首先得到一个Connection对象,一般是从数据库连接池中获取
接着
CallableStatement cstat = conn.prepareCall("{call procedure_demo(?, ?)}");
得到一个CallableStatement对象
这里使用了?作为参数占位符
设置输入参数
cstat.setInt(1, 1);
设置输出参数的类型
cstat.registerOutParameter(2, Types.VARCHAR);
调用存储过程
cstst.execute();
获取存储过程输出参数返回值
cstst.getString(2);
关闭数据库资源
关闭数据库资源分为三步,调用ResultSet、Statement和Connection对应的close()方法
1) 若执行了SQL查询语句,需要关闭ResultSet对象(不是查询语句没有打开ResultSet则没有此步骤)
2) 关闭Statement对象
3) 关闭Connection对象
以上步骤用UML图表示如下(图片来源于网络)
下面按照这些步骤实现JDBC编程,以Oracle为例
安装oracle11gR2参考:
win-server2008-r2安装oracle11gR2
现在假设有一个t_user的表,假设我们的连接数据库的信息如下:
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@192.168.75.130:1521:orcl
user=jison
pass=jison
我们的业务逻辑是要注册一个用户,那么,我们可以这样写这样一个方法
public boolean save(User user) throws SQLException{
String id = UUID.randomUUID().toString().replace("-", "");
user.setId(id);
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@192.168.75.130:1521:orcl";
String user = "jison";
String pass = "jison";
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, pass);
String sql = "insert into t_user(id, name, password) values(?, ?, ?)";
PreparedStatement preStmt = conn.prepareStatement(sql);
preStmt.setString(1, user.getId());
preStmt.setString(2, user.getName());
preStmt.setString(3, user.getPassword());
int rowChange = preStmt.executeUpdate();
preStmt.close();
conn.close();
if(rowChange != 0){
return true;
}
return false;
}
上面用到一个User类
public class User {
private String id;
private String name;
private String password;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
对于上面的配置信息,我们可以简单的封装在一个Java的Properties文件,而连接数据库时因为加载驱动只需要进行一次,那么我们将其移到静态代码块里。修改后的代码如下:
DBConfig.properties代码
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@192.168.75.130:1521:orcl
user=jison
pass=jison
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.UUID;
import org.jisonami.entity.User;
import org.jisonami.sql.DBUtils;
public class UserService {
static Properties properties = new Properties();
static {
try {
properties.load(UserService .class.getClassLoader().getResourceAsStream("DBConfig.properties"));
Class.forName(properties.getProperty("driver"));
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(properties.getProperty("url"),
properties.getProperty("user"),
properties.getProperty("pass"));
}
public boolean save(User user) throws SQLException{
user.setId(generateId());
Connection conn = UserService.getConnection();
String sql = "insert into t_user(id, name, password) values(?, ?, ?)";
PreparedStatement preStmt = conn.prepareStatement(sql);
preStmt.setString(1, user.getId());
preStmt.setString(2, user.getName());
preStmt.setString(3, user.getPassword());
int rowChange = preStmt.executeUpdate();
preStmt.close();
conn.close();
if(rowChange != 0){
return true;
}
return false;
}
private static String generateId(){
return UUID.randomUUID().toString().replace("-", "");
}
}
DataSource数据源
前面的数据库连接都是使用DriverManager.getConnection()的方式获取数据库连接的,这样每次执行sql语句都重新打开一个新的数据库连接,执行完sql语句后有关闭掉比较消耗性能。
于是jdbc就有了DataSource数据连接池的概念,数据源的配置与前面是一样的,必须的配置有四个jdbc驱动类名、数据库url、用户名、密码。
jdbc只提供了DataSource接口,不同的数据源实现中这四个参数的名字不完全一样。
开源的数据连接池池有apache的dpcp数据库连接池,hibernate使用的c3p0数据库连接池。
dpcp有两个版本,分别是commons-dpcp和commons-dpcp2,我们就使用commons-dpcp2好了。 org.apache.commons.dbcp2.BasicDataSource是commons-dpcp2对DataSource接口的实现,直接实例化该对象,并将数据库连接信息set到该对象即可。
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(DBUtils.getDriver());
dataSource.setUrl(DBUtils.getUrl());
dataSource.setUsername(DBUtils.getUserName());
dataSource.setPassword(DBUtils.getPassword());
com.mchange.v2.c3p0.ComboPooledDataSource是c3p0对DataSource接口的实现,直接实例化该对象,并将数据库连接信息set到该对象即可。
ComboPooledDataSource datasource = new ComboPooledDataSource();
datasource.setDriverClass(DBUtils.getDriver());
datasource.setJdbcUrl(DBUtils.getUrl());
datasource.setUser(DBUtils.getUserName());
datasource.setPassword(DBUtils.getPassword());
上面的DBUtils工具类用于提取DBConfig.properties的配置信息,代码如下
public class DBUtils {
static Properties properties = new Properties();
static {
try {
properties.load(DBUtils.class.getClassLoader().getResourceAsStream("DBConfig.properties"));
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getDriver(){
return properties.getProperty("driver");
}
public static String getUrl(){
return properties.getProperty("url");
}
public static String getUserName(){
return properties.getProperty("user");
}
public static String getPassword(){
return properties.getProperty("pass");
}
}
如果是使用了Spring这类依赖注入的框架的话,就可以直接将数据源的实例化配置在配置文件里面了,就不需要直接new对象了。
得到DataSource对象之后,就可以用DataSource获取Connection了
datasource.getConnection();
DataSource得到的Connection做完相应的数据库操作之后,直接像正常的调用close()方法即可,这个操作不会关闭数据库连接,而是将该连接重新初始化后等待新的获取连接请求。