JDBC的进化3

这次进化完成后,JDBC的进化就需要设置一个savepoint了,然后提交一下,提交到我们的脑袋硬盘中。

上一次说到了利用Statement对象来发送sql语句到数据库。然而这样的话,会暴露出两个问题。

那么问题来了!!!

问题1:

在执行executeUpdate(String sql)和executeQuery(String sql)方法时,我们需要拼写sql字符串,就像下面这样:

String sql = "insert into customers values(21, ‘yushen1‘, ‘[email protected]‘, ‘1998-3-2‘, null)";

String sql = "SELECT FlowId flowId, type, IDCard idCard, ExamCard examCard, StudentName studentName, location, Grade grade FROM examstudent WHERE ExamCard = ‘" + examCard + "‘";

感受到点什么了没有???麻烦!!是不是,假如说我要修改20个字段,我是不是得拼好长一个字符串?这就是问题1。

问题2:

看下面的代码:

String user = "‘ OR 1 = 1--‘";
SELECT *
FROM user_table
WHERE user = ‘"+user+"‘;

大家知道发生了什么?我把整张表的信息都获取了,包括密码(你的银行账户密码被我知道了),太可怕了! 这就是SQL注入问题,也是我要说的第二个问题。

告诉你个好消息,有一个一劳永逸的办法可以同时解决这两个问题,同时对于批量处理效率会大大的提升,这个办法就是PreparedStatement接口。简直是好处多多啊。来我们来学习这个办法。还是老样子,从具体到通用。

Solution:

PreparedStatement接口:它是Statement接口的子接口。

看看API中对它的描述:

“An object that represents a precompiled SQL statement.”

“A SQL statement is precompiled and stored in a PreparedStatement object. This object can then be used to efficiently execute this statement multiple times. “

先看第一句:代表一个预编译的SQL命令对象

再看第二句:一个SQL命令是预编译的和存储在一个PreparedStatement对象中。这个对象随后能高效的执行这个命令多次。

涉及到一个词:预编译,我在下面结合代码来说

先来一个具体的例子:

@Test
    public void testPrepareSelect(){
        String sql = "SELECT * FROM users WHERE id = ?;";
        // get connection
        Connection conn = null;
        // get PreparedStatement‘s object
        PreparedStatement ps = null;
        // execute the sql
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            ps = conn.prepareStatement(sql);
            // set the ?
            ps.setInt(1, 1);
            // execute the sql
            rs = ps.executeQuery();
            // get the rs
            if(rs.next()){
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String address = rs.getString("address");
                String phone = rs.getString("phone");
                User user = new User(id, name, address, phone);
                System.out.println(user);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close(rs, ps, conn);
        }

    }

是不是发现个问号,然后给这个问号设置值?问:这个问号是什么东东啊?答:这个问号相当于一个占位符,我就把这个位置占住了。要不为什么叫预编译。

再来一个通用的:

/**
     * PreparedStatement: through the reflect and generic and PreparedStatement
     * @param sql
     * @param clazz
     * @param args
     * @return
     */
    public <T> T getPrepareSelect(String sql,Class<T> clazz, Object ... args){
        T t = null;
        // get the connection
        Connection conn = null;
        // get the PreparedStatement‘s object
        PreparedStatement ps = null;
        // execute the ps
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            ps = conn.prepareStatement(sql);
            // set values for ps
            for(int i = 0; i < args.length; i++){
                ps.setObject(i+1, args[i]);
            }
            rs = ps.executeQuery();
            // get the ResultSetMetaData‘s object
            ResultSetMetaData rsmd = rs.getMetaData();
            // get the columnNum
            int columnNum = rsmd.getColumnCount();
            // read the data of rs, and packaging an object
            if(rs.next()){
                t = clazz.newInstance();
                for(int i = 1; i <= columnNum; i++){
                    // get the columnName and columnValue of the special row special column
                    String columnName = rsmd.getColumnLabel(i);
                    Object columnValue = rs.getObject(columnName);
                    // through the generic put the columnValue to the Class‘ field
                    Field userField = clazz.getDeclaredField(columnName);
                    userField.setAccessible(true);
                    userField.set(t, columnValue);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close(rs, ps, conn);
        }
        return t;
    }

我靠,你这写的什么东西啊? 我就问你你还记得反射和泛型吗?

反射:

我们在编译时不能确定创建什么对象,只能在运行的时候创建,而运行时怎么创建呢?昨天还是前天的博客,我提到过一个思维扩散:描述数据的数据叫元数据,描述注解的注解叫元注解,描述类的类叫??? 汗,我也不知道该叫什么了,但是的确存在一个类来描述一个已编译(已加载?我不确定,回头看看)的类。而通过这个类我们可以获得它描述类的任何信息,包括创建对象和给属性设置值。我后面会总结一篇反射。现在该知道了吧?

因为要写成通用的,

1.我们不能确定返回值是什么类型的对象,我们使用了泛型。

2.对象的属性个数,什么类型你同样不知道,我们使用反射,和多态参数来解决这个问题。

这里出现了个这东西:ResultSetMetaData 它是用来描述ResultSet的,我们知道ResultSet存的是一张数据表,而ResultSetMetaData就是用来描述这张表的,包括他有几列,每列是什么。

现在读我这个程序是不是感觉好多了? 也不过如此么!!!

从具体到一般,我们上面写的仅仅是查询一条记录的。

来,再来一个查询多条记录的通用的方法:

/**
     * PreparedStatement : getAll
     * @param sql
     * @param clazz
     * @param args
     * @return
     */
    public <T> List<T> getAll(String sql, Class<T> clazz, Object ... args){
        List<T> list = new ArrayList<T>();
        // get connection
        Connection conn = null;
        // get PreparedStatement
        PreparedStatement ps = null;
        // execute the sql
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            ps = conn.prepareStatement(sql);
            // set the ps
            for(int i = 0; i < args.length; i++){
                ps.setObject(i+1, args[i]);
            }
            rs = ps.executeQuery();
            // get the columnNum
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnNum = rsmd.getColumnCount();
            // read the rs and write to an object
            while(rs.next()){
                T t = clazz.newInstance();
                for(int i = 1; i <= columnNum; i++){
                    // read
                    String columnName = rsmd.getColumnLabel(i);
                    Object columnVal = rs.getObject(columnName);

                    // write
                    // through the field(reflect)
                    //Field field = clazz.getDeclaredField(columnName);
                    //field.setAccessible(true);
                    //field.set(t, columnVal);

                    // through the method(reflect)
                    PropertyUtils.setProperty(t, columnName, columnVal);
                }
                list.add(t);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close(rs, ps, conn);
        }
        return list;
    }

来了,相比于上一个来说,这个其实很简单了,只不过是增加了一个List<T>集合来存放对象,获取连接和关闭的话,你看看我的进化1就明白了。还有就是使用了Apache提供的工具类,但是我个人还是希望你能自己写写,以后实际项目使用的时候再用工具类会好一点。

这里来说预编译

预编译指令指示了在程序正式编译前就由编译器进行的操作,我认为这个编译当然不是Java中的编译了,而是将它放到了数据库中,在数据库中进行预编译,你仔细想想是不是这样?这个该说说内存了,数据库在内存中会有库池,->数据缓冲区,->日志缓冲区,一条简单的Select语句发送到数据库要经历一个硬解析的过程,硬解析->检查->执行计划,然后到库池,再到数据库缓冲池。对于普通的Statement语句发送的Sql语句,它每次都要执行这个过程。而PreparedStatement则不同,它被编译过的语句会被缓存下来,下次调用有相同的预编译语句就不会重新进行编译(即上面那个过程),将参数传入就会执行。

通过上面一段话:有产生了一个新的东西:

批量处理:

三个方法:addBatch()一个装载的过程,executeBatch()执行的过程,clearBatch()清空缓冲的数据。

具体的执行过程和上面的类似,我会在效率的比较中给出具体的例子。

这些说完了,我们来测试测试他和Statement的效率,不然你们还以为我骗你们,说PreparedStatement效率高。

我同时向数据库中插入100000条记录为例

statement:

@Test
    public void testStatement() {// 260111
        long start = System.currentTimeMillis();

        Connection conn = null;
        Statement statm = null;
        try {
            conn = JDBCUtils.getConnection();

            statm = conn.createStatement();

            for (int i = 0; i < 100000; i++) {
                String sql = "insert into emp1 values(" + i + ", ‘emp" + i
                        + "‘)";

                statm.executeUpdate(sql);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close(statm, conn);
        }

        long end = System.currentTimeMillis();

        System.out.println("the time is :" + (end - start));
    }

执行时间:

260111ms

PreparedStatement:

@Test
    public void testPreparedStatment() {// 141991
        long start = System.currentTimeMillis();

        // get connection
        Connection conn = null;
        // get PreparedStatement‘s object
        PreparedStatement ps = null;
        try {
            conn = JDBCUtils.getConnection();

            String sql = "insert into emp1 values(?, ?)";
            ps = conn.prepareStatement(sql);

            for (int i = 0; i < 100000; i++) {
                ps.setInt(1, i + 1);
                ps.setString(2, "emp" + i);

                ps.executeUpdate();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close(null, ps, conn);
        }

        long end = System.currentTimeMillis();
        System.out.println("the time is :" + (end - start));
    }

141991ms 快了接近一倍

再看看批量处理:

Statement:

@Test
    public void testStatement2() {// 271924
        long start = System.currentTimeMillis();

        Connection conn = null;
        Statement statm = null;
        try {
            conn = JDBCUtils.getConnection();

            statm = conn.createStatement();

            for (int i = 0; i < 100000; i++) {
                String sql = "insert into emp1 values(" + i + ", ‘emp" + i
                        + "‘)";

                statm.addBatch(sql);

                if ((i % 250) == 0) {
                    statm.executeBatch();

                    statm.clearBatch();

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close(statm, conn);
        }

        long end = System.currentTimeMillis();

        System.out.println("the time is :" + (end - start));
    }

271924ms 好慢

PreparedStatement:

接下来就是见证奇迹的时刻!!!

@Test
    public void testPreparedStatement2() {// 3230
        long start = System.currentTimeMillis();

        // get connection
        Connection conn = null;
        // get PreparedStatement‘s object
        PreparedStatement ps = null;
        try {
            conn = JDBCUtils.getConnection();

            String sql = "insert into emp1 values(?, ?)";
            ps = conn.prepareStatement(sql);

            for (int i = 0; i < 100000; i++) {
                ps.setInt(1, i + 1);
                ps.setString(2, "emp" + i);

                ps.addBatch();

                if ((i % 250) == 0) {
                    ps.executeBatch();
                    ps.clearBatch();
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close(null, ps, conn);
        }

        long end = System.currentTimeMillis();
        System.out.println("the time is :" + (end - start));
    }

3230ms 什么???这么快!!!

我就什么也不多说了,继续吧!!

我们接下来说事务,我还是分开写吧,这个太长了,不好阅读。没耐心的观众已经坐不住了!!

补充一点:大数据处理,这里只提供代码示例,可以参考着去研究研究

大数据的读取:

@Test
    public void get(){
        String sql = "select * from customers where id = ?";
        Connection conn = null;
        PreparedStatement ps = null;
        InputStream is = null;
        ResultSet rs = null;
        OutputStream os = null;
        try {
            // get connection
            conn = JDBCUtils.getConnection();

            // get PreparedStatement‘s object
            ps = conn.prepareStatement(sql);
            ps.setInt(1, 22);

            // execute the ps

            rs = ps.executeQuery();

            while(rs.next()){
                int id = rs.getInt(1);
                String name = rs.getString(2);
                String email = rs.getString(3);
                Date birth = rs.getDate(4);

                Customer customer = new Customer(id, name, email, birth);
                System.out.println(customer);

                Blob blob = rs.getBlob(5);
                is = blob.getBinaryStream();
                os = new FileOutputStream("1.jpg");

                byte[] b = new byte[1024];
                int len = 0;
                while((len = is.read(b)) != -1){
                    os.write(b, 0, len);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(os != null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            JDBCUtils.close(rs, ps, conn);
        }

    }

大数据的写入:

public int insertBlob() {
        String sql = "insert into customers values(?, ?, ?, ?, ?)";

        // get connection
        Connection conn = null;
        // get PreparedStatement‘s object
        PreparedStatement ps = null;
        // execute ps
        int rows = 0;
        try {
            conn = JDBCUtils.getConnection();

            ps = conn.prepareStatement(sql);

            ps.setInt(1, 22);
            ps.setString(2, "lisi");
            ps.setString(3, "[email protected]");
            ps.setDate(4,new Date(new java.util.Date().getTime()));

            ps.setBlob(5, new FileInputStream("089.jpg"));
            rows = ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close(ps, conn);
        }
        return rows;
    }
时间: 2024-10-24 22:36:04

JDBC的进化3的相关文章

JDBC的进化3--补充:事务处理

接着JDBC的进化3,我们来说数据库事务. 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态. 怎么理解呢? 一组逻辑单元:我认为指的是多条的DML操作,只是一条DML语句的话,对于mysql来说,执行完成功就自动提交了,没成功的话,就没成功喽,这样说来,一条DML语句就相当于一个原子,不可再分了. 从一种状态变换到另一种状态:即这组操作是成功了还是失败了,他们必须同时都成功,有一个失败,就退回到起点.例如银行的转账,不能一个成功,一个失败吧. 来看看JDBC的事务处理: 先来看看,在什

JDBC的进化

1.数据的存储方式的进化 ①字符串 ②数组 ③文件 ④数据库 其中①和②是存在内存中,会随着程序的终止而结束,不会保留到本地磁盘 ③和④则会将数据存储到本地磁盘,而④可以对数据进行管理,可以根据不同的应用场景选择不同量级的数据库 2.JDBC的进化 起源: Java程序要对磁盘中的数据进行读写,各大数据库厂商提供了相应的驱动,Java提供了相应的接口进行连接. 进化1:面向对象编程 @Test public void testOrclJdbc1() throws SQLException { D

JDBC的进化2

好了,换了个新屏幕,原来的电脑屏幕,过年的时候擦玻璃,玻璃掉下来给砸的裂开了(玻璃一点事情都没有),新屏幕感觉就是爽,也不枉我花了280大洋买的LG的完美屏.战斗力立马感觉飙升20%.闲话就不多说了,继续进化JDBC. 将JDBC的连接完成后,我们要做什么呢?增删改查呗! 好,那就先来一个原始的起源,然后我们逐渐进化. 1.JDBC的进化2-增删改查 获取连接对象后,我们是这样想的,我们要将我们的sql语句通过这个连接发送到数据库,然后让数据库将结果返回给我们. 通过查看API,发现了Conne

JDBC的进化4--最终进化

除去直接调用开源组织给提供的jar包外,我今天要说的内容就是JDBC的最终版,需要我们多敲多练多理解.最重要的是理解.废话不多说了,开始吧. 早上的时候,我将昨天的内容整理,能处理事务的通用方法写到了JDBCUtils工具类中. 其实到这里的时候,所有细节部分的代码都已经实现了.接下来要进行的内容,其实就是对这些内容的整合,整体的调控. 我们来说DAO: 现在我们希望这个程序能更加自动化一点,我们只需要提供一个连接,它就能返回给我们一个对象或一个对象集,或一个结果.或是我们提供一个连接,一个对象

JavaSE:集合总结(Collection,Map)

今天来总结JavaSE部分的集合.首先来从整体来看: 我们主要要学习的内容: Collection: Collection(接口): java.util.Collection |-- List(子接口) : |--ArrayList |--LinkedList |--Vector |-- Set(子接口) : |--AbstracSet(子接口) |--HashSet |--LinkedHashSet |--SortedSet(子接口) |--TreeSet |-- Queue(子接口) : M

Spring DAO vs Spring ORM vs Spring JDBC

Pat 的疑惑 最近关注于 Spring 提供的数据访问技术,对于 Spring 相关的这几个项目有何不同我不是太明白: Spring-DAO (http://docs.spring.io/spring/docs/2.0.8/reference/dao.html) Spring-ORM (http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/orm.html) Spring-JDBC (http://doc

jdbc的简单使用

1.加载驱动(mysql的驱动是com.mysql.jdbc.Driver,SqlServer的驱动是 com.microsoft.sqlserver.jdbc.SQLServerDriver) 2.加载数据库的连接(url, username,password) 3.编写sql语句(String sql="select * from grade  where gradeName = ?";) 4.遍历查询结果 [while (resultSet.next()) {   System.

商城项目整理(三)JDBC增删改查

商品表的增加,修改,删除,订单表的增加,确认,用户表的查看,日志表的增加,查看 商品表建表语句: 1 create table TEST.GOODS_TABLE 2 ( 3 gid NUMBER not null, 4 gname VARCHAR2(90), 5 gdetails CLOB, 6 gpicture VARCHAR2(100), 7 gprice NUMBER, 8 gleixing NUMBER, 9 gpinpai VARCHAR2(20) 10 ) 11 tablespace

jdbc驱动jar导入eclipse

在使用JDBC编程时需要连接数据库,导入JAR包是必须的,导入其它的jar包方法同样如此,导入的方法是 打开eclipse 1.右击要导入jar包的项目,点properties 2.左边选择java build path,右边选择libraries 3.选择add External jars 4.选择jar包的按照路径下的确定后就行了. Java连接MySQL的最新驱动包下载地址 http://www.mysql.com/downloads/connector/j 有两种方法导入jar包,第一种