詳細說明關于Java的數(shù)據(jù)庫連接(JDBC)
Java的數(shù)據(jù)庫連接(JDBC)
1、什么是JDBC
JDBC(JavaDataBase Connectivity) 就是Java數(shù)據(jù)庫連接,說白了就是用Java語言來操作數(shù)據(jù)庫。原來我們操作數(shù)據(jù)庫是在控制臺使用SQL語句來操作數(shù)據(jù)庫,JDBC是用Java語言向數(shù)據(jù)庫發(fā)送SQL語句。
2、JDBC的原理
早期SUN公司的天才們想編寫一套可以連接天下所有數(shù)據(jù)庫的API,但是當他們剛剛開始時就發(fā)現(xiàn)這是不可完成的任務,因為各個廠商的數(shù)據(jù)庫服務器差異太大了。后來SUN開始與數(shù)據(jù)庫廠商們討論,最終得出的結論是,由SUN提供一套訪問數(shù)據(jù)庫的規(guī)范(就是一組接口),并提供連接數(shù)據(jù)庫的協(xié)議標準,然后各個數(shù)據(jù)庫廠商會遵循SUN的規(guī)范提供一套訪問自己公司的數(shù)據(jù)庫服務器的API出現(xiàn)。SUN提供的規(guī)范命名為JDBC,而各個廠商提供的,遵循了JDBC規(guī)范的,可以訪問自己數(shù)據(jù)庫的API被稱之為驅動!JDBC是接口,而JDBC驅動才是接口的實現(xiàn),沒有驅動無法完成數(shù)據(jù)庫連接!每個數(shù)據(jù)庫廠商都有自己的驅動,用來連接自己公司的數(shù)據(jù)庫。

3、演示JDBC的使用
通過下載MySQL的驅動jar文件,將其添加到項目中間,在注冊驅動時要指定為已經(jīng)下載好的驅動。
package jdbc;
import com.mysql.jdbc.Driver; //這是我們驅動的路徑
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class Jdbc01 {
public static void main(String[] args) throws SQLException {
//1.注冊驅動
Driver driver = new Driver();
//2.得到連接
//jdbc:mysql:// 規(guī)定好的協(xié)議 localhost 連接的地址 3306 監(jiān)聽的端口 test_table 連接的數(shù)據(jù)庫
String url = "jdbc:mysql://localhost:3306/test_table";
Properties properties = new Properties();
//user和password 規(guī)定好的不能隨意改
properties.setProperty("user", "root");//
properties.setProperty("password", "161142");
Connection connect = driver.connect(url, properties); //相當于網(wǎng)絡連接
//3.執(zhí)行sql語句
//String sql = "insert into actor values(null,'syj','女','2000-05-26','110')";
String sql = "update actor set name = 'xhj' where id = 2";
Statement statement = connect.createStatement();
int rows = statement.executeUpdate(sql); //返回影響的行數(shù)
if (rows > 0) System.out.println("添加成功");
else System.out.println("添加失敗");
//4.關閉連接資源
statement.close();
connect.close();
}
}
4、數(shù)據(jù)庫連接方式
public class JdbcConn {
@Test /* 第一種 */
public void testConn01() throws SQLException {
//獲取Driver實現(xiàn)類對象
Driver driver = new Driver();
String url = "jdbc:mysql://localhost:3306/test_table";
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "161142");
Connection connect = driver.connect(url, properties);
System.out.println(connect);
}
@Test /* 第二種 */
public void testConn02() throws Exception{
//使用反射加載Driver類,動態(tài)加載,可以通過配置文件靈活使用各種數(shù)據(jù)庫
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) aClass.getDeclaredConstructor().newInstance();
String url = "jdbc:mysql://localhost:3306/test_table";
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "161142");
Connection connect = driver.connect(url, properties);
System.out.println(connect);
}
@Test /* 第三種 */
//DriverManager統(tǒng)一來管理Driver
public void testConn03() throws Exception{
//使用反射加載Driver類
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) aClass.getDeclaredConstructor().newInstance();
//創(chuàng)建url和user和password
String url = "jdbc:mysql://localhost:3306/test_table";
String user = "root";
String password = "161142";
DriverManager.registerDriver(driver); //注冊Driver驅動
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
@Test /* 第四種 */
public void testConn04() throws Exception{
//使用反射加載Driver類
Class.forName("com.mysql.jdbc.Driver");
/* Class.forName("com.mysql.jdbc.Driver")在底層加載Driver時自動完成注冊驅動,簡化代碼
//在底層加載Driver時會自動加載靜態(tài)代碼塊
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
*/
//Class.forName("com.mysql.jdbc.Driver");
/* Class.forName("com.mysql.jdbc.Driver");這句話也可以去掉
mysql驅動5.1.6可以無需CLass.forName(“com.mysql.jdbc.Driver");
從jdk1.5以后使用了jdbc4,不再需要顯示調(diào)用class.forName()注冊驅動而是自動調(diào)用驅動
jar包下META-INF\services\java.sqI.Driver文本中的類名稱去注冊
建議還是寫上,更加明確
*/
//創(chuàng)建url和user和password
String url = "jdbc:mysql://localhost:3306/test_table";
String user = "root";
String password = "161142";
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
@Test /* 第五種(推薦使用) */
public void testConn05() throws Exception{
//在方式4的情況下,將信息放到配置文件里,利于后續(xù)可持續(xù)操作
//獲取配置文件信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//獲取相關信息
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class.forName(driver); //加載Driver類,建議加上
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
}
5、JDBC的查詢
使用ResultSet 記錄查詢結果
ResultSet: 底層使用ArrayList 存放每一行數(shù)據(jù)(二維字節(jié)數(shù)組,每一維表示一行中的一個數(shù)據(jù))
Resultment: 用于執(zhí)行靜態(tài)SQL語句并返回其生成的結果的對象,是一個接口,需要各個數(shù)據(jù)庫廠家來實現(xiàn)。(實際中我們一般不用這個)
public class jdbc03 {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select id,`name`,sex,borndate from actor;");
while (resultSet.next()){ //resultSet.previous();//向上移動一行
int id = resultSet.getInt(1);
//int id = resultSet.getInt("id"); //也可以按照列明來獲取
String name = resultSet.getString(2);
String sex = resultSet.getString(3);
Date date = resultSet.getDate(4);
//Object object = resultSet.getObject(索引|列明); //對象形式操作(分情況考慮)
System.out.println(id + "\t" + name + "\t" + sex + "\t" + date);
}
statement.close();
connection.close();
}
}
6、SQL注入
SQL注入: 是利用某些系統(tǒng)沒有對用戶輸入的數(shù)據(jù)進行充分的檢查,而在用戶輸入數(shù)據(jù)中注入非法的SQL語句段或命令,惡意攻擊數(shù)據(jù)庫。
例如下列代碼實現(xiàn)了注入問題(而Statement就存在這個問題,所以實際開發(fā)過程中不用它)
create table admit(name varchar(32),password varchar(32));
insert into admit values('tom','1234');
select * from admit where name = 'tom' and password = '1234'; # 輸出 tom 1234
# 如果有人輸入 name 為 1' or password 為 or '1' = '1
# 那么select 就變成
select * from admit where name = '1' or ' and password = ' or '1' = '1'; # 其中'1' = '1'永遠成立
7、預處理查詢
使用PreparedStatement代替Statement就避免了注入問題,通過傳入**?** 代替拼接 (PreparedStatement接口繼承了Statement接口)
PreparedStatement的好處
- 不再使用+拼接sql語句,減少語法錯誤
- 有效的解決了sql注入問題!
- 大大減少了編譯次數(shù),效率較高
7.1 查詢 已解決注入問題
public class jdbc04 {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.print("請輸入用戶名:");
String name = scanner.nextLine();
System.out.print("請輸入密碼:");
String pwd = scanner.nextLine();
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
//Statement statement = connection.createStatement();
//preparedStatement是PreparedStatement實現(xiàn)類的對象
PreparedStatement preparedStatement = connection.prepareStatement("select `name` ,`password` " +
"from admit where name = ? and password = ?");
preparedStatement.setString(1,name); //?號下標從1開始
preparedStatement.setString(2,pwd);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) System.out.println("登錄成功");
else System.out.println("登陸失敗");
preparedStatement.close();
connection.close();
}
}
7.2 插入,更新,刪除
public class jdbc05 {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.print("請輸入用戶名:");
String name = scanner.nextLine();
System.out.print("請輸入密碼:");
String pwd = scanner.nextLine();
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
//添加
String sql1 = "insert into admit values(?,?)";
//修改
String sql2 = "update admit set name = ? where name = ? and password = ?";
//刪除
String sql3 = "delete from admit where name = ? and password = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql3);
//preparedStatement.setString(1,name+"plas"); //?號下標從1開始
//preparedStatement.setString(2,name);
//preparedStatement.setString(3,pwd);
preparedStatement.setString(1,name);
preparedStatement.setString(2,pwd);
int rows = preparedStatement.executeUpdate();
if (rows > 0) System.out.println("操作成功");
else System.out.println("操作失敗");
preparedStatement.close();
connection.close();
}
}
8、工具類開發(fā)
由于在進行數(shù)據(jù)庫操作時,有些步驟是重復的,如連接,關閉資源等操作。
工具類
package utils;
import java.sql.*;
import java.io.FileInputStream;
import java.util.Properties;
public class JDBCUtils {
private static String user; //用戶名
private static String password; //密碼
private static String url; //連接數(shù)據(jù)庫的url
private static String driver; //驅動
//靜態(tài)代碼塊進行行初始化
static {
try {
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
user = properties.getProperty("user");
password = properties.getProperty("password");
url = properties.getProperty("url");
driver = properties.getProperty("driver");
} catch (Exception e) {
//實際開發(fā)過程中(將編譯異常,改成運行異常,用戶可以捕獲異常,也可以默認處理該異常)
throw new RuntimeException(e);
}
}
//連接
public static Connection getConnection(){
try {
return DriverManager.getConnection(url,user,password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
//關閉資源
public static void close(ResultSet set, Statement statement,Connection connection){
try {
if (set != null) set.close();
if (statement != null)statement.close();
if (connection != null)connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
應用:
public class JdbcUtilsTest {
@Test //測試select操作
public void testSelect() {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//得到連接
connection = JDBCUtils.getConnection();
//設置sql
String sql = "select * from actor where id = ?";
//創(chuàng)建PreparedStatement
preparedStatement = connection.prepareStatement(sql);
//占位賦值
preparedStatement.setInt(1,2);
//執(zhí)行
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
/* 也可以這樣寫
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String sex = resultSet.getString("sex");
Date date = resultSet.getDate("borndate");
String phone = resultSet.getString("phone");
*/
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
String sex = resultSet.getString(3);
Date date = resultSet.getDate(4);
String phone = resultSet.getString(5);
System.out.println(id + "\t" + name + "\t" + sex + "\t" + date + "\t" + phone);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(resultSet, preparedStatement, connection);
}
}
@Test //測試DML操作
public void testDML() {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
//得到連接
connection = JDBCUtils.getConnection();
//設置sql
String sql = "update actor set name = ?,sex = ? where id = ?";
//創(chuàng)建PreparedStatement
preparedStatement = connection.prepareStatement(sql);
//占位符賦值
preparedStatement.setString(1, "sxy");
preparedStatement.setString(2, "男");
preparedStatement.setInt(3, 2);
//執(zhí)行
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(null, preparedStatement, connection);
}
}
}
9、JDBC事務
public class Jdbc06 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
connection.setAutoCommit(false);//關閉自動提交(開啟事務)
//第一個動作
String sql = "update actor set phone = phone - 10 where id = 2";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
//int i = 1/0; 異常
//第二個動作
sql = "update actor set phone = phone + 10 where id = 1";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
//提交事務
connection.commit();
} catch (Exception e) {
System.out.println("有異常存在,撤銷sql服務");
try {
connection.rollback(); //回滾到事務開始的地方
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
} finally {
JDBCUtils.close(null, preparedStatement, connection);
}
}
}
10、批處理
public class Jdbc07 {
@Test //普通處理5000條插入數(shù)據(jù) 執(zhí)行時間169839
public void test01() {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
String sql = "insert into actor(id,`name`,sex) values (?,?,'男')";
preparedStatement = connection.prepareStatement(sql);
long begin = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
preparedStatement.setString(1, 3 + i + "");
preparedStatement.setString(2, "sxy" + (i + 1));
preparedStatement.executeUpdate();
}
long end = System.currentTimeMillis();
System.out.println(end - begin);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.close(null, preparedStatement, connection);
}
}
@Test //批處理 執(zhí)行時間429
public void test02() {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
String sql = "insert into actor(id,`name`,sex) values (?,?,'男')";
preparedStatement = connection.prepareStatement(sql);
long begin = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
preparedStatement.setString(1, 3 + i + "");
preparedStatement.setString(2, "sxy" + (i + 1));
//將sql語句加入批處理包中
preparedStatement.addBatch();
/*
preparedStatement.addBatch()在底層把每一條數(shù)據(jù)加入到ArrayList
執(zhí)行過程:檢查本條sql中的語法問題 -> 把本條sql語句加入到ArrayList -> 每1000條執(zhí)行一次
批處理優(yōu)點:減少了編譯次數(shù),又減少了運行次數(shù),效率大大提高
還需要在properties配置文件中將url加上?rewriteBatchedStatements=true
url=jdbc:mysql://localhost:3306/test_table?rewriteBatchedStatements=true
*/
//當有1000條時,在進行處理
if ((i + 1) % 1000 == 0) {
preparedStatement.executeBatch();
//清空批處理包
preparedStatement.clearBatch();
}
}
long end = System.currentTimeMillis();
System.out.println(end - begin);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.close(null, preparedStatement, connection);
}
}
}
11、數(shù)據(jù)庫連接池
由于有很多用戶連接數(shù)據(jù)庫,而數(shù)據(jù)庫連接數(shù)量又是有限制的,而且就算連接并且關閉也是很耗時,所以就有了引入了數(shù)據(jù)庫連接池可以很好的來解決這個問題。下面是普通連接數(shù)據(jù)庫連接并且關閉5000次所耗時間6249毫秒,可以發(fā)下時間相對很長。
public class ConQuestion {
public static void main(String[] args) {
//看看連接-關閉 connection 會耗用多久
long start = System.currentTimeMillis();
System.out.println("開始連接.....");
for (int i = 0; i < 5000; i++) {
//使用傳統(tǒng)的jdbc方式,得到連接
Connection connection = JDBCUtils.getConnection();
//做一些工作,比如得到PreparedStatement ,發(fā)送sql
//..........
//關閉
JDBCUtils.close(null, null, connection);
}
long end = System.currentTimeMillis();
System.out.println("傳統(tǒng)方式5000次 耗時=" + (end - start));//傳統(tǒng)方式5000次 耗時=6249
}
}
11.1 數(shù)據(jù)庫連接池基本介紹
- 預先在緩沖池中放入一定數(shù)量的連接,當需要建立數(shù)據(jù)庫連接時,只需從“緩沖池”中取出一個,使用完畢之后再放回去。
- 數(shù)據(jù)庫連接池負責分配,管理和釋放數(shù)據(jù)庫連接,它允許應用程序重復使用一個現(xiàn)有的數(shù)據(jù)庫連接,而不是重新建立一個。
- 當應用程序向連接池請求的連接數(shù)超過最大連接數(shù)量時,這些請求將被加入到等待隊列中。
11.2 JDBC的數(shù)據(jù)庫連接池使用
JDBC的數(shù)據(jù)庫連接池使用javax.sql.DataSource來表示,DataSource只是一個接口,該接口通常由第三方提供實現(xiàn)。
11.3 數(shù)據(jù)庫連接池的種類
- C3P0 數(shù)據(jù)庫連接池,速度相對較慢,穩(wěn)定性不錯(hibernate,spring)。(用的較多)
- DBCP數(shù)據(jù)庫連接池,速度相對c3p0較快,但不穩(wěn)定。
- Proxool數(shù)據(jù)庫連接池,有監(jiān)控連接池狀態(tài)的功能,穩(wěn)定性較c3p0差一點。
- BoneCP 數(shù)據(jù)庫連接池,速度快。
- Druid (德魯伊)是阿里提供的數(shù)據(jù)庫連接池,集DBCP,C3P0,Proxool優(yōu)點于身的數(shù)據(jù)庫連接池。(應用最廣)
11.4 C3P0連接池
利用C3P0連接池再次嘗試連接5000次數(shù)據(jù)庫 可以發(fā)現(xiàn)耗時方式一僅僅花了456毫秒,第二種通過配置文件操作也是花了419毫秒差不多的時間,值得說的是這個連接池連接配置文件不能是我們自己寫,官方有給定的模板(c3p0.config.xml)。
public class C3P0_ {
@Test //方式一: 相關參數(shù),在程序中指定user,url,password等
public void testC3P0_1() throws Exception {
//創(chuàng)建一個數(shù)據(jù)源對象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//通過配合文件獲取相關連接信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//給數(shù)據(jù)源(comboPooledDataSource)設置相關參數(shù)
//連接管理是由comboPooledDataSource(連接池)來管理的
comboPooledDataSource.setDriverClass(driver); //設置驅動
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
//初始化數(shù)據(jù)源的連接數(shù)
comboPooledDataSource.setInitialPoolSize(10);
//數(shù)據(jù)庫連接池最大容量,如果還有連接請求,那么就會將該請求放入等待隊列中
comboPooledDataSource.setMaxPoolSize(50);
//測試連接池的效率, 測試對mysql 5000次操作
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
//getConnection()這個方法就是重寫了DataSource接口的方法
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
//c3p0 5000連接mysql 耗時=456
System.out.println("c3p0 5000連接mysql 耗時=" + (end - start));
comboPooledDataSource.close();
}
//第二種方式 使用配置文件模板來完成
//將C3P0 提供的 c3p0.config.xml 拷貝到 src目錄下
//該文件指定了連接數(shù)據(jù)庫和連接池的相關參數(shù)
@Test
public void testC3P0_02() throws SQLException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("sxy");
//測試5000次連接mysql
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
//c3p0的第二種方式(5000) 耗時=419
System.out.println("c3p0的第二種方式(5000) 耗時=" + (end - start));
}
}
11.5 Druid連接池
在使用Druid連接池連接數(shù)據(jù)庫500000次耗時643毫秒,而C3P0500000次連接耗時2373毫秒,很顯然Druid連接速度更快。
public class Druid_ {
@Test
public void testDruid() throws Exception {
//1.加入Druid jar包
//2.加入 配置文件 druid.properties 放到src目錄下
//3.創(chuàng)建Properties對象
Properties properties = new Properties();
properties.load(new FileInputStream("src\\druid.properties"));
//4.創(chuàng)建一個指定參數(shù)的數(shù)據(jù)庫連接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
long start = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
Connection connection = dataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
//Druid的500000次創(chuàng)建 耗時=643
System.out.println("Druid的500000次創(chuàng)建 耗時=" + (end - start));
}
}
對應的工具類
public class JDBCUtilsByDruid {
private static DataSource ds;
//在靜態(tài)代碼塊完成 ds初始化
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\\druid.properties"));
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//編寫getConnection方法
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//關閉連接:在數(shù)據(jù)庫連接池技術中,close不是真的斷掉連接,而是把使用的Connection對象放回連接池
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
try {
if (resultSet != null) resultSet.close();
if (statement != null) statement.close();
if (connection != null) connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
使用工具類:
public class TestUtilsByDruid {
@Test
public void testSelect() {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//得到連接
connection = JDBCUtilsByDruid.getConnection();
System.out.println(connection.getClass());
//connection 的運行類型 class com.alibaba.druid.pool.DruidPooledConnection
//設置sql
String sql = "select * from actor where id = ?";
//創(chuàng)建PreparedStatement
preparedStatement = connection.prepareStatement(sql);
//占位賦值
preparedStatement.setInt(1, 2);
//執(zhí)行
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
String sex = resultSet.getString(3);
Date date = resultSet.getDate(4);
String phone = resultSet.getString(5);
System.out.println(id + "\t" + name + "\t" + sex + "\t" + date + "\t" + phone);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtilsByDruid.close(resultSet, preparedStatement, connection);
}
}
}
12、Apache-DBUtils
由于resultSet存放數(shù)據(jù)集合,在connection關閉時,resultSet結果集無法使用。所以為了使用這些數(shù)據(jù),也有JDBC官方提供的文件Apache-DBUtils來存放數(shù)據(jù)。
12.1 ArrayList模擬
ArrayList模擬Apache-DBUtils
Actor類 用來保存Actor表中的數(shù)據(jù)用的。
public class Actor { //Javabean, POJO, Domain對象
private Integer id;
private String name;
private String sex;
private Date borndate;
private String phone;
public Actor() { //一定要給一個無參構造器[反射需要]
}
public Actor(Integer id, String name, String sex, Date borndate, String phone) {
this.id = id;
this.name = name;
this.sex = sex;
this.borndate = borndate;
this.phone = phone;
}
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; }
public Date getBorndate() { return borndate; }
public void setBorndate(Date borndate) { this.borndate = borndate; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
@Override
public String toString() {
return "\nActor{" + "id=" + id + ", name='" + name + '\'' +
", sex='" + sex + '\'' + ", borndate=" + borndate +
", phone='" + phone + '\'' + '}';
}
}
用ArrayList來存放數(shù)據(jù)
public class LikeApDB {
@Test
public /*也可以返回ArrayList<Actor>*/void testSelectToArrayList() {
Connection connection = null;
String sql = "select * from actor where id >= ?";
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
ArrayList<Actor> list = new ArrayList<>();
try {
connection = JDBCUtilsByDruid.getConnection();
System.out.println(connection.getClass());
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 1);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");//getName()
String sex = resultSet.getString("sex");//getSex()
Date borndate = resultSet.getDate("borndate");
String phone = resultSet.getString("phone");
//把得到的 resultSet 的記錄,封裝到 Actor對象,放入到list集合
list.add(new Actor(id, name, sex, borndate, phone));
}
System.out.println("list集合數(shù)據(jù)=" + list);
for(Actor actor : list) {
System.out.println("id=" + actor.getId() + "\t" + actor.getName());
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//關閉資源
JDBCUtilsByDruid.close(resultSet, preparedStatement, connection);
}
//因為ArrayList 和 connection 沒有任何關聯(lián),所以該集合可以復用.
//return list;
}
}
12.2 Apache-DBUtils
基本介紹
commons-dbutils是 Apache組織提供的一個開源JDBC工具類庫,它是對JDBC的封裝,使用dbutils能極大簡化jdbc編碼的工作量。
DbUtils類
- QueryRunner類:該類封裝了SQL的執(zhí)行,是線程安全的??梢詫崿F(xiàn)增,刪,改,查,批處理
- 使用QueryRunner類實現(xiàn)查詢。
- ResultSetHandler接口:該接口用于處理 java.sql.ResultSet,將數(shù)據(jù)按要求轉換為另一種形式
應用實例
使用Apache-DBUtils工具+數(shù)據(jù)庫連接池(Druid)方式,完成對一個表的增刪改查。
package datasourse;
import ApDB.Actor;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public class DBUtils_Use {
@Test //查詢多條數(shù)據(jù)
public void testQueryMany() throws Exception {
//1.得到連接(Druid)
Connection connection = JDBCUtilsByDruid.getConnection();
//2.使用 DBUtils 類和接口,先引入 DBUtils jar文件 ,放到src目錄下
//3.創(chuàng)建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4.執(zhí)行相應的方法,返回ArrayList結果集
String sql = "select * from actor where id >= ?";
//String sql = "select id,`name` from actor where id >= ?";
/*
(1) query 方法就是執(zhí)行sql 語句,得到resultSet ---封裝到 --> ArrayList 集合中
(2) 返回集合
(3) connection: 連接
(4) sql : 執(zhí)行的sql語句
(5) new BeanListHandler<>(Actor.class): 在將resultSet -> Actor 對象 -> 封裝到 ArrayList
底層使用反射機制 去獲取Actor 類的屬性,然后進行封裝
(6) 1 就是給 sql 語句中的? 賦值,可以有多個值,因為是可變參數(shù)Object... params
(7) 底層得到的resultSet ,會在query 關閉, 關閉PreparedStatement
*/
List<Actor> query =
queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
/**
* 分析 queryRunner.query方法源碼分析
* public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
* PreparedStatement stmt = null;//定義PreparedStatement
* ResultSet rs = null;//接收返回的 ResultSet
* Object result = null;//返回ArrayList
*
* try {
* stmt = this.prepareStatement(conn, sql);//創(chuàng)建PreparedStatement
* this.fillStatement(stmt, params);//對sql 進行 ? 賦值
* rs = this.wrap(stmt.executeQuery());//執(zhí)行sql,返回resultset
* result = rsh.handle(rs);//返回的resultset --> arrayList[result] [使用到反射,對傳入class對象處理]
* } catch (SQLException var33) {
* this.rethrow(var33, sql, params);
* } finally {
* try {
* this.close(rs);//關閉resultset
* } finally {
* this.close((Statement)stmt);//關閉preparedstatement對象
* }
* }
*
* return result;
* }
*/
for (Actor actor : query) {
System.out.print(actor);
}
JDBCUtilsByDruid.close(null,null,connection);
}
@Test //查詢單條記錄
public void testQuerySingle() throws SQLException {
Connection connection = JDBCUtilsByDruid.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql = "select * from actor where id = ?";
//已知查詢的是單行,所以就用BeanHandler,返回一個對應的對象
Actor query = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 2);
System.out.print(query);
JDBCUtilsByDruid.close(null,null,connection);
}
@Test //查詢單行單列(某個信息) 返回一個Object對象
public void testQuerySingleObject() throws SQLException {
Connection connection = JDBCUtilsByDruid.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql = "select `name` from actor where id = ?";
//已知查詢的是單行單列,所以就用BeanHandler,返回一個Object
Object query = queryRunner.query(connection, sql, new ScalarHandler(), 1);
System.out.println(query);
JDBCUtilsByDruid.close(null,null,connection);
}
@Test //演示DML操作(insert,update,delete)
public void testDML() throws SQLException {
Connection connection = JDBCUtilsByDruid.getConnection();
QueryRunner queryRunner = new QueryRunner();
//String sql = "update actor set phone = ? where id = ?";
//int affectedRow = queryRunner.update(connection, sql, "110", 2);
String sql = "insert into actor values(?,?,?,?,?)";
int affectedRow = queryRunner.update(connection, sql, 3, "xhj", "女", "2000-05-26", "110");
//String sql = "delete from actor where id = ?";
//int affectedRow = queryRunner.update(connection, sql, 5004);
System.out.println(affectedRow > 0 ? "OK" : "NO");
JDBCUtilsByDruid.close(null,null,connection);
}
}
13、BasicDao
引入問題
- SQL語句是固定,不能通過參數(shù)傳入,通用性不好,需要進行改進,更方便執(zhí)行增刪改查
- 對于select 操作,如果有返回值,返回類型不能固定,需要使用泛型
- 將來的表很多,業(yè)務需求復雜,不可能只靠一個JAVA類完成。
所以在實際開發(fā)中,也有解決辦法 —BasicDao
13.1 BasicDAO類
public class BasicDAO<T> { //泛型指定具體的類型
private QueryRunner queryRunner = new QueryRunner();
//開發(fā)通用的DML,針對任意表
public int update(String sql,Object... parameter){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return queryRunner.update(connection, sql, parameter);
} catch (SQLException e) {
throw new RuntimeException(e);//將編譯異常轉化成運行異常,可以被捕獲,也可以被拋出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
/** 返回多個對象(多行結果)
*
* @param sql sql語句,可以存在?
* @param clazz 傳入一個類的class對象 例如Actor.class
* @param parameter 傳入?號具體的值,可以有多個
* @return 根據(jù)類似Actor.class類型,返回對應的ArrayList集合
*/
public List<T> QueryMultiply(String sql,Class<T> clazz, Object... parameter){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return queryRunner.query(connection,sql,new BeanListHandler<T>(clazz),parameter);
} catch (SQLException e) {
throw new RuntimeException(e);//將編譯異常轉化成運行異常,可以被捕獲,也可以被拋出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//返回單個對象(單行數(shù)據(jù))
public T querySingle(String sql,Class<T> clazz,Object... parameter){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return queryRunner.query(connection,sql,new BeanHandler<T>(clazz),parameter);
} catch (SQLException e) {
throw new RuntimeException(e);//將編譯異常轉化成運行異常,可以被捕獲,也可以被拋出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//返回單個對象的單個屬性(單行中的單列)
public Object queryScalar(String sql,Object... parameter){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return queryRunner.query(connection,sql,new ScalarHandler(),parameter);
} catch (SQLException e) {
throw new RuntimeException(e);//將編譯異常轉化成運行異常,可以被捕獲,也可以被拋出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
}
13.2 domain中的類
public class Actor { //Javabean, POJO, Domain對象
private Integer id;
private String name;
private String sex;
private Date borndate;
private String phone;
public Actor() { //一定要給一個無參構造器[反射需要]
}
public Actor(Integer id, String name, String sex, Date borndate, String phone) {
this.id = id;
this.name = name;
this.sex = sex;
this.borndate = borndate;
this.phone = phone;
}
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; }
public Date getBorndate() { return borndate; }
public void setBorndate(Date borndate) { this.borndate = borndate; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
@Override
public String toString() {
return "\nActor{" + "id=" + id + ", name='" + name + '\'' +
", sex='" + sex + '\'' + ", borndate=" + borndate +
", phone='" + phone + '\'' + '}';
}
}
ActorDAO類繼承BasicDAO類,這樣的類可以有很多。
public class ActorDAO extends BasicDAO<Actor> {
}
13.3 測試類
public class TestDAO {
@Test//測試ActorDAO對actor表的操作
public void testActorDAO() {
ActorDAO actorDAO = new ActorDAO();
//1.查詢多行
List<Actor> actors = actorDAO.QueryMultiply("select * from actor where id >= ?", Actor.class, 1);
System.out.println(actors);
//2.查詢單行
Actor actor = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 1);
System.out.println(actor);
//3.查詢單行單個數(shù)據(jù)
Object o = actorDAO.queryScalar("select name from actor where id = ?", 1);
System.out.println(o);
//4.DML操作 當前演示update
int affectedRow = actorDAO.update("update actor set phone = ? where id = ?", "120", 3);
System.out.println(affectedRow > 0 ? "OK" : "NO");
}
}
到此這篇關于詳細說明關于Java的數(shù)據(jù)庫連接(JDBC)的文章就介紹到這了,更多相關Java的數(shù)據(jù)庫連接(JDBC)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Java連接MySQL數(shù)據(jù)庫實例
- Java使用JDBC連接數(shù)據(jù)庫的詳細步驟
- 詳解Java使用JDBC連接MySQL數(shù)據(jù)庫
- Java使用JDBC連接數(shù)據(jù)庫
- Java使用JNDI連接數(shù)據(jù)庫的實現(xiàn)方法
- Java連接MySQL數(shù)據(jù)庫命令行程序過程
- 一篇文章帶你了解java數(shù)據(jù)庫連接
- 詳解Java數(shù)據(jù)庫連接池
- Java基礎之JDBC的數(shù)據(jù)庫連接與基本操作
- Java 數(shù)據(jù)庫連接(JDBC)的相關總結
- Java 如何使用JDBC連接數(shù)據(jù)庫
- Java連接數(shù)據(jù)庫的步驟介紹
相關文章
Java中JUC包(java.util.concurrent)下的常用子類
相信大家已經(jīng)對并發(fā)機制中出現(xiàn)的很多的常見知識點進行了總結,下面這篇文章主要給大家介紹了關于Java中JUC包(java.util.concurrent)下的常用子類的相關資料,文中通過圖文以及示例代碼介紹的非常詳細,需要的朋友可以參考下2022-12-12
Java初級必看的數(shù)據(jù)類型與常量變量知識點
這篇文章主要給大家介紹了關于Java初級必看的數(shù)據(jù)類型與常量變量知識點的相關資料,需要的朋友可以參考下2023-11-11
SpringBoot實現(xiàn)列表數(shù)據(jù)導出為Excel文件
這篇文章主要為大家詳細介紹了在Spring?Boot框架中如何將列表數(shù)據(jù)導出為Excel文件,文中的示例代碼講解詳細,感興趣的小伙伴可以了解下2024-02-02
SpringBoot創(chuàng)建JSP登錄頁面功能實例代碼
這篇文章主要介紹了SpringBoot創(chuàng)建JSP登錄頁面功能實例代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-04-04
Java使用synchronized修飾方法來同步線程的實例演示
synchronized下的方法控制多線程程序中的線程同步非常方便,這里就來看一下Java使用synchronized修飾方法來同步線程的實例演示,需要的朋友可以參考下2016-06-06

