Java之路(十一):DAO優化

DAO的重構

1.PreparedStatement(預編譯處理語句)

Statement接口只能實現靜態SQL語句,我們使用PreparedStatement。

PreparedStatement是Statement子接口,表示預編譯語句對象,通過佔位符?來拼接SQL

創建預編譯語句對象

    @Test
    public void testSaveByPreparedStatement() throws Exception{
        String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";//SQL模板
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        //設置佔位符參數值
        ps.setString(1,"Dans");
        ps.setString(2,"9999999");
        ps.setString(3,"F");
        ps.setInt(4,25);
        ps.executeUpdate();//注意,沒有參數
        JdbcUtil.close(conn,ps,null);
    }

具體實現DAO的代碼:

public class StudentDAOImpl implements IStudentDAO {

    @Override
    public void save(Student stu) {
        String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,stu.getName());
            ps.setString(2,stu.getId());
            ps.setString(3,stu.getSex());
            ps.setInt(4,stu.getAge());
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,null);
        }
    }

    @Override
    public void delete(String id) {
        String sql = "DELETE FROM student WHERE Student_id = ?";
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,id);
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,null);
        }
    }

    @Override
    public void update(String id, Student newStu) {
        String sql = "UPDATE student SET Student_name = ?,sex = ?, age = ? WHERE Student_id = ?";
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,newStu.getName());
            ps.setString(2,newStu.getSex());
            ps.setInt(3,newStu.getAge());
            ps.setString(4,id);
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,null);
        }
    }

    @Override
    public Student get(String id) {
        String sql = "SELECT * FROM student WHERE Student_id = ?";
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,id);
            rs = ps.executeQuery();
            if (rs.next()) {
                Student stu = new Student();

                stu.setName(rs.getString("Student_name"));
                stu.setSex(rs.getString("sex"));
                stu.setAge(rs.getInt("age"));
                stu.setId(id);
                return stu;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,rs);
        }
        return null;
    }

    @Override
    public List<Student> listAll() {
        List<Student> list = new ArrayList<>();
        String sql = "SELECT * FROM student";
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();

            while (rs.next()) {
                Student stu = new Student();
                stu.setName(rs.getString("Student_name"));
                stu.setSex(rs.getString("sex"));
                stu.setAge(rs.getInt("age"));
                stu.setId(rs.getString("Student_id"));
                list.add(stu);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,rs);
        }
        return list;
    }
}

PreparedStatement原理

用預編譯處理語句在預編譯池中已經存在了相同的代碼,那麼1-3步可以省略不寫,因此性能會更高。

但是MySQL不支持PreparedStatement,Oracle支持PreparedStatement.

防止SQL注入問題

如果我們使用Statement方式來進行DQL操作,

String sql = "SELECT * FROM student WHERE Student_name = 'admin' AND password = '1234'";

當我把填入的admin換成:' OR 1=1 OR '

即如下:

String sql = "SELECT * FROM student WHERE Student_name = '' OR 1=1 OR '' AND password = '1234'";

只要數據庫中存在數據,那麼查詢就是成功的,這便是SQL注入。

而使用PrepareStatement便可以解決SQL注入問題。

String sql = "SELECT * FROM student WHERE Student_name = ? AND password = ?";

2.事務

我們用轉賬的例子來進行模擬。

       試想,我們在轉賬的時候,從我的賬戶轉出1000元,然後服務器收到消息,再將收款方賬戶增加1000元,如果這兩件事是獨立的,那麼若在我轉出的同時,服務器停止工作(斷電或者被黑了),那麼收款方將無法收到轉賬消息,但是我的賬戶已經轉出了1000元,那麼我將白白損失1000元。

    @Test
    public void test1() throws Exception {
        Connection conn = JdbcUtil.getConnection();
        //--------------檢查Bobbui的賬戶餘額-------------------
        String sql = "SELECT * FROM account WHERE name = ? AND balance >= ?";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1, "Bobbui");
        ps.setInt(2, 1000);
        ResultSet rs = ps.executeQuery();
        if (!rs.next()) {
            throw new RuntimeException("餘額不足!");
        }

        //---------------減少Bobbui賬戶1000元------------------
        sql = "UPDATE account SET balance = balance - ? WHERE name = ?";
        ps = conn.prepareStatement(sql);
        ps.setInt(1, 1000);
        ps.setString(2, "Bobbui");
        ps.executeUpdate();
        //使用異常模擬停電
        int a = 1 / 0;

        //--------------增加DANS1000元-------------------------
        sql = "UPDATE account SET balance = balance + ? WHERE name = ?";
        ps = conn.prepareStatement(sql);
        ps.setInt(1, 1000);
        ps.setString(2, "DANS");
        ps.executeUpdate();
        JdbcUtil.close(conn, ps, rs);
    }

因此我們引出事務(Transaction),事務的相關操作:

    @Test
    public void test2() {
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn.setAutoCommit(false);
            //--------------檢查Bobbui的賬戶餘額-------------------
            String sql = "SELECT * FROM account WHERE name = ? AND balance >= ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, "Bobbui");
            ps.setInt(2, 1000);
            rs = ps.executeQuery();
            if (!rs.next()) {
                throw new RuntimeException("餘額不足!");
            }

            //---------------減少Bobbui賬戶1000元------------------
            sql = "UPDATE account SET balance = balance - ? WHERE name = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, 1000);
            ps.setString(2, "Bobbui");
            ps.executeUpdate();
            //使用異常模擬停電
            //int a = 1 / 0;

            //--------------增加DANS1000元-------------------------
            sql = "UPDATE account SET balance = balance + ? WHERE name = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, 1000);
            ps.setString(2, "DANS");
            ps.executeUpdate();

            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (Exception v) {
                v.printStackTrace();
            }
        } finally {
            JdbcUtil.close(conn, ps, rs);
        }

    }

這樣就解決了上述問題。

3.批處理操作

    @Test
    public void testSaveByPreparedStatement_batch() throws Exception{
        String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";//SQL模板
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        for (int i = 0; i < 3000; i++) {
            ps.setString(1,"Dans");
            ps.setString(2,"9999999");
            ps.setString(3,"F");
            ps.setInt(4,25);
            
            ps.addBatch();//添加進批處理中
            if (i % 200 == 0) {
                ps.executeBatch();//執行批量操作
                ps.clearBatch();//清除緩存
                ps.clearParameters();//清除參數
            }
        }
    }

但是,MySQL服務器既不支持PrepareStatement的性能優化,也不支持JDBC中的批量操作,但是在新的JDBC驅動中,我們可以通過設置參數來進行優化:在URL後面綴上?rewriteBatchedStatements=true(在5.1.13版本後都支持)

url=jdbc:mysql:///studentinfo?rewriteBatchedStatements=true

4.BOLB和TEXT類型

    @Test
    public void test1() throws Exception {
        String sql = "INSERT INTO image (img) VALUES (?)";
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setBlob(1,new FileInputStream("D:/1.jpg"));
        ps.executeUpdate();
        JdbcUtil.close(conn,ps,null);
    }

    @Test
    public void test2() throws Exception {
        String sql = "SELECT * FROM image WHERE id = ?";
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setInt(1,1);
        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            Blob blob = rs.getBlob("img");
            InputStream in = blob.getBinaryStream();
            Files.copy(in, Paths.get("D:/1234.jpg"));
        }
        JdbcUtil.close(conn,ps,null);
    }

5.獲取自動生成的主鍵

    @Test
    public void testStatement() throws Exception {
        String sql = "INSERT INTO pk (name,age) VALUES (?,?)";
        Connection conn = JdbcUtil.getConnection();
        //設置主鍵可獲取
        PreparedStatement ps = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
        ps.setString(1,"ZZZ");
        ps.setInt(2,20);
        ps.executeUpdate();
        //獲取主鍵結果集
        ResultSet rs = ps.getGeneratedKeys();
        if (rs.next()) {
            Integer id = rs.getInt(1);
            System.out.println(id);
        }
        JdbcUtil.close(conn,ps,rs);
    }

很簡單,沒什麼好說的。

6.連接池

創建DataSource對象

使用DataSource需要導入commos-dbcp.jar和commons-pool.jar兩個jar包

/**
 * 使用DBCP連接池
 */
public class DBCPTest {

    /**
     * 創建一個連接池對象
     * @return DataSource
     */
    public DataSource getDataSource() {
        //創建連接對象
        BasicDataSource ds = new BasicDataSource();
        //設置連接數據庫的四要素
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql:///studentinfo");
        ds.setUsername("root");
        ds.setPassword("password");
        ds.setMaxWaitMillis(1000);//設置最大等待時間
        ds.setInitialSize(3);//設置初始連接數
        ds.setMaxIdle(3);//最大空閒連接數
        ds.setMinIdle(1);//最小空閒連接數
        ds.setMaxTotal(10);//最大連接數
        return ds;
    }

    @Test
    public void test1() throws Exception {
        DataSource ds = getDataSource();
        Connection conn = ds.getConnection();
        String sql = "SELECT * FROM student";
        PreparedStatement ps = conn.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
            System.out.println(rs.getString("Student_id"));
        }
    }
}

使用properties文件解耦DBCP

public class DBCPUtil {
    
    private static DataSource ds = null;

    static {
        try {
            Properties p = new Properties();
            p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("dbcp.properties"));
            ds = BasicDataSourceFactory.createDataSource(p);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() {
        try {
            //從連接池中獲取Connection對象
            return ds.getConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //釋放資源
    public static void close(Connection conn, Statement st, ResultSet rs) {
        try {
            if (st != null) {
                st.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

 使用BasicDataSourceFactory很快獲得properties文件中的值

    @Test
    public void test1() throws Exception {
        //DataSource ds = getDataSource();
        //Connection conn = ds.getConnection();
        Connection conn = DBCPUtil.getConnection();
        String sql = "SELECT * FROM student";
        PreparedStatement ps = conn.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
            System.out.println(rs.getString("Student_id"));
        }
    }

7.Druid連接池

需要從阿里巴巴下載Druid的jar包

各數據庫性能對比:

使用起來那就很簡單了

    static {
        try {
            Properties p = new Properties();
            p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("druid.properties"));
            //基於DBCP
            //ds = BasicDataSourceFactory.createDataSource(p);
            //基於Druid
            ds = DruidDataSourceFactory.createDataSource(p);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

8.JDBC Template

我們觀察DML的增刪改操作,發現他們工作的流程有異曲同工之處:

     @Override
    public void save(Student stu) {
        String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,stu.getName());
            ps.setString(2,stu.getId());
            ps.setString(3,stu.getSex());
            ps.setInt(4,stu.getAge());
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,null);
        }
    }

    @Override
    public void delete(String id) {
        String sql = "DELETE FROM student WHERE Student_id = ?";
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,id);
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,null);
        }
    }

他們的工作原理都是一樣的,我們考慮將他們相同的部分抽取出來,重構出一個Template類,然後用循環來設置佔位符參數

    /**
     * DML操作(增刪改)模板
     *
     * @param sql    DML造作的SQL模板
     * @param params SQL模板中?對應參數的值
     * @return 受影響的行數
     */
    public static int update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            //設置佔位符參數
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            return ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, ps, null);
        }
        return 0;
    }

實現類修改爲:

    @Override
    public void save(Student stu) {
        String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";
        Object[] params = new Object[]{stu.getStudent_name(), stu.getStudent_id(), stu.getSex(), stu.getAge()};
        JdbcTemplate.update(sql, params);
    }

    @Override
    public void delete(String id) {
        JdbcTemplate.update("DELETE FROM student WHERE Student_id = ?", id);
    }

    @Override
    public void update(String id, Student newStu) {
        String sql = "UPDATE student SET Student_name = ?,sex = ?, age = ? WHERE Student_id = ?";
        Object[] params = new Object[]{newStu.getStudent_name(), newStu.getSex(), newStu.getAge(), id};
        JdbcTemplate.update(sql, params);
    }

對於DQL操作來說,也是有很多相同的地方,將其抽取出來,形成DQL模板

    @Override
    public Student get(String id) {
        List<Student> list = new ArrayList<>();
        String sql = "SELECT * FROM student WHERE Student_id = ?";
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1,id);
            rs = ps.executeQuery();
            while (rs.next()) {
                Student stu = new Student();
                stu.setName(rs.getString("Student_name"));
                stu.setSex(rs.getString("sex"));
                stu.setAge(rs.getInt("age"));
                stu.setId(id);
                list.add(stu);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn,ps,rs);
        }
        return list.size() == 1 ? list.get(0) : null;
    }
   public static List<Student> query(String sql, Object... params) {
        List<Student> list = new ArrayList<>();
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            rs = ps.executeQuery();
            while (rs.next()) {
                //處理結果集
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, ps, rs);
        }
        return list;
    }

實現類修改爲:

    @Override
    public Student get(String id) {
        String sql = "SELECT * FROM student WHERE Student_id = ?";
        List<Student> list = JdbcTemplate.query(sql,id);
        return list.size() == 1 ? list.get(0) : null;
    }

9.Template優化

       上述的Template中,我們只能將數據庫的結果集每一行封裝成固定的Student對象,在開發中我們需要操作N張表,便有N個對象,上述方法顯然是不合理的。我們將模板類命名爲JDBCTemplate,那麼該類應該適用於任何情況。

       因此,我們考慮將處理結果集的行爲交給每個對象的DAO實現類來做。

       爲了避免不同的DAO實現類心意的處理結果集的方法名字不同,我們用一個接口來指定規範:

//結果集處理器,規範處理結果集的方法名稱
public interface IResultSetHandler {

    //處理結果集
    List handle(ResultSet rs) throws Exception;
}

但是我們考慮到,不一定每一個結果集都需要用List來存,所以這裏我們用泛型:

public interface IResultSetHandler<T> {

    T handle(ResultSet rs) throws Exception;
}

把結果集每一行數據封裝成Student對象的實現:

class StudentResultSetHandle implements IResultSetHandle<List<Student>> {

    @Override
    public List<Student> handle(ResultSet rs) throws Exception {
        List list = new ArrayList();
        while (rs.next()) {
            Student stu = new Student();
            stu.setName(rs.getString("Student_name"));
            stu.setSex(rs.getString("sex"));
            stu.setAge(rs.getInt("age"));
            stu.setId(rs.getString("Student_id"));
            list.add(stu);
        }
        return list;
    }
}

但是我們發現,若存在N個DAO實現類,那麼便會有N個實現Handler類,而這些類的結構完全相同:

  1. 創建一個對象
  2. 取出結果集中當前光標所在行的某一列數據
  3. 調用該對象的setter方法,把某一列的數據設置進去(可以使用Java的內省機制)

因此可以將其抽取出來形成結果集處理器,但是要注意:

  1. 規定表中的列名必須和對象中的屬性名相同
  2. 規定表中的列名的類型必須和Java中的類型要匹配

結果集處理器BeanHandler代碼如下:

/**
 * new BeanHandle(Student.class)
 * new BeanHandle(Teacher.class)
 *
 * @param <T>
 */
//表示把結果集中的一行數據封裝成一個對象,專門針對結果集中只有一行數據的情況
public class BeanHandler<T> implements IResultSetHandler<T> {

    private Class<T> classType;//把結果集中的一行數據封裝成什麼類型的對象

    public BeanHandler(Class<T> classType) {
        this.classType = classType;
    }

    /**
     * 規定表中的列名稱必須和對象中的屬性名相同;
     * 規定表中的列名的類型必須和Java中的類型要匹配
     * --------------##使用內省機制##--------------
     *
     * @param rs
     * @return
     * @throws Exception
     */
    @Override
    public T handle(ResultSet rs) throws Exception {
        //1.創建對應類的一個對象
        T obj = classType.newInstance();
        //2.取出結果集中當前光寶所在行的某一列的數據
        BeanInfo beanInfo = Introspector.getBeanInfo(classType, Object.class);
        PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
        if (rs.next()) {
            for (PropertyDescriptor pd : pds) {
                String columnName = pd.getName();//獲取對象的屬性名,屬性和列名相同
                Object val = rs.getObject(columnName);
                //System.out.println(columnName + "," + val);
                //3.調用該對象的setter方法,把某一列的數據設置進去
               pd.getWriteMethod().invoke(obj,val);
            }
        }
        return obj;
    }
}

BeanListHandler同理如下:

public class BeanListHandler<T> implements IResultSetHandler<List<T>> {

    private Class<T> classType;//把結果集中的一行數據封裝成什麼類型的對象

    public BeanListHandler(Class<T> classType) {
        this.classType = classType;
    }

    @Override
    public List<T> handle(ResultSet rs) throws Exception {
        List<T> list = new ArrayList<>();
        while (rs.next()) {
            T obj = classType.newInstance();
            list.add(obj);
            BeanInfo beanInfo = Introspector.getBeanInfo(classType, Object.class);
            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor pd : pds) {
                String columnName = pd.getName();//獲取對象的屬性名,屬性和列名相同
                Object val = rs.getObject(columnName);
                pd.getWriteMethod().invoke(obj,val);
            }
        }
        return list;
    }
}

此時JdbcTemplate模板類如下:

//JDBC操作模板類
public class JdbcTemplate {

    /**
     * DQL(查詢)操作模板
     * 如果查詢多個學生,返回一個List<Student>
     * 單個學生也可以返回一個List<Student>
     *
     * @param sql    DQL操作的SQL模板
     * @param params SQL中?對應的參數值
     * @return List集合
     */
    public static <T>T query(String sql, IResultSetHandler<T> rsh, Object... params) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            rs = ps.executeQuery();
            return rsh.handle(rs);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, ps, rs);
        }
        throw new RuntimeException("查詢錯誤");
    }

    /**
     * DML操作(增刪改)模板
     *
     * @param sql    DML造作的SQL模板
     * @param params SQL模板中?對應參數的值
     * @return 受影響的行數
     */
    public static int update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            ps = conn.prepareStatement(sql);
            //設置佔位符參數
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            return ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, ps, null);
        }
        return 0;
    }
}

實現類如下:

public class StudentDAOImpl implements IStudentDAO {

    @Override
    public void save(Student stu) {
        String sql = "INSERT INTO student (Student_name,Student_id,sex,age) VALUES (?,?,?,?)";
        Object[] params = new Object[]{stu.getStudent_name(), stu.getStudent_id(), stu.getSex(), stu.getAge()};
        JdbcTemplate.update(sql, params);
    }

    @Override
    public void delete(String id) {
        JdbcTemplate.update("DELETE FROM student WHERE Student_id = ?", id);
    }

    @Override
    public void update(String id, Student newStu) {
        String sql = "UPDATE student SET Student_name = ?,sex = ?, age = ? WHERE Student_id = ?";
        Object[] params = new Object[]{newStu.getStudent_name(), newStu.getSex(), newStu.getAge(), id};
        JdbcTemplate.update(sql, params);
    }

    @Override
    public Student get(String id) {
        String sql = "SELECT * FROM student WHERE Student_id = ?";
        return JdbcTemplate.query(sql,new BeanHandler<>(Student.class), id);
    }

    @Override
    public List<Student> listAll() {
        return JdbcTemplate.query("SELECT * FROM student",new BeanListHandler<>(Student.class));

    }
}

顯得非常簡潔,從原來的將近兩百行優化到了四十行。

這些就是數據庫操作的一些模板,若給他人使用,我們可以將其打成jar包,可以應付絕大部分的數據庫操作。但是還是有一些小問題,我的處理結果集不一定是某個類的對象,可以是某一個值,比如我使用了數據庫的聚合函數。那麼我們可以用一個匿名內部類來重寫IResultSetHandler接口:

    @Test
    public void testGetCount() {
        String sql = "SELECT COUNT(Student_id) FROM student";
        int totalCount = JdbcTemplate.query(sql, new IResultSetHandler<Integer>() {
            @Override
            public Integer handle(ResultSet rs) throws Exception {
                if (rs.next()) {
                    return rs.getInt(1);
                }
                return 0;
            }
        });
        System.out.println(totalCount);
    }

當然可以使用lambda表達式:

    @Test
    public void testGetCount() {
        String sql = "SELECT COUNT(Student_id) FROM student";
        int totalCount = JdbcTemplate.query(sql, rs -> {
            if (rs.next()) {
                return rs.getInt(1);
            }
            return 0;
        });
        System.out.println(totalCount);
    }

簡潔明瞭。

       整個DAO就是一個封裝的思想,目的就是實現代碼的高內聚和低耦合,便於後期維護。我們抽象出來的模板類和Handler類,就是把數據庫操作中相同的或者類似的部分封裝起來,然後只修改SQL語句,實現對數據庫的操作,這樣操作人員只需要修改Impl實現類就可以了。

       Java真有意思(/斜眼笑)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章