# JDBC和MySQL的使用 **一、概述** **1.軟體架構方式介紹** ![](https://i.imgur.com/IlY858C.png) 其中:Servlet最主要做三件事 * 獲取請求 * 處理請求 * 響應請求 JSP則是用以顯示動態頁面變化及響應請求 Cookie和Session分別是前端和後端紀錄用戶信息用。 **2.Java中的數據存儲技術** * 在Java中,數據庫存取技術可分為如下幾類: * JDBC直接訪問數據庫 * JDO(Java數據對象)技術 * 第三方O / R工具,如Hibernate,Mybatis等 * JDBC是java訪問數據庫的基石,JDO,Hibernate,MyBatis等只是更好的封裝了JDBC。 **3.JDBC介紹** * JDBC(Java數據庫連接性)是一個**獨立於特定數據庫管理系統,通用的SQL數據庫訪問和操作的公共接口(具有API),定義了可訪問數據庫的標準Java類庫,( java.sql,javax.sql )使用這些類庫可以以一種**標準**的方法,方便地訪問數據庫資源。 * JDBC為訪問不同的數據庫提供了一種統一的途徑,為開發者屏蔽了一些細節問題。 * JDBC的目標是使Java程序員使用JDBC可以連接任何提供提供的JDBC驅動程序的數據庫系統,這樣就可以使程序員無需對特定的數據庫系統的特點有過多的了解,從而大大簡化和 加快了開發過程。 * 如果沒有JDBC,那麼Java程序訪問數據庫時是這樣的: ![](https://i.imgur.com/cu3Wlc3.png) * 有了JDBC,Java程序訪問數據庫時是這樣的: ![](https://i.imgur.com/cIg6bb7.png) **4.jdbc程式編寫的步驟** ![](https://i.imgur.com/euvh5iv.png) 補充:ODBC( Open Database Connectivity ,開放式數據庫連接),是微軟在Windows平台下推出的。用戶在程序中只需要調用ODBC API,由ODBC驅動程序將調用轉換為對特定數據庫 的調用請求。 **二、獲取數據庫連接** <font color="#f00">1.新建javaProject</font> ![](https://i.imgur.com/ss5iYeC.png) <font color="#f00">2.添加mysql的jar檔</font> * 在project下自己建立一個lib資料夾放入mysql的jar檔 ![](https://i.imgur.com/OvymkyJ.png) * 並且右鍵mysql的jar檔-BuildPath-Add Build Path ![](https://i.imgur.com/0gEUimC.png) <font color="#f00">3.建立程式碼</font> Driver的實現類可由滑鼠點到Driver後,ctrl+t顯示。 URL:"jdbc:mysql://localhost:3306/test" * localhost:ip地址 3306:mysql端口號 test為數據庫名稱 方式一: ![](https://i.imgur.com/tibe1uO.png) --- 方式二:對方式一的迭代,在如下的程式碼中部出現第三方的api,使得程式碼具有更好的可移植性 ![](https://i.imgur.com/RI11lOg.png) --- 方式三:使用DriverManager替換Driver ![](https://i.imgur.com/sJ9wWlo.png) --- 方式四:可以只是加載驅動,不用顯示註冊驅動 ![](https://i.imgur.com/VHMRPXb.png) --- <font color="#f00">方式五(final版):將數據庫連接需要的4個基本信息聲明在配置文件中,通過讀取配置文件的方式,獲取連接</font> - 在專案的src下右鍵New File 名稱一般為jdbc.properties - 輸入4個基本信息 - 其中driverClass可以隨意取,全看加載驅動時用甚麼名而已,例如:Class.forName(driverClass); ![](https://i.imgur.com/QNgMCBR.png) ![](https://i.imgur.com/yd8Dsqf.png) 好處: 1.實現了數據與代碼的分離.實現解偶 2.如果需要修改配置文件信息,可以避免重新打包程式 其中:讀取配置文件的基本信息可以改寫為 ![](https://i.imgur.com/GqSuLaf.png) --- * 若想要加載實現類的源碼,於實現類的Driver()上 ctrl +左鍵 進入 ![](https://i.imgur.com/NvQw8w7.png) ![](https://i.imgur.com/mLN5EYa.png) 4.URL * JDBC URL用於標識一個被註冊的驅動程序,驅動程序管理器通過該URL選擇正確的驅動程序,從而建立到數據庫的連接。 * JDBC URL的標準由三部分組成,各部分之間用冒號分隔。 * jdbc:子協議:子名稱 * 協議**:JDBC URL中的協議總是jdbc * 子協議:子協議用於標識一個數據庫驅動程序 * 子名稱:一種標識數據庫的方法。子名稱可以依不同的子協議而變化,用子名稱的目的是為了定位數據庫提供足夠的信息。包含主機名(對應服務端的ip地址),端口號,數據庫名 * 例如 ![](https://i.imgur.com/UbE0eOT.png) <font color="#f00">三、使用PreparedStatement實現CRUD操作</font> 步驟為: <font color="#f00"> 1.獲取數據庫的連接 2.預編譯sql語句,返回PreparedStatement的實例 3.填充佔位符 4.執行 5.資源的關閉 </font> ![](https://i.imgur.com/BaWKrYz.png) --- <font color="#f00">插入資料:</font> ![](https://i.imgur.com/vyiebXr.png) ![](https://i.imgur.com/Z0PmEi0.png) ``` package com.atguigu3.perparedstatement.crud; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.sql.Date; import java.util.Properties; import org.junit.Test; import com.atguigu1.connection.ConnectionTest; public class PreparedStatementUpdateTest { // 向customers表中添加一條紀錄 @Test public void testInsert() { Connection conn = null; PreparedStatement ps = null; try { //1.讀取配置文件中的4個基本信息 //當前類名.class.getClassLoader =獲取類加載器 . 指定文件名 // InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties"); InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties"); Properties pros = new Properties(); pros.load(is);// 加載is文件 // 開始獲取信息 String user = pros.getProperty("user"); String password = pros.getProperty("password"); String url = pros.getProperty("url"); String driverClass = pros.getProperty("driverClass"); // 2.加載驅動 Class.forName(driverClass); // 3.獲取連接 conn = DriverManager.getConnection(url, user, password); System.out.println(conn); //4.預編譯sql語句,返回PreparedStatement的實例 String sql = "insert into customers(name,email,birth)values(?,?,?)"; //?表示佔位符 ps = conn.prepareStatement(sql); //5.填充佔位符 ps.setString(1, "哪扎"); ps.setString(2, "nazha@gmail.com"); //格式化日期格式 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); java.util.Date date = sdf.parse("1000-01-01"); ps.setDate(3, new Date(date.getTime())); //6.執行操作 ps.execute(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); }finally { //7.資料關閉 避免對象在創建時異常而產生空指針異常 try { if(ps != null) { ps.close(); } } catch (SQLException e) { e.printStackTrace(); } try { if(conn != null) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } } ``` --- --- <font color="#f00">因為建立連線和關閉連線必須重複且常常使用,因此封裝到一個class類的方法中。</font> ``` package com.atguigu.util; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; /* 操作數據庫的工具類 * */ public class JDBCUtils { // 獲取數據庫的連接 public static Connection getConnection() throws Exception { // 1.讀取配置文件中的4個基本信息 // 當前類名.class.getClassLoader =獲取類加載器 . 指定文件名 InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties"); Properties pros = new Properties(); pros.load(is);// 加載is文件 // 開始獲取信息 String user = pros.getProperty("user"); String password = pros.getProperty("password"); String url = pros.getProperty("url"); String driverClass = pros.getProperty("driverClass"); // 2.加載驅動 Class.forName(driverClass); // 3.獲取連接 Connection conn = DriverManager.getConnection(url, user, password); return conn; } public static void closerResource(Connection conn, Statement ps) { // 7.資料關閉 try { if (ps != null) { ps.close(); } } catch (SQLException e) { e.printStackTrace(); } try { if (conn != null) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } //關閉資源操作 public static void closerResource(Connection conn, Statement ps, ResultSet rs) { try { if (ps != null) { ps.close(); } } catch (SQLException e) { e.printStackTrace(); } try { if (conn != null) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } try { if (rs != null) { rs.close(); } } catch (SQLException e) { e.printStackTrace(); } } } ``` --- --- 使用封裝好的連線和關閉類的方法來修改資料庫中的數據: ``` package com.atguigu3.perparedstatement.crud; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.sql.Date; import java.util.Properties; import org.junit.Test; import com.atguigu.util.JDBCUtils; import com.atguigu1.connection.ConnectionTest; public class PreparedStatementUpdateTest { //修改customers表中一條紀錄 @Test public void testUpdate() { //1.獲取數據庫的連接 Connection conn = null; PreparedStatement ps = null; try { conn = JDBCUtils.getConnection(); //2.預編譯sql語句,返回PreparedStatement的實例 String sql = "update customers set name = ? where id = ? "; ps = conn.prepareStatement(sql); //3.填充佔位符 ps.setObject(1, "莫扎特"); ps.setObject(2,18); //4.執行 ps.execute(); } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); }finally { //5.資源的關閉 JDBCUtils.closerResource(conn, ps); } } ``` --- --- <font color="#f00">使用通用方法來進行增刪改操作</font> ``` package com.atguigu3.perparedstatement.crud; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.sql.Date; import java.util.Properties; import org.junit.Test; import com.atguigu.util.JDBCUtils; import com.atguigu1.connection.ConnectionTest; public class PreparedStatementUpdateTest { @Test public void testCommonUpdate() { // String sql = "delete from customers where id = ?"; // update(sql,3); String sql = "update `order` set order_name = ? where order_id = ?"; update(sql,"DD",2); } //通用的增刪改操作 sql中佔位符的個數語可變形參的長度相同 public void update(String sql,Object ...args){ Connection conn = null; PreparedStatement ps = null; try { //1.獲取數據庫的連接 conn = JDBCUtils.getConnection(); //2.預編譯sql語句,返回PreparedStatement的實例 ps = conn.prepareStatement(sql); //3.填充佔位符 for(int i = 0;i < args.length;i++) { ps.setObject(i + 1, args[i]);//小心參數聲明錯誤 } //4.執行 ps.execute(); } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); }finally { //5.資源關閉 JDBCUtils.closerResource(conn, ps); } } ``` --- --- <font color="#f00">三、數據庫的查詢</font> **1.java與sql對應數據類形轉換表** ![](https://i.imgur.com/otCAwxX.png) <font color="#f00">* *** 建立查詢操作** 1.先會才使用通用查詢</font> ``` package com.atguigu3.perparedstatement.crud; import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.junit.Test; import com.atguigu.util.JDBCUtils; import com.atguigu3.bean.Customer; //針對於customers表的查詢操作 public class CustomerForQuery { @Test public void testQuery1() { Connection conn = null; PreparedStatement ps = null; ResultSet resultSet = null; try { conn = JDBCUtils.getConnection(); String sql = "select id,name,email,birth from customers where id = ?"; ps = conn.prepareStatement(sql); ps.setObject(1,1); //執行並返回結果集 resultSet = ps.executeQuery(); //處理結果集 if(resultSet.next()) {//判斷結果集的下一條是否有數據,如果有數據返回true,並指針下移,如果返回faulse,指針不下移,直接結束 //獲取當前這條數據的各個字段直 int id = resultSet.getInt(1); String name = resultSet.getString(2); String email = resultSet.getString(3); Date birth = resultSet.getDate(4); //方式一: // System.out.println("id =" +id +",name="+name + ",email="+email+",birth="+birth ); //方式二: // Object[] date = new Object[] {id,name,email,bith}; //方式三:將數據封裝為一個對象(推薦!!!!!!!!!!!) Customer customer = new Customer(id,name,email,birth); System.out.println(customer); } } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); }finally { //關閉資源 JDBCUtils.closerResource(conn, ps, resultSet); } } } ``` <font color="#f00">* *** 建立一個類用來接收查詢操作返回的數據**</font> ``` package com.atguigu3.bean; import java.sql.Date; /* * ORM编程思想 (object relational mapping) * 一个数据表对应一个java类 * 表中的一条记录对应java类的一个对象 * 表中的一个字段对应java类的一个属性 * */ public class Customer { private int id; private String name; private String email; private Date birth; public Customer() { super(); } public Customer(int id, String name, String email, Date birth) { super(); this.id = id; this.name = name; this.email = email; this.birth = birth; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } @Override public String toString() { return "Customer [id=" + id + ", name=" + name + ", email=" + email + ", birth=" + birth + "]"; } } ``` --- 進階: * 針對用customer表(單一表)的通用查詢,使用到反射技術 ``` package com.atguigu3.perparedstatement.crud; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import org.junit.Test; import com.atguigu.util.JDBCUtils; import com.atguigu3.bean.Customer; //針對於customers表的查詢操作 public class CustomerForQuery { @Test public void testQueryForCustomers() { String sql = "select id,name,birth,email from customers where id = ?"; Customer customer = queryForCustomers(sql,13); System.out.println(customer); sql = "select name,email from customers where name = ?"; Customer customer1 = queryForCustomers(sql,"周杰伦"); System.out.println(customer1); } //針對於cuctomer表的通用查詢操作 public Customer queryForCustomers(String sql,Object...args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); for(int i = 0; i < args.length;i++) { ps.setObject(i + 1 , args[i]); } //獲取結果集 rs = ps.executeQuery(); //獲取結果集的元數據:ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); //通過ResultSetMetaData獲取結果集中的列數 int columnCount = rsmd.getColumnCount(); if(rs.next()) { Customer cust = new Customer(); //處理結果及一行數據中的每一個列 for(int i = 0;i < columnCount; i++) { //獲取列值:通過ResultSet Object columnvalue = rs.getObject(i + 1); //獲取每個列的列名:通過ResultSetMetaData String columnName = rsmd.getColumnName(i + 1); String columnLabel = rsmd.getColumnLabel(i + 1); //給cust對象指定的cloumnName屬性,賦值為columnvalue,通過反射 //拿到columnName的屬性 Field field = Customer.class.getDeclaredField(columnLabel); //個人覺得只要Field.setAccessible(true); 之後,即使是final關鍵字標示過得屬性也可以有訪問許可權!這樣的反射會改變JAVA的結構, field.setAccessible(true); field.set(cust, columnvalue); } return cust; } } catch (Exception e) { e.printStackTrace(); }finally { JDBCUtils.closerResource(conn, ps, rs); } return null; } ``` --- --- order表測試: 使用一般方法查詢表 ``` //一般查詢 @Test public void testQuery1() { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); String sql = "select order_id,order_name,order_date from `order` where order_id = ?"; ps = conn.prepareStatement(sql); ps.setObject(1, 1); rs = ps.executeQuery(); if (rs.next()) { int id = (int) rs.getObject(1); String name = (String) rs.getObject(2); Date date = (Date) rs.getObject(3); Order order = new Order(id, name, date); System.out.println(order); } } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.closerResource(conn, ps, rs); } } } ``` 資料庫表格order對應的類 ``` package com.atguigu3.bean; import java.sql.Date; public class Order { private int orderId; private String orderName; private Date orderDate; public Order() { super(); } public Order(int orderId, String orderName, Date orderDate) { super(); this.orderId = orderId; this.orderName = orderName; this.orderDate = orderDate; } public int getOrderId() { return orderId; } public void setOrderId(int orderId) { this.orderId = orderId; } public String getOrderName() { return orderName; } public void setOrderName(String orderName) { this.orderName = orderName; } public Date getOrderDate() { return orderDate; } public void setOrderDate(Date orderDate) { this.orderDate = orderDate; } @Override public String toString() { return "Order [orderId=" + orderId + ", orderName=" + orderName + ", orderDate=" + orderDate + "]"; } } ``` --- --- order表格通用查詢方法!!!!! ``` package com.atguigu3.perparedstatement.crud; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import org.junit.Test; import com.atguigu.util.JDBCUtils; import com.atguigu3.bean.Order; /* * 針對於order表的通用查詢 * 针对于表的字段名与类的属性名不相同的情况: * 1. 必须声明sql时,使用类的属性名来命名字段的别名 * 2. 使用ResultSetMetaData时,需要使用getColumnLabel()来替换getColumnName(), * 获取列的别名。 * 说明:如果sql中没有给字段其别名,getColumnLabel()获取的就是列名 * * */ public class OrderForQuery { @Test public void testOrderForQuery() { String sql = "select order_id orderId,order_name orderName,order_date orderDate from `order` where order_id = ?"; Order order = orderForQuery(sql,1); System.out.println(order); } //針對於order表的通用查詢 public Order orderForQuery(String sql ,Object...args){ Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); for(int i =0;i < args.length;i++) { ps.setObject(i + 1, args[i]); } //執行,獲取結果集 rs = ps.executeQuery(); //獲取結果集的元數據 ResultSetMetaData rsmd = rs.getMetaData(); //獲取列數 int columnCount = rsmd.getColumnCount(); if(rs.next()) { Order order = new Order(); for(int i =0; i < columnCount;i++) { //獲取每個列的列值:通過ResultSet Object columnValue = rs.getObject(i + 1); //通過ResultSetMetaData //獲取每個列的列名:getColumnName()- 不推薦使用 //獲取列的別名:getColumnLabel() String columnName = rsmd.getColumnName(i + 1); String columnLabel = rsmd.getColumnLabel(i + 1); //通過反射,將對象指定名columnName的屬性值賦值為指定的值columnValue Field field = Order.class.getDeclaredField(columnLabel); //保證私有的也能訪問 field.setAccessible(true); field.set(order, columnValue); } return order; } } catch (Exception e) { e.printStackTrace(); }finally { JDBCUtils.closerResource(conn ,ps,rs); } return null; //反射做的時候會拿資料庫表格裡面的欄名到類裡面找有沒有相同名稱的屬性 //所以可以給表格裡面的欄名起別名,別名依照類裡面的屬性名起 //而且在獲取列名的後去要獲取別名,使用:rsmd.getColumnLabel } ``` # 查詢操作的流程 ![](https://i.imgur.com/Qv8l5mX.png) --- 筆記: * 任何一個類,在建置javabean時,必須提供一個空參的構造方法. * 使用Scanner輸入日期格式時,若是格式為"yyyy-mm-dd" String的話,則傳至mysql資料庫中會發生隱式轉換。若是其他隔式則會報錯。 * ps.execute(); 如果執行的是查詢操作,有返回結果,則此方法返回true; 如果執行的是增、刪、改操作,沒有返回結果,則此方法返回false *ps.executeUpdate(): 返回值1為增刪改操作成功 0則是失敗 * String.equalsIgnoreCase("a"): 這個方法比較的時候忽略大小寫 --- 使用通用方法獲取多個表中的數據 ``` package com.atguigu3.perparedstatement.crud; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import org.junit.Test; import com.atguigu.util.JDBCUtils; import com.atguigu3.bean.Customer; import com.atguigu3.bean.Order; //使用PreparedStatement時限針對於不同表的通用查詢操作 public class PreparedStatementQueryTest { @Test public void testGetInstance() { String sql = "select id,name,email from customers where id =?"; Customer customer = getInstance(Customer.class,sql,12); System.out.println(customer); String sql1 = "select order_id orderId,order_name orderName from `order` where order_id =?"; Order order = getInstance(Order.class, sql1, 1); System.out.println(order); } public <T>T getInstance(Class<T> clazz,String sql, Object... args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } // 獲取結果集 rs = ps.executeQuery(); // 獲取結果集的元數據:ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 通過ResultSetMetaData獲取結果集中的列數 int columnCount = rsmd.getColumnCount(); if (rs.next()) { T t = clazz.newInstance(); // 處理結果及一行數據中的每一個列 for (int i = 0; i < columnCount; i++) { // 獲取列值:通過ResultSet Object columnvalue = rs.getObject(i + 1); // 獲取每個列的列名:通過ResultSetMetaData String columnName = rsmd.getColumnName(i + 1); String columnLabel = rsmd.getColumnLabel(i + 1); // 給cust對象指定的cloumnName屬性,賦值為columnvalue,通過反射 // 拿到columnName的屬性 Field field = clazz.getDeclaredField(columnLabel); // 個人覺得只要Field.setAccessible(true); // 之後,即使是final關鍵字標示過得屬性也可以有訪問許可權!這樣的反射會改變JAVA的結構, field.setAccessible(true); field.set(t, columnvalue); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.closerResource(conn, ps, rs); } return null; } } ``` --- --- 針對於多個表的多個對象查詢,使用通用查詢方法: ``` package com.atguigu3.perparedstatement.crud; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.util.ArrayList; import java.util.List; import org.junit.Test; import com.atguigu.util.JDBCUtils; import com.atguigu3.bean.Customer; import com.atguigu3.bean.Order; //使用PreparedStatement時限針對於不同表的通用查詢操作 public class PreparedStatementQueryTest { @Test public void testGetForList() { String sql = "select id,name,email from customers where id < ?"; List<Customer> list = getForList(Customer.class,sql,12); list.forEach(System.out::println);//lanbda表達式 String sql1 = "select order_id orderId,order_name orderName from `order` "; List<Order> orderList = getForList(Order.class, sql1); orderList.forEach(System.out::println); } public <T> List<T> getForList(Class<T> clazz,String sql, Object... args){ Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } // 獲取結果集 rs = ps.executeQuery(); // 獲取結果集的元數據:ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 通過ResultSetMetaData獲取結果集中的列數 int columnCount = rsmd.getColumnCount(); //創建集合對象 ArrayList<T> list = new ArrayList<T>(); while (rs.next()) { T t = clazz.newInstance(); // 處理結果及一行數據中的每一個列:給t對象指定的屬性賦值 for (int i = 0; i < columnCount; i++) { // 獲取列值:通過ResultSet Object columnvalue = rs.getObject(i + 1); // 獲取每個列的列名:通過ResultSetMetaData String columnName = rsmd.getColumnName(i + 1); String columnLabel = rsmd.getColumnLabel(i + 1); // 給cust對象指定的cloumnName屬性,賦值為columnvalue,通過反射 // 拿到columnName的屬性 Field field = clazz.getDeclaredField(columnLabel); // 個人覺得只要Field.setAccessible(true); // 之後,即使是final關鍵字標示過得屬性也可以有訪問許可權!這樣的反射會改變JAVA的結構, field.setAccessible(true); field.set(t, columnvalue); } list.add(t); } return list; } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.closerResource(conn, ps, rs); } return null; } ```