詳細說明關於Java的數據庫連接(JDBC)
Java的數據庫連接(JDBC)
1、什麼是JDBC
JDBC(JavaDataBase Connectivity) 就是Java數據庫連接,說白瞭就是用Java語言來操作數據庫。原來我們操作數據庫是在控制臺使用SQL語句來操作數據庫,JDBC是用Java語言向數據庫發送SQL語句。
2、JDBC的原理
早期SUN公司的天才們想編寫一套可以連接天下所有數據庫的API,但是當他們剛剛開始時就發現這是不可完成的任務,因為各個廠商的數據庫服務器差異太大瞭。後來SUN開始與數據庫廠商們討論,最終得出的結論是,由SUN提供一套訪問數據庫的規范(就是一組接口),並提供連接數據庫的協議標準,然後各個數據庫廠商會遵循SUN的規范提供一套訪問自己公司的數據庫服務器的API出現。SUN提供的規范命名為JDBC,而各個廠商提供的,遵循瞭JDBC規范的,可以訪問自己數據庫的API被稱之為驅動!JDBC是接口,而JDBC驅動才是接口的實現,沒有驅動無法完成數據庫連接!每個數據庫廠商都有自己的驅動,用來連接自己公司的數據庫。
3、演示JDBC的使用
通過下載MySQL的驅動jar文件,將其添加到項目中間,在註冊驅動時要指定為已經下載好的驅動。
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:// 規定好的協議 localhost 連接的地址 3306 監聽的端口 test_table 連接的數據庫 String url = "jdbc:mysql://localhost:3306/test_table"; Properties properties = new Properties(); //user和password 規定好的不能隨意改 properties.setProperty("user", "root");// properties.setProperty("password", "161142"); Connection connect = driver.connect(url, properties); //相當於網絡連接 //3.執行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); //返回影響的行數 if (rows > 0) System.out.println("添加成功"); else System.out.println("添加失敗"); //4.關閉連接資源 statement.close(); connect.close(); } }
4、數據庫連接方式
public class JdbcConn { @Test /* 第一種 */ public void testConn01() throws SQLException { //獲取Driver實現類對象 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類,動態加載,可以通過配置文件靈活使用各種數據庫 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統一來管理Driver public void testConn03() throws Exception{ //使用反射加載Driver類 Class<?> aClass = Class.forName("com.mysql.jdbc.Driver"); Driver driver = (Driver) aClass.getDeclaredConstructor().newInstance(); //創建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時會自動加載靜態代碼塊 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,不再需要顯示調用class.forName()註冊驅動而是自動調用驅動 jar包下META-INF\services\java.sqI.Driver文本中的類名稱去註冊 建議還是寫上,更加明確 */ //創建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的情況下,將信息放到配置文件裡,利於後續可持續操作 //獲取配置文件信息 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: 底層使用ArrayLis
t 存放每一行數據(二維字節數組,每一維表示一行中的一個數據)
Resultment: 用於執行靜態SQL語句並返回其生成的結果的對象,是一個接口,需要各個數據庫廠傢來實現。(實際中我們一般不用這個)
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註入: 是利用某些系統沒有對用戶輸入的數據進行充分的檢查,而在用戶輸入數據中註入非法的SQL語句段或命令,惡意攻擊數據庫。
例如下列代碼實現瞭註入問題(而Statement就存在這個問題,所以實際開發過程中不用它)
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註入問題!
- 大大減少瞭編譯次數,效率較高
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實現類的對象 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、工具類開發
由於在進行數據庫操作時,有些步驟是重復的,如連接,關閉資源等操作。
工具類
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; //連接數據庫的url private static String driver; //驅動 //靜態代碼塊進行行初始化 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) { //實際開發過程中(將編譯異常,改成運行異常,用戶可以捕獲異常,也可以默認處理該異常) 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 = ?"; //創建PreparedStatement preparedStatement = connection.prepareStatement(sql); //占位賦值 preparedStatement.setInt(1,2); //執行 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 = ?"; //創建PreparedStatement preparedStatement = connection.prepareStatement(sql); //占位符賦值 preparedStatement.setString(1, "sxy"); preparedStatement.setString(2, "男"); preparedStatement.setInt(3, 2); //執行 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條插入數據 執行時間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 //批處理 執行時間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()在底層把每一條數據加入到ArrayList 執行過程:檢查本條sql中的語法問題 -> 把本條sql語句加入到ArrayList -> 每1000條執行一次 批處理優點:減少瞭編譯次數,又減少瞭運行次數,效率大大提高 還需要在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、數據庫連接池
由於有很多用戶連接數據庫,而數據庫連接數量又是有限制的,而且就算連接並且關閉也是很耗時,所以就有瞭引入瞭數據庫連接池可以很好的來解決這個問題。下面是普通連接數據庫連接並且關閉5000次所耗時間6249毫秒,可以發下時間相對很長。
public class ConQuestion { public static void main(String[] args) { //看看連接-關閉 connection 會耗用多久 long start = System.currentTimeMillis(); System.out.println("開始連接....."); for (int i = 0; i < 5000; i++) { //使用傳統的jdbc方式,得到連接 Connection connection = JDBCUtils.getConnection(); //做一些工作,比如得到PreparedStatement ,發送sql //.......... //關閉 JDBCUtils.close(null, null, connection); } long end = System.currentTimeMillis(); System.out.println("傳統方式5000次 耗時=" + (end - start));//傳統方式5000次 耗時=6249 } }
11.1 數據庫連接池基本介紹
- 預先在緩沖池中放入一定數量的連接,當需要建立數據庫連接時,隻需從“緩沖池”中取出一個,使用完畢之後再放回去。
- 數據庫連接池負責分配,管理和釋放數據庫連接,它允許應用程序重復使用一個現有的數據庫連接,而不是重新建立一個。
- 當應用程序向連接池請求的連接數超過最大連接數量時,這些請求將被加入到等待隊列中。
11.2 JDBC的數據庫連接池使用
JDBC的數據庫連接池使用javax.sql.DataSource來表示,DataSource隻是一個接口,該接口通常由第三方提供實現。
11.3 數據庫連接池的種類
- C3P0 數據庫連接池,速度相對較慢,穩定性不錯(hibernate,spring)。(用的較多)
- DBCP數據庫連接池,速度相對c3p0較快,但不穩定。
- Proxool數據庫連接池,有監控連接池狀態的功能,穩定性較c3p0差一點。
- BoneCP 數據庫連接池,速度快。
- Druid (德魯伊)是阿裡提供的數據庫連接池,集DBCP,C3P0,Proxool優點於身的數據庫連接池。(應用最廣)
11.4 C3P0連接池
利用C3P0連接池再次嘗試連接5000次數據庫 可以發現耗時方式一僅僅花瞭456毫秒,第二種通過配置文件操作也是花瞭419毫秒差不多的時間,值得說的是這個連接池連接配置文件不能是我們自己寫,官方有給定的模板(c3p0.config.xml)。
public class C3P0_ { @Test //方式一: 相關參數,在程序中指定user,url,password等 public void testC3P0_1() throws Exception { //創建一個數據源對象 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"); //給數據源(comboPooledDataSource)設置相關參數 //連接管理是由comboPooledDataSource(連接池)來管理的 comboPooledDataSource.setDriverClass(driver); //設置驅動 comboPooledDataSource.setJdbcUrl(url); comboPooledDataSource.setUser(user); comboPooledDataSource.setPassword(password); //初始化數據源的連接數 comboPooledDataSource.setInitialPoolSize(10); //數據庫連接池最大容量,如果還有連接請求,那麼就會將該請求放入等待隊列中 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目錄下 //該文件指定瞭連接數據庫和連接池的相關參數 @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連接池連接數據庫500000次耗時643毫秒,而C3P0500000次連接耗時2373毫秒,很顯然Druid連接速度更快。
public class Druid_ { @Test public void testDruid() throws Exception { //1.加入Druid jar包 //2.加入 配置文件 druid.properties 放到src目錄下 //3.創建Properties對象 Properties properties = new Properties(); properties.load(new FileInputStream("src\\druid.properties")); //4.創建一個指定參數的數據庫連接池 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次創建 耗時=643 System.out.println("Druid的500000次創建 耗時=" + (end - start)); } }
對應的工具類
public class JDBCUtilsByDruid { private static DataSource ds; //在靜態代碼塊完成 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(); } //關閉連接:在數據庫連接池技術中,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 = ?"; //創建PreparedStatement preparedStatement = connection.prepareStatement(sql); //占位賦值 preparedStatement.setInt(1, 2); //執行 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存放數據集合,在connection關閉時,resultSet結果集無法使用。所以為瞭使用這些數據,也有JDBC官方提供的文件Apache-DBUtils來存放數據。
12.1 ArrayList模擬
ArrayList模擬Apache-DBUtils
Actor類 用來保存Actor表中的數據用的。
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來存放數據
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集合數據=" + 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 沒有任何關聯,所以該集合可以復用. //return list; } }
12.2 Apache-DBUtils
基本介紹
commons-dbutils是 Apache組織提供的一個開源JDBC工具類庫,它是對JDBC的封裝,使用dbutils能極大簡化jdbc編碼的工作量。
DbUtils類
- QueryRunner類:該類封裝瞭SQL的執行,是線程安全的。可以實現增,刪,改,查,批處理
- 使用QueryRunner類實現查詢。
- ResultSetHandler接口:該接口用於處理 java.sql.ResultSet,將數據按要求轉換為另一種形式
應用實例
使用Apache-DBUtils工具+數據庫連接池(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 //查詢多條數據 public void testQueryMany() throws Exception { //1.得到連接(Druid) Connection connection = JDBCUtilsByDruid.getConnection(); //2.使用 DBUtils 類和接口,先引入 DBUtils jar文件 ,放到src目錄下 //3.創建QueryRunner QueryRunner queryRunner = new QueryRunner(); //4.執行相應的方法,返回ArrayList結果集 String sql = "select * from actor where id >= ?"; //String sql = "select id,`name` from actor where id >= ?"; /* (1) query 方法就是執行sql 語句,得到resultSet ---封裝到 --> ArrayList 集合中 (2) 返回集合 (3) connection: 連接 (4) sql : 執行的sql語句 (5) new BeanListHandler<>(Actor.class): 在將resultSet -> Actor 對象 -> 封裝到 ArrayList 底層使用反射機制 去獲取Actor 類的屬性,然後進行封裝 (6) 1 就是給 sql 語句中的? 賦值,可以有多個值,因為是可變參數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);//創建PreparedStatement * this.fillStatement(stmt, params);//對sql 進行 ? 賦值 * rs = this.wrap(stmt.executeQuery());//執行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語句是固定,不能通過參數傳入,通用性不好,需要進行改進,更方便執行增刪改查
- 對於select 操作,如果有返回值,返回類型不能固定,需要使用泛型
- 將來的表很多,業務需求復雜,不可能隻靠一個JAVA類完成。
所以在實際開發中,也有解決辦法 —BasicDao
13.1 BasicDAO類
public class BasicDAO<T> { //泛型指定具體的類型 private QueryRunner queryRunner = new QueryRunner(); //開發通用的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 根據類似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); } } //返回單個對象(單行數據) 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.查詢單行單個數據 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的數據庫連接(JDBC)的文章就介紹到這瞭,更多相關Java的數據庫連接(JDBC)內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!