JDBC進(jìn)階 & BeanUtils組件簡(jiǎn)介

預(yù)編譯sql處理(防止sql注入)
  • Statement : 執(zhí)行SQL命令
    • CallableStatement : 執(zhí)行存儲(chǔ)過程
    • PreparedStatement : 預(yù)編譯SQL語(yǔ)句執(zhí)行
  • 使用預(yù)編譯SQL語(yǔ)句的命令對(duì)象的好處 : 避免頻繁sql拼接 (可以使用占位符), 可以防止sql注入
    • 例如: 登陸模塊輸入用戶名,密碼的時(shí)候, 可以避免用戶輸入的惡意密碼
public class App {
    // 連接參數(shù)
    //private String url = "jdbc:mysql://localhost:3306/jdbc_demo";
    private String url = "jdbc:mysql:///jdbc_demo";
    private String user = "root";
    private String password = "root";
    private Connection con;
    private Statement stmt;
    private PreparedStatement pstmt;
    private ResultSet rs;
    // 1. 沒有使用防止sql注入的案例
    @Test
    public void testLogin() {
        // 1.0 模擬登陸的用戶名缤底,密碼
        String userName = "tom";
        //String pwd = "8881";
        String pwd = " ' or 1=1 -- ";
        // SQL語(yǔ)句
        String sql = "select * from admin where userName='"+userName+"'  and pwd='"+pwd+"' ";
        System.out.println(sql);
        try {
            // 1.1 加載驅(qū)動(dòng)汰寓,創(chuàng)建連接
            Class.forName("com.mysql.jdbc.Driver");     
            con = DriverManager.getConnection(url, user, password);
            // 1.2 創(chuàng)建stmt對(duì)象
            stmt = con.createStatement();
            // 1.3 執(zhí)行查詢
            rs = stmt.executeQuery(sql);
            // 業(yè)務(wù)判斷
            if (rs.next()) {
                System.out.println("登陸成功, 編號(hào):" + rs.getInt("id"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 1.4 關(guān)閉
            try {
                rs.close();
                stmt.close();
                con.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    // 2. 使用PreparedStatement, 防止sql注入
    @Test
    public void testLogin2() {
        
        // 1.0 模擬登陸的用戶名莹菱,密碼
        String userName = "tom";
        //String pwd = "8881";
        String pwd = " ' or 1=1 -- ";
        
        // SQL語(yǔ)句
        String sql = "select * from admin where userName=?  and pwd=? ";
        try {
            // 1.1 加載驅(qū)動(dòng),創(chuàng)建連接
            Class.forName("com.mysql.jdbc.Driver");     
            con = DriverManager.getConnection(url, user, password);
            // 1.2 創(chuàng)建pstmt對(duì)象
            pstmt = con.prepareStatement(sql);   // 對(duì)sql語(yǔ)句預(yù)編譯
            // 設(shè)置占位符值
            pstmt.setString(1, userName);
            pstmt.setString(2, pwd);
            
            // 1.3 執(zhí)行
            rs = pstmt.executeQuery();
            if (rs.next()) {
                System.out.println("登陸成功," + rs.getInt("id"));
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 1.4 關(guān)閉
            try {
                rs.close();
                pstmt.close();
                con.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }   
}
存儲(chǔ)過程調(diào)用
  • 存儲(chǔ)過程的定義 :
-- 存儲(chǔ)過程
-- 定義分隔符
DELIMITER $$
CREATE PROCEDURE proc_login()
BEGIN
   SELECT * FROM admin;
END $$
-- 調(diào)用
CALL proc_login;
  • 存儲(chǔ)過程使用示例 :
public class App_call {
    // 全局參數(shù)
    private Connection con;
    private Statement stmt;
    private PreparedStatement pstmt;
    private CallableStatement cstmt;  // 存儲(chǔ)過程
    private ResultSet rs;
    // 程序中調(diào)用存儲(chǔ)過程
    @Test
    public void testCall() throws Exception {
        try {
            //1 . 創(chuàng)建連接
            con = JdbcUtil.getConnection();
            //2.  創(chuàng)建執(zhí)行存儲(chǔ)過程的stmt對(duì)象
            CallableStatement cstmt = con.prepareCall("CALL proc_login");
            //3.  執(zhí)行(存儲(chǔ)過程)
            rs = cstmt.executeQuery();
            
            // 遍歷結(jié)果棋弥,測(cè)試
            if (rs.next()) {
                String name = rs.getString("userName");
                String pwd = rs.getString("pwd");
                // 測(cè)試
                System.out.println(name + pwd);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
批處理
  • 業(yè)務(wù)場(chǎng)景:當(dāng)需要向數(shù)據(jù)庫(kù)發(fā)送一批SQL語(yǔ)句執(zhí)行時(shí),應(yīng)避免向數(shù)據(jù)庫(kù)一條條的發(fā)送執(zhí)行诚欠,而應(yīng)采用JDBC的批處理機(jī)制顽染,以提升執(zhí)行效率漾岳。
  • 執(zhí)行批處理SQL語(yǔ)句:
    1. executeBatch()方法:執(zhí)行批處理命令
    2. clearBatch()方法:清除批處理命令
  • 實(shí)現(xiàn)批處理有兩種方式,
    • 第一種方式:Statement.addBatch(sql) list
      • 優(yōu)點(diǎn):可以向數(shù)據(jù)庫(kù)發(fā)送多條不同的SQL語(yǔ)句粉寞。
      • 缺點(diǎn):SQL語(yǔ)句沒有預(yù)編譯; 當(dāng)向數(shù)據(jù)庫(kù)發(fā)送多條語(yǔ)句相同尼荆,但僅參數(shù)不同的SQL語(yǔ)句時(shí),需重復(fù)寫上很多條SQL語(yǔ)句唧垦。例如:
  Insert into user(name,password) values(‘a(chǎn)a’,’111’);
  Insert into user(name,password) values(‘bb’,’222’);
  Insert into user(name,password) values(‘cc’,’333’);
  Insert into user(name,password) values(‘dd’,’444’);
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
String sql1 = "insert into user(name,password,email,birthday) 
 values('kkk','123','abc@sina.com','1978-08-08')";
String sql2 = "update user set password='123456' where id=3";
st = conn.createStatement();
st.addBatch(sql1);  //把SQL語(yǔ)句加入到批命令中
st.addBatch(sql2);  //把SQL語(yǔ)句加入到批命令中
st.executeBatch();
} finally {
 JdbcUtil.free(conn, st, rs);
}
  • 第二種方式:采用Statement.addBatch(sql)方式實(shí)現(xiàn)批處理:
    • 優(yōu)點(diǎn):發(fā)送的是預(yù)編譯后的SQL語(yǔ)句捅儒,執(zhí)行效率高
    • 缺點(diǎn):只能應(yīng)用在SQL語(yǔ)句相同,但參數(shù)不同的批處理中振亮。因此此種形式的批處理經(jīng)常用于在同一個(gè)表中批量插入數(shù)據(jù)巧还,或批量更新表的數(shù)據(jù)
        conn = JdbcUtil.*getConnection*();
        String sql = "insert into user(name,password,email,birthday) values(?,?,?,?)";
        st = conn.prepareStatement(sql);
        for(int i=0;i<50000;i++){
            st.setString(1, "aaa" + i);
            st.setString(2, "123" + i);
            st.setString(3, "aaa" + i + "@sina.com");
            st.setDate(4,**new** Date(1980, 10, 10));
            st.addBatch();
            if (i%1000==0){
                st.executeBatch();
                st.clearBatch();
            }
        }
        st.executeBatch();
  public class App {
 // 測(cè)試批處理操作
 @Test
  public void testBatch() throws Exception {
  
  // 模擬數(shù)據(jù)
  List<Admin> list = new ArrayList<Admin>();
 for (int i=1; i<21; i++) {
   Admin admin = new Admin();
   admin.setUserName("Jack" + i);
   admin.setPwd("888" + i);
   list.add(admin);
  }
  
  // 保存
  AdminDao dao = new AdminDao();
  dao.save(list);
 }
}
// 封裝所有的與數(shù)據(jù)庫(kù)的操作
  public class AdminDao {
 
 // 全局參數(shù)
private Connection con;
private PreparedStatement pstmt;
private ResultSet rs;

 // 批量保存管理員
 public void save(List<Admin> list) {
  // SQL
  String sql = "INSERT INTO admin(userName,pwd) values(?,?)";
  try {
   
   // 獲取連接
   con = JdbcUtil.getConnection();
   // 創(chuàng)建stmt 
   pstmt = con.prepareStatement(sql);     // 【預(yù)編譯SQL語(yǔ)句】
   
   for (int i=0; i<list.size(); i++) {
    Admin admin = list.get(i);
    // 設(shè)置參數(shù)
    pstmt.setString(1, admin.getUserName());
    pstmt.setString(2, admin.getPwd());
    
    // 添加批處理
    pstmt.addBatch();      // 【不需要傳入SQL】
    
    // 測(cè)試:每5條執(zhí)行一次批處理
    if (i % 5 == 0) {
     // 批量執(zhí)行 
     pstmt.executeBatch();
     // 清空批處理
     pstmt.clearBatch();
    }
   }
   
   // 批量執(zhí)行 
   pstmt.executeBatch();
   // 清空批處理
   pstmt.clearBatch();
   
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   JdbcUtil.closeAll(con, pstmt, rs);
  }
 }
}
插入數(shù)據(jù),獲取自增長(zhǎng)值
  • 需求 : 李俊杰 : 18, 張相 : 19
  • 如何設(shè)計(jì)數(shù)據(jù)庫(kù)坊秸?
 編號(hào)   員工姓名    年齡    部門
 01     李俊杰     18    開發(fā)部
 02     張三       19    開發(fā)部
  • 思考 : 如何減少數(shù)據(jù)冗余 -> 設(shè)置外鍵約束(部門與員工, 一對(duì)多的關(guān)系)
 編號(hào)    員工姓名    年齡    部門
 01       李俊杰    18      1
 02       張三      19      1

 部門編號(hào)     部門名稱    
     1         開發(fā)部            
  • 設(shè)計(jì)數(shù)據(jù)庫(kù):
 員工表 (外鍵表) 【員工表有一個(gè)外鍵字段麸祷,引用了部門表的主鍵】
 部門表(主鍵表)
  • 編碼總體思路 : 保存員工及其對(duì)應(yīng)的部門
    • 步驟:先保存部門; 再得到部門主鍵,再保存員工
    • 開發(fā)具體步驟:
      1. 設(shè)計(jì)javabean
      2. 設(shè)計(jì)dao
      3. 測(cè)試
部門
CREATE TABLE dept(
   deptId INT PRIMARY KEY AUTO_INCREMENT,
   deptName VARCHAR(20)
);
-- 員工
CREATE TABLE employee(
   empId INT PRIMARY KEY AUTO_INCREMENT,
   empName VARCHAR(20),
   dept_id  INT   --  外鍵字段   
);
-- 給員工表添加外鍵約束
ALTER TABLE employee ADD CONSTRAINT FK_employee_dept_deptId
    FOREIGN KEY(dept_id) REFERENCES dept(deptId) ;
public class EmpDao {
    private Connection con;
    private PreparedStatement pstmt;
    private ResultSet rs;
    // 保存員工妇斤,同時(shí)保存關(guān)聯(lián)的部門
    public void save(Employee emp){
        // 保存部門
        String sql_dept = "insert into dept(deptName) values(?)";
        // 保存員工
        String sql_emp = "INSERT INTO employee (empName,dept_id) VALUES (?,?)";
        // 部門id
        int deptId = 0;
        try {
            // 連接
            con = JdbcUtil.getConnection();
            /*****保存部門摇锋,獲取自增長(zhǎng)*******/
            // 【一、需要指定返回自增長(zhǎng)標(biāo)記】
            pstmt = con.prepareStatement(sql_dept,Statement.RETURN_GENERATED_KEYS);
            // 設(shè)置參數(shù)
            pstmt.setString(1, emp.getDept().getDeptName());
            // 執(zhí)行
            pstmt.executeUpdate();
            // 【二站超、獲取上面保存的部門子增長(zhǎng)的主鍵】
            rs =  pstmt.getGeneratedKeys();
            // 得到返回的自增長(zhǎng)字段
            if (rs.next()) {
                deptId = rs.getInt(1);
            }
            /*****保存員工*********/
            pstmt = con.prepareStatement(sql_emp);
            // 設(shè)置參數(shù)
            pstmt.setString(1, emp.getEmpName());
            pstmt.setInt(2, deptId);
            pstmt.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.closeAll(con, pstmt, rs);
        }
    }
}
事務(wù)
  • 基本概念 : 事務(wù)使指一組最小邏輯操作單元荸恕,里面有多個(gè)操作組成。 組成事務(wù)的每一部分必須要同時(shí)提交成功死相,如果有一個(gè)操作失敗融求,整個(gè)操作就回滾
  • 事務(wù)ACID特性 :
    • 原子性(Atomicity) : 原子性是指事務(wù)是一個(gè)不可分割的工作單位,事務(wù)中的操作要么都發(fā)生算撮,要么都不發(fā)生
    • 一致性(Consistency) : 事務(wù)必須使數(shù)據(jù)庫(kù)從一個(gè)一致性狀態(tài)變換到另外一個(gè)一致性狀態(tài)
    • 隔離性(Isolation) : 事務(wù)的隔離性是多個(gè)用戶并發(fā)訪問數(shù)據(jù)庫(kù)時(shí)生宛,數(shù)據(jù)庫(kù)為每一個(gè)用戶開啟的事務(wù),不能被其他事務(wù)的操作數(shù)據(jù)所干擾肮柜,多個(gè)并發(fā)事務(wù)之間要相互隔離
    • 持久性(Durability) : 持久性是指一個(gè)事務(wù)一旦被提交陷舅,它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變就是永久性的,接下來(lái)即使數(shù)據(jù)庫(kù)發(fā)生故障也不應(yīng)該對(duì)其有任何影響
  • 事務(wù)的特性:
    • 原子性审洞,是一個(gè)最小邏輯操作單元
    • 一致性莱睁,事務(wù)過程中,數(shù)據(jù)處于一致狀態(tài)
    • 持久性芒澜, 事務(wù)一旦提交成功仰剿,對(duì)數(shù)據(jù)的更改會(huì)反映到數(shù)據(jù)庫(kù)中
    • 隔離性, 事務(wù)與事務(wù)之間是隔離的
  • 案例 :
    • 需求 : 張三給李四轉(zhuǎn)賬
    • 設(shè)計(jì) : 賬戶表
    • 技術(shù) :
      • Connection :
        • void setAutoCommit(boolean autoCommit) : 設(shè)置事務(wù)是否自動(dòng)提交; 如果設(shè)置為false痴晦,表示手動(dòng)提交事務(wù)
      • void commit() : 手動(dòng)提交事務(wù)
      • void rollback() : 回滾 (出現(xiàn)異常時(shí)候南吮,所有已經(jīng)執(zhí)行成功的代碼需要回退到事務(wù)開始前的狀態(tài))
      • Savepoint setSavepoint(String name)
    • 代碼示例 :
-- 賬戶表
CREATE TABLE account(
   id INT PRIMARY KEY AUTO_INCREMENT,
   accountName VARCHAR(20),
   money DOUBLE
);
-- 轉(zhuǎn)賬
UPDATE account SET money=money-1000 WHERE accountName='張三';
UPDATE account SET money=money+1000 WHERE accountName='李四';
public class AccountDao {
    // 全局參數(shù)
    private Connection con;
    private PreparedStatement pstmt;
    // 1. 轉(zhuǎn)賬,沒有使用事務(wù)
    public void trans1() {
        String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='張三';";
        String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
        try {
            con = JdbcUtil.getConnection(); // 默認(rèn)開啟的隱士事務(wù)
            con.setAutoCommit(true);
            /*** 第一次執(zhí)行SQL ***/
            pstmt = con.prepareStatement(sql_zs);
            pstmt.executeUpdate();
            /*** 第二次執(zhí)行SQL ***/
            pstmt = con.prepareStatement(sql_ls);
            pstmt.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.closeAll(con, pstmt, null);
        }

    }
    // 2. 轉(zhuǎn)賬誊酌,使用事務(wù)
    public void trans2() {
        String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='張三';";
        String sql_ls = "UPDATE1 account SET money=money+1000 WHERE accountName='李四';";

        try {
            con = JdbcUtil.getConnection(); // 默認(rèn)開啟的隱士事務(wù)
            // 一部凑、設(shè)置事務(wù)為手動(dòng)提交
            con.setAutoCommit(false);

            /*** 第一次執(zhí)行SQL ***/
            pstmt = con.prepareStatement(sql_zs);
            pstmt.executeUpdate();

            /*** 第二次執(zhí)行SQL ***/
            pstmt = con.prepareStatement(sql_ls);
            pstmt.executeUpdate();

        } catch (Exception e) {
            try {
                // 二露乏、 出現(xiàn)異常,需要回滾事務(wù)
                con.rollback();
            } catch (SQLException e1) {
            }
            e.printStackTrace();
        } finally {
            try {
                // 三砚尽、所有的操作執(zhí)行成功, 提交事務(wù)
                con.commit();
                JdbcUtil.closeAll(con, pstmt, null);
            } catch (SQLException e) {
            }
        }
    }
    // 3. 轉(zhuǎn)賬施无,使用事務(wù), 回滾到指定的代碼段
    public void trans() {
        // 定義個(gè)標(biāo)記
        Savepoint sp = null;
        // 第一次轉(zhuǎn)賬
        String sql_zs1 = "UPDATE account SET money=money-1000 WHERE accountName='張三';";
        String sql_ls1 = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
        // 第二次轉(zhuǎn)賬
        String sql_zs2 = "UPDATE account SET money=money-500 WHERE accountName='張三';";
        String sql_ls2 = "UPDATE1 account SET money=money+500 WHERE accountName='李四';";

        try {
            con = JdbcUtil.getConnection(); // 默認(rèn)開啟的隱士事務(wù)
            con.setAutoCommit(false);       // 設(shè)置事務(wù)手動(dòng)提交

            /*** 第一次轉(zhuǎn)賬 ***/
            pstmt = con.prepareStatement(sql_zs1);
            pstmt.executeUpdate();
            pstmt = con.prepareStatement(sql_ls1);
            pstmt.executeUpdate();
            
            // 回滾到這個(gè)位置必孤?
            sp = con.setSavepoint(); 
            
            /*** 第二次轉(zhuǎn)賬 ***/
            pstmt = con.prepareStatement(sql_zs2);
            pstmt.executeUpdate();
            pstmt = con.prepareStatement(sql_ls2);
            pstmt.executeUpdate();

        } catch (Exception e) {
            try {
                // 回滾 (回滾到指定的代碼段)
                con.rollback(sp);
            } catch (SQLException e1) {
            }
            e.printStackTrace();
        } finally {
            try {
                // 提交
                con.commit();
            } catch (SQLException e) {
            }
            JdbcUtil.closeAll(con, pstmt, null);
        }
    }
}
Jdbc中大文本類型的處理
  • 在實(shí)際開發(fā)中,程序需要把大文本或二進(jìn)制數(shù)據(jù)保存到數(shù)據(jù)庫(kù)
    • Oracle中大文本數(shù)據(jù)類型 : Clob(長(zhǎng)文本類型, MySQL中不支持瑞躺,使用的是text), Blob(二進(jìn)制類型)
    • MySQL數(shù)據(jù)庫(kù): Text(長(zhǎng)文本類型), Blob(二進(jìn)制類型)
  • 基本概念:大數(shù)據(jù)也稱之為L(zhǎng)OB(Large Objects)敷搪,LOB又分為:clob和blob
    • clob用于存儲(chǔ)大文本。
    • blob用于存儲(chǔ)二進(jìn)制數(shù)據(jù)幢哨,例如圖像赡勘、聲音、二進(jìn)制文等捞镰。
  • 對(duì)MySQL而言只有blob闸与,而沒有clob,mysql存儲(chǔ)大文本采用的是Text岸售,Text和blob分別又分為:
    • TINYTEXT践樱、TEXT、MEDIUMTEXT和LONGTEXT
    • TINYBLOB凸丸、BLOB拷邢、MEDIUMBLOB和LONGBLOB
public class App_text {

    // 全局參數(shù)
    private Connection con;
    private Statement stmt;
    private PreparedStatement pstmt;
    private ResultSet rs;

    @Test
    // 1. 保存大文本數(shù)據(jù)類型   ( 寫longtext)
    public void testSaveText() {
        String sql = "insert into test(content) values(?)";
        try {
            // 連接
            con = JdbcUtil.getConnection();
            // pstmt 對(duì)象
            pstmt = con.prepareStatement(sql);
            // 設(shè)置參數(shù)
            // 先獲取文件路徑
            String path = App_text.class.getResource("tips.txt").getPath();
            FileReader reader = new FileReader(new File(path));
            pstmt.setCharacterStream(1, reader);
            
            // 執(zhí)行sql
            pstmt.executeUpdate();
            
            // 關(guān)閉
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.closeAll(con, pstmt, null);
        }
    }
    
    @Test
    // 2. 讀取大文本數(shù)據(jù)類型   ( 讀longtext)
    public void testGetAsText() {
        String sql = "select * from  test;";
        try {
            // 連接
            con = JdbcUtil.getConnection();
            // pstmt 對(duì)象
            pstmt = con.prepareStatement(sql);
            // 讀取
            rs = pstmt.executeQuery();
            if (rs.next()) {
                // 獲取長(zhǎng)文本數(shù)據(jù), 方式1:
                //Reader r = rs.getCharacterStream("content");
                
                // 獲取長(zhǎng)文本數(shù)據(jù), 方式2:
                System.out.print(rs.getString("content"));
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.closeAll(con, pstmt, null);
        }
    }
}
Jdbc綜合練習(xí)
  • 需求分析:登陸、注冊(cè)屎慢、注銷瞭稼;
    • 登陸成功 : 顯示所有的員工
    • 設(shè)計(jì)
      • 數(shù)據(jù)庫(kù)設(shè)計(jì):
        • Admin, 存放所有的登陸用戶
        • Employee, 存放所有的員工信息
      • 系統(tǒng)設(shè)計(jì)
    • 系統(tǒng)結(jié)構(gòu)
      • 分層: 基于mvc模式的分層
      • 項(xiàng)目用到的公用組件、類 (了解)
    • 編碼

BeanUtils組件簡(jiǎn)介

  • 程序中對(duì)javabean的操作很頻繁腻惠, 所以apache提供了一套開源的api环肘,方便對(duì)javabean的操作!即BeanUtils組件(作用是簡(jiǎn)化javabean的操作)
  • 用戶可以從www.apache.org下載BeanUtils組件集灌,然后再在項(xiàng)目中引入jar文件悔雹!

導(dǎo)入BenUtils組件

  • 引入commons-beanutils-1.8.3.jar核心包
  • 引入日志支持包: commons-logging-1.1.3.jar
  • 如果缺少日志jar文件,報(bào)錯(cuò):
    java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
    at org.apache.commons.beanutils.ConvertUtilsBean.<init>(ConvertUtilsBean.java:157)
    at org.apache.commons.beanutils.BeanUtilsBean.<init>(BeanUtilsBean.java:117)
    at org.apache.commons.beanutils.BeanUtilsBean$1.initialValue(BeanUtilsBean.java:68)

實(shí)例, 基本用法

  • 方法1: 對(duì)象屬性的拷貝
    BeanUtils.copyProperty(admin, "userName", "jack");
    BeanUtils.setProperty(admin, "age", 18);
  • 方法2: 對(duì)象的拷貝
    BeanUtils.copyProperties(newAdmin, admin);
  • 方法3: map數(shù)據(jù)拷貝到j(luò)avabean中 (注意:map中的key要與javabean的屬性名稱一致)
    BeanUtils.populate(adminMap, map);
public  void  test1()  throws  Exception {
 
 // a. 基本操作
 Admin admin = **new** Admin();
//  admin.setUserName("Jack");
//  admin.setPwd("999");
 
 // b. BeanUtils組件實(shí)現(xiàn)對(duì)象屬性的拷貝
 BeanUtils.copyProperty(admin, "userName", "jack");
 BeanUtils.setProperty(admin, "age", 18); // copy, set 都可以
 
 // 總結(jié)1: 對(duì)于基本數(shù)據(jù)類型绝页,會(huì)自動(dòng)進(jìn)行類型轉(zhuǎn)換!
 
 // c. 對(duì)象的拷貝
 Admin newAdmin = new  Admin();
 BeanUtils.copyProperties(newAdmin, admin);
 
 // d. map數(shù)據(jù)荠商,拷貝到對(duì)象中
 Admin adminMap = new  Admin();
 Map<String,Object> map = new  HashMap<String,Object>();
 map.put("userName", "Jerry");
 map.put("age", 29);
 // 注意:map中的key要與javabean的屬性名稱一致
 BeanUtils.populate(adminMap, map);
 
 // 測(cè)試
 System.out.println(adminMap.getUserName());
 System.out.println(adminMap.getAge());
}

實(shí)例, 日期類型的拷貝

  • 需要注冊(cè)日期類型轉(zhuǎn)換器,2種方式參見下面代碼
 //2. 自定義日期類型轉(zhuǎn)換器
    @Test
    public void test2() throws Exception {
        // 模擬表單數(shù)據(jù)
        String name = "jack";
        String age = "20";
        String birth = "   ";

        // 對(duì)象
        Admin admin = new Admin();
        
        // 注冊(cè)日期類型轉(zhuǎn)換器:1续誉, 自定義的方式
        ConvertUtils.register(new Converter() {
            // 轉(zhuǎn)換的內(nèi)部實(shí)現(xiàn)方法莱没,需要重寫
            @Override
            public Object convert(Class type, Object value) {
                
                // 判斷
                if (type != Date.class) {
                    return null;
                }
                if (value == null || "".equals(value.toString().trim())) {
                    return null;
                }
                
                
                try {
                    // 字符串轉(zhuǎn)換為日期
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                    return sdf.parse(value.toString());
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }
            }
        },Date.class);
        
        // 把表單提交的數(shù)據(jù),封裝到對(duì)象中
        BeanUtils.copyProperty(admin, "userName", name);
        BeanUtils.copyProperty(admin, "age", age);
        BeanUtils.copyProperty(admin, "birth", birth);
        
        //------ 測(cè)試------
        System.out.println(admin);
    }
    
    //2. 使用提供的日期類型轉(zhuǎn)換器工具類
    @Test
    public void test3() throws Exception {
        // 模擬表單數(shù)據(jù)
        String name = "userName";
        String age = "20";
        String birth = null;
        
        // 對(duì)象
        Admin admin = new Admin();
        
        // 注冊(cè)日期類型轉(zhuǎn)換器:2: 使用組件提供的轉(zhuǎn)換器工具類
        ConvertUtils.register(new DateLocaleConverter(), Date.class);
                
        // 把表單提交的數(shù)據(jù)酷鸦,封裝到對(duì)象中
        BeanUtils.copyProperty(admin, "userName", name);
        BeanUtils.copyProperty(admin, "age", age);
        BeanUtils.copyProperty(admin, "birth", birth);
        
        //------ 測(cè)試------
        System.out.println(admin);
    }
}

應(yīng)用

public  class  WebUtils {

 @Deprecated
 public  static <T> T copyToBean_old(HttpServletRequest request, Class<T> clazz) {
  try {
   // 創(chuàng)建對(duì)象
   T t = clazz.newInstance();
   
   // 獲取所有的表單元素的名稱
   Enumeration<String> enums = request.getParameterNames();
   // 遍歷
   while (enums.hasMoreElements()) {
    // 獲取表單元素的名稱:<input type="password" name="pwd"/>
    String name = enums.nextElement();  // pwd
    // 獲取名稱對(duì)應(yīng)的值
    String value = request.getParameter(name);
    // 把指定屬性名稱對(duì)應(yīng)的值進(jìn)行拷貝
    BeanUtils.copyProperty(t, name, value);
   }
   
  return t;
  } catch (Exception e) {
   throw  new  RuntimeException(e);
  }
 }
 
 /**
  * 處理請(qǐng)求數(shù)據(jù)的封裝
  */
  public  static  <T> T copyToBean(HttpServletRequest request, Class<T> clazz) {
  try {
   // (注冊(cè)日期類型轉(zhuǎn)換器)
   // 創(chuàng)建對(duì)象
   T t = clazz.newInstance();
   BeanUtils.populate(t, request.getParameterMap()); // 使用map
   return  t;
  } catch (Exception e) {
   throw  new  RuntimeException(e);
  }
 }
}

元數(shù)據(jù)

  • 在jdbc中獲取數(shù)據(jù)庫(kù)的定義饰躲,例如:數(shù)據(jù)庫(kù)牙咏、表、列的定義信息, 就用到元數(shù)據(jù)
  • 在jdbc中可以使用: 數(shù)據(jù)庫(kù)元數(shù)據(jù)嘹裂、參數(shù)元數(shù)據(jù)妄壶、結(jié)果集元數(shù)據(jù)(元數(shù)據(jù)定義相關(guān)api, ..MetaData)
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;

import org.junit.Test;

import anderson.sh.utils.JdbcUtil; // 自己的工具庫(kù)

public class App {

    //1. 數(shù)據(jù)庫(kù)元數(shù)據(jù)
    @Test
    public void testDB() throws Exception {
        // 獲取連接
        Connection conn = JdbcUtil.getConnection();
        // 獲取數(shù)據(jù)庫(kù)元數(shù)據(jù)
        DatabaseMetaData metaData = conn.getMetaData(); // alt + shift + L  快速獲取方法返回值
        
        System.out.println(metaData.getUserName());
        System.out.println(metaData.getURL());
        System.out.println(metaData.getDatabaseProductName());
    }
    
    //2. 參數(shù)元數(shù)據(jù)
    @Test
    public void testParams() throws Exception {
        // 獲取連接
        Connection conn = JdbcUtil.getConnection();
        // SQL
        String sql = "select * from dept where deptid=? and deptName=?";
        // Object[] values = {"tom","888"};
        
        PreparedStatement pstmt = conn.prepareStatement(sql);
        // 參數(shù)元數(shù)據(jù)
        ParameterMetaData p_metaDate = pstmt.getParameterMetaData();
        // 獲取參數(shù)的個(gè)數(shù)
        int count = p_metaDate.getParameterCount();
                
        // 測(cè)試
        System.out.println(count);
    }
    
    // 3. 結(jié)果集元數(shù)據(jù)
    @Test
    public void testRs() throws Exception {
        String sql = "select * from dept ";
        
        // 獲取連接
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement pstmt = conn.prepareStatement(sql);
        ResultSet rs = pstmt.executeQuery();
        // 得到結(jié)果集元數(shù)據(jù)(目標(biāo):通過結(jié)果集元數(shù)據(jù)寄狼,得到列的名稱)
        ResultSetMetaData rs_metaData = rs.getMetaData();
        
        // 迭代每一行結(jié)果
        while (rs.next()) {
            // 1. 獲取列的個(gè)數(shù)
            int count = rs_metaData.getColumnCount();
            // 2. 遍歷丁寄,獲取每一列的列的名稱
            for (int i=0; i<count; i++) {
                // 得到列的名稱
                String columnName = rs_metaData.getColumnName(i + 1);
                // 獲取每一行的每一列的值
                Object columnValue = rs.getObject(columnName);
                // 測(cè)試
                System.out.print(columnName + "=" + columnValue + ",");
            }
            System.out.println();
        }   
    }   
}

Dao操作的抽取(BaseDAO)

  • Dao操作通用的步驟:
    1. 寫SQL語(yǔ)句
    2. 獲取連接
    3. 創(chuàng)建stmt
    4. 執(zhí)行sql
    • 更新
    • 查詢
    1. 關(guān)閉/異常
  • 通用的DAO(BaseDao)
    1. 更新 : public void update(String sql, Object[] paramValues);
    2. 查詢 :
    • 傳入的什么類型的對(duì)象,就封裝為什么類型
    • 要求: 列的名稱泊愧,要與指定類型的對(duì)象的屬性名稱一樣)
      Public List<T> query (String sql , Object[] paramValues , Class<T> clazz);
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.apache.commons.beanutils.BeanUtils;
import anderson.sh.utils.JdbcUtil;

/**
 * 通用的dao伊磺,自己寫的所有的dao都繼承此類;
 * 此類定義了2個(gè)通用的方法:
 *  1. 更新
 *      2. 查詢
 */
public class BaseDao {
    
    // 初始化參數(shù)
    private Connection con;
    private PreparedStatement pstmt;
    private ResultSet rs;

    /**
     * 更新的通用方法
     * @param sql   更新的sql語(yǔ)句(update/insert/delete)
     * @param paramsValue  sql語(yǔ)句中占位符對(duì)應(yīng)的值(如果沒有占位符,傳入null)
     */
    public void update(String sql,Object[] paramsValue){
        
        try {
            // 獲取連接
            con = JdbcUtil.getConnection();
            // 創(chuàng)建執(zhí)行命令的stmt對(duì)象
            pstmt = con.prepareStatement(sql);
            // 參數(shù)元數(shù)據(jù): 得到占位符參數(shù)的個(gè)數(shù)
            int count = pstmt.getParameterMetaData().getParameterCount();
            
            // 設(shè)置占位符參數(shù)的值
            if (paramsValue != null && paramsValue.length > 0) {
                // 循環(huán)給參數(shù)賦值
                for(int i=0;i<count;i++) {
                    pstmt.setObject(i+1, paramsValue[i]);
                }
            }
            // 執(zhí)行更新
            pstmt.executeUpdate();
            
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            JdbcUtil.closeAll(con, pstmt, null);
        }
    }
    
    /**
     * 查詢的通用方法
     * @param sql
     * @param paramsValue
     */
    public <T> List<T> query(String sql, Object[] paramsValue,Class<T> clazz){
        
        try {
            // 返回的集合
            List<T> list = new ArrayList<T>();
            // 對(duì)象
            T t = null;
            
            // 1. 獲取連接
            con = JdbcUtil.getConnection();
            // 2. 創(chuàng)建stmt對(duì)象
            pstmt = con.prepareStatement(sql);
            // 3. 獲取占位符參數(shù)的個(gè)數(shù)删咱, 并設(shè)置每個(gè)參數(shù)的值
            //int count = pstmt.getParameterMetaData().getParameterCount();
            if (paramsValue != null && paramsValue.length > 0) {
                for (int i=0; i<paramsValue.length; i++) {
                    pstmt.setObject(i+1, paramsValue[i]);
                }
            }
            // 4. 執(zhí)行查詢
            rs = pstmt.executeQuery();
            // 5. 獲取結(jié)果集元數(shù)據(jù)
            ResultSetMetaData rsmd = rs.getMetaData();
            // ---> 獲取列的個(gè)數(shù)
            int columnCount = rsmd.getColumnCount();
            
            // 6. 遍歷rs
            while (rs.next()) {
                // 要封裝的對(duì)象
                t = clazz.newInstance();
                
                // 7. 遍歷每一行的每一列, 封裝數(shù)據(jù)
                for (int i=0; i<columnCount; i++) {
                    // 獲取每一列的列名稱
                    String columnName = rsmd.getColumnName(i + 1);
                    // 獲取每一列的列名稱, 對(duì)應(yīng)的值
                    Object value = rs.getObject(columnName);
                    // 封裝: 設(shè)置到t對(duì)象的屬性中  【BeanUtils組件】
                    BeanUtils.copyProperty(t, columnName, value);               
                }
                
                // 把封裝完畢的對(duì)象屑埋,添加到list集合中
                list.add(t);
            }
            
            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            JdbcUtil.closeAll(con, pstmt, rs);
        }
    }
}

import java.util.List;

public class AdminDao extends BaseDao {

    // 刪除
    public void delete(int id) {
        String sql = "delete from admin where id=?";
        Object[] paramsValue = {id};
        super.update(sql, paramsValue);
    }

    // 插入
    public void save(Admin admin) {
        String sql = "insert into admin (userName,pwd) values (?,?)";
        Object[] paramsValue = {admin.getUserName(),admin.getPwd()};
        super.update(sql, paramsValue);
    }
    
    // 查詢?nèi)?    public List<Admin> getAll(){
        String sql = "select * from admin";
        List<Admin> list = super.query(sql, null, Admin.class);
        return list;
    }
    
    // 根據(jù)條件查詢(主鍵)
    public Admin findById(int id){
        String sql = "select * from admin where id=?";
        List<Admin> list = super.query(sql, new Object[]{id}, Admin.class);
        return  (list!=null&&list.size()>0) ? list.get(0) : null;
    }   
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市痰滋,隨后出現(xiàn)的幾起案子摘能,更是在濱河造成了極大的恐慌,老刑警劉巖敲街,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件团搞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡聪富,警方通過查閱死者的電腦和手機(jī)莺丑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)墩蔓,“玉大人梢莽,你說我怎么就攤上這事〖榕” “怎么了昏名?”我有些...
    開封第一講書人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)阵面。 經(jīng)常有香客問我轻局,道長(zhǎng),這世上最難降的妖魔是什么样刷? 我笑而不...
    開封第一講書人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任仑扑,我火速辦了婚禮,結(jié)果婚禮上置鼻,老公的妹妹穿的比我還像新娘镇饮。我一直安慰自己,他們只是感情好箕母,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開白布储藐。 她就那樣靜靜地躺著俱济,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钙勃。 梳的紋絲不亂的頭發(fā)上蛛碌,一...
    開封第一講書人閱讀 48,954評(píng)論 1 283
  • 那天,我揣著相機(jī)與錄音辖源,去河邊找鬼蔚携。 笑死,一個(gè)胖子當(dāng)著我的面吹牛克饶,可吹牛的內(nèi)容都是我干的浮梢。 我是一名探鬼主播,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼彤路,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了芥映?” 一聲冷哼從身側(cè)響起洲尊,我...
    開封第一講書人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎奈偏,沒想到半個(gè)月后坞嘀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惊来,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年丽涩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裁蚁。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡矢渊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出枉证,到底是詐尸還是另有隱情矮男,我是刑警寧澤,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布室谚,位于F島的核電站毡鉴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏秒赤。R本人自食惡果不足惜猪瞬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望入篮。 院中可真熱鬧陈瘦,春花似錦、人聲如沸崎弃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至线婚,卻和暖如春遏弱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背塞弊。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工漱逸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人游沿。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓饰抒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親诀黍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子袋坑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • JDBC簡(jiǎn)介 SUN公司為了簡(jiǎn)化、統(tǒng)一對(duì)數(shù)據(jù)庫(kù)的操作眯勾,定義了一套Java操作數(shù)據(jù)庫(kù)的規(guī)范枣宫,稱之為JDBC。JDBC...
    奮斗的老王閱讀 1,504評(píng)論 0 51
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法吃环,類相關(guān)的語(yǔ)法也颤,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法郁轻,異常的語(yǔ)法翅娶,線程的語(yǔ)...
    子非魚_t_閱讀 31,581評(píng)論 18 399
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 3,790評(píng)論 0 11
  • 一、事務(wù) 事務(wù)就是一個(gè)事情好唯,組成這個(gè)事情可能有多個(gè)單元竭沫,要求這些單元,要么全都成功渠啊,要么全都不成功输吏。在開發(fā)中,有事...
    野狗子嗷嗷嗷閱讀 2,797評(píng)論 0 6
  • 你有經(jīng)過美麗的櫥窗看到漂亮的衣服替蛉,但就是穿著難看的尷尬經(jīng)歷嗎贯溅? 你有著暴飲暴食然后全身不舒服一連難受好幾天的悲傷往...
    大熊律師閱讀 459評(píng)論 1 1