javaweb学习(3):JDBC进阶之路

时间:2021-09-25 13:14:39

前言:

JDBC学习起来并不难,但是随着学习的深入,涉及到的知识越来越多,见到的框架也越来越多。

于是乎,很有必要对学过的知识做个总结。以下代码都是在eclipse中完成的。


第一章:单连接操作

起初学习jdbc,一上来我们接触到的jdbc就是jdk的标准“单连接”操作,

首先下载jdbc的java包:点击下载jar包

一个完整的标准操作代码应如下所示:

package com.jimmy.jdbc;

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

public class Demo1 {
public static void main(String[] args) throws Exception {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rts = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/emp", "root", "123456");
ps = conn.prepareStatement("select * from emp");
rts = ps.executeQuery();
while(rts.next()){
System.out.println(rts.getObject(2));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rts != null) {
try {
rts.close();
} catch (Exception e) {
e.printStackTrace();
}
rts = null;
}
if (ps != null) {
try {
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
ps = null;
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
conn = null;
}
}
}
}

这样操作的弊端显而易见,主要有两方面的缺陷:

1,重复的大量代码在执行相同的操作,如:取得连接,关闭资源等。

2,每次只取得一个连接,然后再关闭,效率极其低下。

针对以上方面的缺点,我们来一一攻破。


第二章:自己写一个封装类

自己写一个类,将获得连接和关闭资源的重复代码封装一下:

1,首先我们在src目录下写一个配置文件:mysqlInfo.properties,将一些配置信息写进去:

 driverClass = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/user
username = root
password = 123456


2,MyJDBCUtils.java类用于读取配置文件,并封装代码:

package com.jimmy.MyDBUtils;

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

public class MyJDBCUtils {
//数据库连接用到的字段我们从配置文件中读取
private static String driverClass;
private static String url;
private static String username;
private static String password;
//下面的静态代码块就是读取配置文件中的数据
static{
ResourceBundle rb = ResourceBundle.getBundle("mysqlInfo");
driverClass = rb.getString("driverClass");
url = rb.getString("url");
username = rb.getString("username");
password = rb.getString("password");
}
//取得连接函数
public static Connection getConnection() throws Exception {
Class.forName(driverClass);
Connection conn = DriverManager.getConnection(url,username, password);
return conn;
}
//关闭资源函数
public static void closeRes(ResultSet rst, Statement stmt, Connection conn){
if (rst != null) {
try {
rst.close();
} catch (Exception e) {
e.printStackTrace();
}
rst = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
conn = null;
}
}
}

这样一来,我们再写第一章中的代码时,会是如下这个样子:

package com.jimmy.MyDBUtils;

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

import org.junit.Test;

public class MyJDBCUtilsTest {
@Test
public void testSelect() throws Exception{
Connection conn = null;
PreparedStatement ps = null;

conn = MyJDBCUtils.getConnection(); //获得连接
ps = conn.prepareStatement("select * from emp");
ResultSet rts = ps.executeQuery();
while (rts.next()) {
System.out.println(rts.getString(2));
}
MyJDBCUtils.closeRes(rts, ps, conn); //关闭资源
}
}

这样一来,获得连接和关闭资源的代码被大大的简化了,关键是我们写的封装代码类可以重复使用。

当然,我们只是简单的封装,后面会有第三方jar包完成更简化的封装。


第三章:连接池(也叫数据源)解决单连接问题

我们可以自己写一个类,实现连接池,思路如下:

首先创建10个连接对象conn,然后将这10个conn放到一个List中,每次使用时,从List中取出一个,用完后放回List。较麻烦,就不实现了。

三方库大多都是基于以上思想实现了连接池,那么我们就来介绍一个第三方jar包:C3P0包(点击下载jar包

下载解压后,将lib包里的c3p0-0.9.1.2.jar添加到BuildPath中去。

1,首先在src路径下新建一个配置文件:“c3p0-config.xml”,名称固定。

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/user</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
</default-config>
</c3p0-config>


2,新建一个类:C3P0Utils.java,其封装了C3P0提供的一些方法。

package com.jimmy.C3P0;

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

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;


public class C3P0Utils {
//得到一个数据源(连接池)对象
private static DataSource ds = new ComboPooledDataSource();
//对外提供返回数据源的方法
public static DataSource getDs() { return ds;}

//封装了数据源从池中获取拿到连接的方法
public static Connection getConnection() {
try {
return ds.getConnection();
} catch (SQLException e) {
throw new RuntimeException("服务器异常");
}
}

//关闭资源方法
public static void closeRes(Connection conn, Statement stmt, ResultSet rts) {
if(rts!=null){
try {
rts.close(); //关闭对象
} catch (Exception e) {
e.printStackTrace();
}
rts = null;
}
if(stmt!=null){
try {
stmt.close(); //关闭对象
} catch (Exception e) {
e.printStackTrace();
}
stmt = null;
}
if(conn!=null){
try {
conn.close(); //那么,这里的close()方法不再是关闭连接,而是将conn放回了池中。
} catch (Exception e) {
e.printStackTrace();
}
conn = null;
}
}
}

那么,我们来写一个测试类:

package com.jimmy.C3P0;

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

import org.junit.Test;

public class C3P0test {
@Test
public void test() {
Connection conn = null;
PreparedStatement ps = null;

try {
conn = C3P0Utils.getConnection();
ps = conn.prepareStatement("INSERT INTO account(username,money) VALUES(?,?)");
ps.setString(1, "jimmy");
ps.setDouble(2, 521);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally{
C3P0Utils.closeRes(conn, ps, null);
}
}
}

那么,测试代码看来与第二章中我们自己写的代码很像,不过代码背后真实千差万别。


第四章:DBUtils.jar包

这个包是由apache提供的,是对jdbc的进一步封装(执行sql语句将只有2行代码),并且支持线程池(也叫数据源)。一起来看看吧。

首先下载jar包(点击下载),添加commons-dbutils-1.4.jar到Build Path中。


1,开发中,往往要将数据库中的每条数据封装到一个java对象中去,或者将从网页输入中得到的数据也封装到java对象中去。

所以在使用DBUtils之前,要创建一个实体类,该实体类的成员变量要跟数据库表的列名一样。

比如:DB中有个User表,字段名和类型如下;

id int,
username varchar(20),
password varchar(20),
email varchar(20),
birthday varchar(20)

那么我们就要创建一个java类:

package com.jimmy.DBUtils;

import java.io.Serializable;

public class User implements Serializable{
//成员变量名应该与DB中表的列名一模一样
private int id;
private String username;
private String password;
private String email;
private String birthday;

//自动生成成员变量的set和get方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}

//toString()方法只是为了看效果。
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password="
+ password + ", email=" + email + ", birthday=" + birthday
+ "]";
}
}

2,本案例要用到连接池,我们用上一章中C3P0Utils.java类提供的返回连接池方法:getDs()方法。

然后我们就可以很轻松地取出数据了。

package com.jimmy.DBUtils;

import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;

import com.jimmy.C3P0.C3P0Utils;

public class DBUtilsCRUDDemo1 {


@Test
public void testSelect2() throws Exception{
//1,创建QueryRunner对象,并得到c3p0提供的连接池。
QueryRunner qr = new QueryRunner(C3P0Utils.getDs());
//2,当query方法执行了select语句后,将结果集以参数的形式传递给第2个参数,第2个参数将结果封装到User对象中,最后将各个对象放进List中.
List<User> list = qr.query("select * from user where id=?", new BeanListHandler<User>(User.class),1);

for (User user : list) {
System.out.println(user);
}
}
}
这样就取出了id为1的那行数据。

这样可以看出,操作数据库就只有2行代码了。不仅简化了重复代码,而且使用到了连接池技术。解决了一开始我们提出的2个弊端。


往后还会学习到各种各样的jdbc框架,学到了就再来补充。