1. 程式人生 > 其它 >Java高階特性 - JDBC(下)

Java高階特性 - JDBC(下)

技術標籤:Educoder實訓之Java

第1關:指定型別JDBC封裝
任務描述
本關任務:按照要求封裝一個JDBC工具類。
相關知識
為了完成本關任務,你需要掌握JDBC的基本使用,可參考上一個實訓內容 Java高階特性 - JDBC(上)。
本章節將針對已知資料結構的某張表進行JDBC的封裝。
連線資料庫
在增刪改查的過程中,我們都離不開資料庫的連線,因此我們可以將其操作封裝成一個方法,方法無需引數,將連線物件作為返回值。
在方法中完成驅動載入和資料庫連線即可使用:

private static Connection getConnection() {
    //1.載入驅動
    //2.連線資料庫
    Connection conn=null;
    //返回連線物件
    return conn;
}

封裝完後我們就可直接在增刪改查中直接使用該方法了。
關閉資料庫連線
同樣每次連線完資料庫我們都需要對相應資源進行釋放,我們也將其封裝為一個方法,方法引數為經常被使用到的物件,這些物件通常是ResultSet, Statement和Connection,因此我們的關閉連線方法如下:

public static void close(ResultSet rs,PreparedStatement ps,Connection conn){
    try {
        if(rs!=null)  rs.close();
        if(ps!=null)  ps.close();
        if(conn!=null)  conn.close();
    } catch (SQLException e) {
        e.printStackT\frace();
    }
}

新增資料
現我們資料庫中已有的一張新聞表news,結構如下:
在這裡插入圖片描述
根據表結構我們建立一個News物件:

public class News {
    private int id;
    private String title;
    private String anthor_name;
    public News(int id, String title, String anthor_name) {
        super();
        this.id = id;
        this.title = title;
        this.anthor_name = anthor_name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    ......//省略其他屬性的get、set方法
}

日常生活中我們每天都會看到很多新聞,因此我們需要往資料庫中不斷新增最新新聞,下面我們一起來對新增方法進行封裝:
封裝前我們先對方法進行構思,該方法需傳遞一個News物件,無需返回值,因此方法的定義和實現思路如下:

public void insert(News news) throws SQLException {
    Connection conn = getConnection();//拿到連線物件
    PreparedStatement ps = null;
    //編寫新增sql語句
    String sql = "";
    try{
        ps = conn.prepareStatement(sql);
        //通過傳入的news物件對預編譯中 ? 進行賦值
        ps.setXXX(1,news.getXXX());
        //執行新增sql語句
        ps.executeUpdate();
    }catch(SQLException e){
        e.printStackT\frace();
    }finally{
        //關閉連線
        close(null, ps, conn);
    }
}

程式設計要求
在右側編輯器補充程式碼,完成資料庫連線、刪除、更新以及查詢方法。其中刪除方法是通過使用者傳入新聞id來進行刪除。
注意:連線資料庫名為mysql_db,資料庫使用者為root,密碼為123123。
測試說明
平臺會對你編寫的程式碼進行測試:
測試輸入:無
預期輸出:
News [id=1, title=岳雲鵬的18歲,賈玲的18歲,沈騰的18歲,網友:不是來搞笑的?, anthor_name=光明網]
說明:測試檔案中會向news表中插入二條新聞資料,以便對你編寫的方法進行檢測,資料如下:
在這裡插入圖片描述
開始你的任務吧,祝你成功!
參考程式碼:

package step1;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import test.News;
public class JDBCUtils {
    /**
     * 連線資料庫
     */
    private static Connection getConnection() {
        Connection conn=null;
        /**********  Begin  **********/
        String url="jdbc:mysql://localhost:3306/mysql_db";
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(url, "root","123123");
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (SQLException e) {
            e.printStackTrace();
        }
        /**********   End   **********/
        return conn;
    }
    /**
     * 更新資料方法
     * @param news
     * @throws SQLException
     */
    public void update(News news) throws SQLException {
        Connection conn = getConnection();
        PreparedStatement ps = null;
        /**********  Begin  **********/
        String sql = "update news set title=?,author_name=? where id=?";
        try{
            ps = conn.prepareStatement(sql);
            ps.setString(1, news.getTitle());
            ps.setString(2, news.getAuthor_name());
            ps.setInt(3, news.getId());
            ps.executeUpdate();
        }catch(SQLException e){
            e.printStackTrace();
            throw new SQLException("更新資料失敗");
        }finally{
            close(null, ps, conn);
        }    
        /**********  End  **********/
    }
    /**
     * 查詢所有資料
     * @return
     * @throws SQLException
     */
    public List<News> findAll() throws SQLException {
        Connection conn =  getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        News news = null;
        List<News> newsList = new ArrayList<News>();
        /**********  Begin  **********/
        String sql = "select * from news";
        try{
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();
            while(rs.next()){
                news = new News();
                news.setId(rs.getInt(1));
                news.setTitle(rs.getString(2));
                news.setAuthor_name(rs.getString(3));
                newsList.add(news);
            }
        }catch(SQLException e){
            e.printStackTrace();
            throw new SQLException("查詢所有資料失敗");
        }finally{
            close(rs, ps, conn);
        }
        /**********  End  **********/
        return newsList;
    }
    /**
     * 刪除方法
     * @param id
     * @throws SQLException
     */
    public void delete(int id) throws SQLException{
        Connection conn = getConnection();
        PreparedStatement ps = null;
        /**********  Begin  **********/
        String sql = "delete from news where id=?";
        try{
            ps = conn.prepareStatement(sql);
            ps.setInt(1,id);
            ps.executeUpdate();
        }catch(SQLException e){
            e.printStackTrace();
            throw new SQLException(" 刪除資料失敗");
        }
        finally{
            close(null, ps, conn);
        }        
        /**********  End  **********/
    }
    /**
     * 增加物件
     * @param news
     * @throws SQLException
     */
    public void insert(News news) throws SQLException {
        Connection conn = getConnection();
        PreparedStatement ps = null;
        String sql = "insert into news(id,title,author_name)values(?,?,?)";
        try{
            ps = conn.prepareStatement(sql);
            ps.setInt(1, news.getId());
            ps.setString(2, news.getTitle());
            ps.setString(3, news.getAuthor_name());
            ps.executeUpdate();
        }catch(SQLException e){
            e.printStackTrace();
            throw new SQLException("新增資料失敗");
        }finally{
           close(null, ps, conn);
        }
    }
    /**
     * 根據id查詢物件
     * @param id
     * @return
     * @throws SQLException
     */
    public News findById(int id) throws SQLException {
        Connection conn = getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        News news = null;
        String sql = "select * from news where id=?";
        try{
            ps = conn.prepareStatement(sql);
            ps.setInt(1, id);
            rs = ps.executeQuery();
            if(rs.next()){
            	news = new News();
            	news.setId(id);
            	news.setTitle(rs.getString(2));
            	news.setAuthor_name(rs.getString(3));
            }
        }catch(SQLException e){
            e.printStackTrace();
            throw new SQLException("根據ID查詢資料失敗");
        }
        finally{
            close(rs, ps, conn);
        }
        return news;
    }
    /**
     * 關閉資料庫連線
     * @param rs
     * @param ps
     * @param conn
     */
    public static void close(ResultSet rs,PreparedStatement ps,Connection conn){
    	try {
    		if(rs!=null)rs.close();
    		if(ps!=null)ps.close();
    		if(conn!=null)conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
    }
}	

第2關:泛型JDBC封裝
任務描述
本關任務:封裝一個泛型類的JDBC工具類。
相關知識
上一章節中我們對具體類(News)進行JDBC的封裝,但是在一個專案中,資料庫中的表遠不止一個,難道我們需要對每一張表都進行封裝嗎?顯然是不妥的,因此我們可以將JDBC工具類封裝為一個泛型的。
在學習了反射( Java高階特性 - Java反射 )之後,我們都知道反射機制的強大,利用反射可以獲取到類的結構資訊,動態呼叫屬性和方法等等。因此,本章節我們採用反射對JDBC的增刪改查進行泛型的封裝。
為了完成本關任務,你需要掌握:反射的常用方法。
反射的常用方法
獲取Class的例項的三種方法

Class class=類名.class;
Class class=Class.forName("全類名");
Class class=物件.getClass();

獲取物件的類名

String className=class.getName();//獲取結果為全類名
String className=class.getSimpleName();//獲取簡單類名

獲取Field

Field field=class.getField("屬性名");//通過屬性名獲取public的屬性
Field[] fields=class.getFields();//獲取所有用public修飾的屬性
Field field=class.getDeclaredField("屬性名");//獲取的屬性包括public和private
Field[] field = c.getDeclaredFields();//獲取所有屬性包括public和private

獲取Field的資訊

String name=field.getName();//獲取屬性名
Class<?> type=filed.getType();//獲取屬性型別
Object value=field.get(obj);//獲取obj物件field屬性的值
field.set(obj,value);//設定obj物件的field屬性的值

設定private修飾的屬性為可訪問

field.setAccessible(true);//預設為false只能對public修飾的操作,設定為true可對private修飾的操作

更新資料的泛型封裝分析及實現
我們可以從sql語句來進行分析,更新資料的sql,大家都不陌生:update 表名 set column2=value2,columen3=value3 where column1=value1;
觀察sql語句我們可以將該方法設計為讓使用者傳入一個Object物件,然後利用反射獲取物件中的所有屬性對其進行修改,具體實現如下(注意Object物件屬性名稱要求和資料庫中表結構欄位名以及型別一致):

public static void update(Object obj) {
    Connection conn = getConnection();//獲取連線物件
    PreparedStatement ps = null;
    Class<?> c = obj.getClass();//獲取obj的Class
    StringBuffer sb = new StringBuffer("update "+ c.getSimpleName() +" set ");//利用StringBuffer進行修改SQL語句的構造
    Field[] field = c.getDeclaredFields();//通過反射獲取物件的屬性陣列
    for(int i = 1; i < field.length; i++) {
        if(i != field.length-1) {    //判斷是否為最後一個屬性,若不是則後增加逗號
            sb.append(field[i].getName()).append("=?,");
        }else {    //若為最後一個屬性則新增 where
            sb.append(field[i].getName()).append("=? where ");
        }
    }
    //預設第一個屬性為主鍵,切更改時通過第一個屬性進行更改
    sb.append(field[0].getName() + "=?");
    try {
        ps = conn.prepareStatement(sb.toString());
        for(int i = 1; i < field.length; i++) {
            field[i].setAccessible(true);//設定可以訪問私有屬性
            ps.setObject(i, field[i].get(obj));//對預編譯的SQL語句中的 ? 進行賦值
        }
        field[0].setAccessible(true);
        ps.setObject(field.length, field[0].get(obj));
        ps.execute();//執行sql語句
    } catch (Exception e) {
        e.printStackT\frace();
    }finally {
        close(null,ps,conn);//關閉連線資料
    }
}

程式設計要求
根據更新資料的示例,在右側編輯器補充程式碼,完成增加資料、刪除資料、查詢表中所有資料三個方法。
測試說明
測試輸入:無
預期輸出:
Student [id=2, name=李四, sex=男, age=20]
News [id=1, title=岳雲鵬的18歲,賈玲的18歲,沈騰的18歲,網友:不是來搞笑的?, author_name=光明網]
News [id=2, title=假設飛行器每秒跑1光年,能飛到宇宙邊緣嗎?科學家說出答案, author_name=探索宇宙奧祕]
平臺將會根據已有的Student表和News表來呼叫你所編寫的方法,進行資料的增刪改查,分別為二張表中插入二條資料載進行修改News
表中一條資料,刪除Student表中一條資料,插入資料如下:
Student表
在這裡插入圖片描述
News表
在這裡插入圖片描述
注意:本章實訓封裝的JDBC工具類只做參考,你也可根據自己的實際需求進行封裝屬於自己的工具類。
開始你的任務吧,祝你成功!
參考程式碼:

package step2;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class JDBCUtils {
	private static Connection getConnection() {
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		String url="jdbc:mysql://localhost:3306/mysql_db";
		Connection conn=null;
		try {
			conn = DriverManager.getConnection(url, "root","123123");
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}
	/**
     * 類名對應表,屬性對應欄位
     * @param obj  傳入的物件
     * @return
     */
    public void insert(Object obj) {
    	Connection conn = getConnection();  //連線資料庫
    	PreparedStatement ps = null;
    	/********** Begin **********/
    	//獲取obj的Class
        Class<?> c = obj.getClass();        
        //利用StringBuffer進行插入SQL語句的構造
        StringBuffer sb1 = new StringBuffer("insert into "+ c.getSimpleName() +"(");  //通過反射獲取類名對映表名
        StringBuffer sb2 = new StringBuffer(" values(");  //注意前面要多加一個空格 否則sql將連在一起
        Field[] field = c.getDeclaredFields();            //獲取物件的屬性陣列
        for(int i = 0; i < field.length; i++) {           //遍歷屬性構造SQL語句
            if(i != field.length-1) {
                sb1.append(field[i].getName()).append(",");
                sb2.append("?,");
            }else {
                sb1.append(field[i].getName()).append(")");
                sb2.append("?);");
            }
        }
        String sql = sb1.append(sb2).toString();
        try {
            ps = conn.prepareStatement(sql);
            for(int i = 0; i < field.length; i++) {    
                field[i].setAccessible(true);                    //設定屬性的可訪問性,可以訪問私有屬性
                try {                                            //通過Field的get(Object)方法獲取Object物件的屬性值
                    ps.setObject(i+1, field[i].get(obj));        //對預編譯的SQL語句中的?進行賦值
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            ps.execute();        //執行SQL
        }
        /********** End **********/
        catch (SQLException e) {
            e.printStackTrace();
        }finally {    
            close(null,ps,conn);
        }
    }
    /**
     * 通過物件的Class獲取對應表中的所有記錄
     * @param c
     * @return
     */
    public <T> List<T> selectAll(Class<T> c) {
    	Connection conn = getConnection();
    	List<T> list = new ArrayList<T>();          
    	PreparedStatement ps = null;
    	ResultSet rs = null;
    	/********** Begin **********/
       String sql = "select * from "+ c.getSimpleName()+";";        //通過反射獲取類名對應表名構造SQL語句
        Field[] field = c.getDeclaredFields();                        //通過反射獲取所有屬性
        try {
            ps = conn.prepareStatement(sql);            
            rs = ps.executeQuery();                     
            while(rs.next()) {
                T obj = c.newInstance();                //通過反射構造一個T型別的例項
                for(int i = 0; i < field.length; i++) {        
                    field[i].setAccessible(true);                            //設定可以訪問私有屬性
                    field[i].set(obj, rs.getObject(field[i].getName()));    //通過屬性名獲取結果集中的值賦值到例項物件中
                }
                list.add(obj);                                                //將例項物件新增到list集合
            }
         }
        /********** End **********/ 
        catch (Exception e) {
            e.printStackTrace();
        }finally {    
            close(rs,ps,conn);
        }
        return list;
    }
    /**
     * 通過主鍵(預設第一個屬性)刪除物件
     * @param obj
     * @return
     */
    public void delete(Object obj) {
    	Connection conn = getConnection();
    	PreparedStatement ps = null;
    	/********** Begin **********/
       //獲取obj的Class
        Class<?> c = obj.getClass();        
        //構造刪除的SQL語句
        StringBuffer sb = new StringBuffer("delete from ");
        sb.append(c.getSimpleName()).append(" where ");
        //獲取物件屬性陣列
        Field[] field = c.getDeclaredFields();
        //設定第一個屬性的可訪問性
        field[0].setAccessible(true);
        //獲取第一個屬性的屬性名構造刪除sql
        sb.append(field[0].getName()).append("=?");
        String sql = sb.toString();
        try {
            ps = conn.prepareStatement(sql);
            ps.setObject(1, field[0].get(obj));    
            ps.execute();
        } 
        /********** End **********/
        catch (Exception e) {
            e.printStackTrace();
        }finally {    
            close(null,ps,conn);
        }
    }
    /**
     * 模擬jdbc的更新操作,預設第一個屬性為主鍵
     * @param obj
     * @return
     */
    public void update(Object obj) {
    	Class<?> c = obj.getClass();//獲取obj的Class
    	StringBuffer sb = new StringBuffer("update "+ c.getSimpleName() +" set ");//利用StringBuffer進行修改SQL語句的構造
    	Field[] field = c.getDeclaredFields();//通過反射獲取物件的屬性陣列
    	for(int i = 1; i < field.length; i++) {
    		if(i != field.length-1) {    //判斷是否為最後一個屬性,若不是則後增加逗號
    			sb.append(field[i].getName()).append("=?,");
    		}else {    //若為最後一個屬性則新增 where
    			sb.append(field[i].getName()).append("=? where ");
    		}
    	}
    	//預設第一個屬性為主鍵,切更改時通過第一個屬性進行更改
    	sb.append(field[0].getName() + "=?");
    	String sql = sb.toString()+";";
    	Connection conn = getConnection();//獲取連線物件
    	PreparedStatement ps = null;
    	try {
    		ps = conn.prepareStatement(sql);
    		for(int i = 1; i < field.length; i++) {
    			field[i].setAccessible(true);//設定可以訪問私有屬性
    			ps.setObject(i, field[i].get(obj));//對預編譯的SQL語句中的 ? 進行賦值
    		}
    		field[0].setAccessible(true);
    		ps.setObject(field.length, field[0].get(obj));
    		ps.execute();//執行sql語句
    	} catch (Exception e) {
    		e.printStackTrace();
    	}finally {
    		close(null,ps,conn);//關閉連線資料
    	}
    }
    public static void close(ResultSet rs,PreparedStatement ps,Connection conn){
    	try {
    		if(rs!=null) rs.close();
    		if(ps!=null) ps.close();
    		if(conn!=null) conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
    }
    public <T> Object selectById(Class<T> c,int id) {
        String sql = "select * from "+ c.getSimpleName()+" where id="+id;    
        Field[] field = c.getDeclaredFields();
        Connection conn = getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        Object obj=null;
        try {
            ps = conn.prepareStatement(sql);  
            rs = ps.executeQuery(); 
            obj = c.newInstance();
            while(rs.next()) {
                for(int i = 0; i < field.length; i++) {      
                    field[i].setAccessible(true);           
                    field[i].set(obj, rs.getObject(field[i].getName()));   
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {    
            close(rs,ps,conn);
        }
        return obj;
    }
}