zoukankan      html  css  js  c++  java
  • JDBC

    和 Statement一样,PreparedStatement也是用来执行sql语句的
    与创建Statement不同的是,需要根据sql语句创建PreparedStatement
    除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接

    注: 这是JAVA里唯二的基1的地方,另一个是查询语句中的ResultSet也是基1的。

    package jdbc;
       
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
       
    public class TestJDBC {
        public static void main(String[] args) {
            try {
                Class.forName("com.mysql.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
     
            String sql = "insert into hero values(null,?,?,?)";
            try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin");
            	// 根据sql语句创建PreparedStatement
                PreparedStatement ps = c.prepareStatement(sql);
            ) {
            	
                // 设置参数
                ps.setString(1, "提莫");
                ps.setFloat(2, 313.0f);
                ps.setInt(3, 50);
                // 执行
                ps.execute();
     
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
       
        }
    }
    

      Statement 需要进行字符串拼接,可读性和维护性比较差

     
    String sql = "insert into hero values(null,"+"'提莫'"+","+313.0f+","+50+")";
     


    PreparedStatement 使用参数设置,可读性好,不易犯错

     
    String sql = "insert into hero values(null,?,?,?)";
     
    PreparedStatement有预编译机制,性能比Statement更快

    PreparedStatement的优点3-防止SQL注入式攻击

    假设name是用户提交来的数据
     
    String name = "'盖伦' OR 1=1";
     

    使用Statement就需要进行字符串拼接
    拼接出来的语句是:
     
    select * from hero where name = '盖伦' OR 1=1
     

    因为有OR 1=1,这是恒成立的
    那么就会把所有的英雄都查出来,而不只是盖伦
    如果Hero表里的数据是海量的,比如几百万条,把这个表里的数据全部查出来
    会让数据库负载变高,CPU100%,内存消耗光,响应变得极其缓慢

    而PreparedStatement使用的是参数设置,就不会有这个问题


    execute与executeUpdate的相同点:都可以执行增加,删除,修改
    不同1:
    execute可以执行查询语句
    然后通过getResultSet,把结果集取出来
    executeUpdate不能执行查询语句
    不同2:
    execute返回boolean类型,true表示执行的是查询语句,false表示执行的是insert,delete,update等等
    executeUpdate返回的是int,表示有多少条数据受到了影响


    在事务中的多个操作,要么都成功,要么都失败
    通过 c.setAutoCommit(false);关闭自动提交
    使用 c.commit();进行手动提交
    在22行-35行之间的数据库操作,就处于同一个事务当中,要么都成功,要么都失败
    所以,虽然第一条SQL语句是可以执行的,但是第二条SQL语句有错误,其结果就是两条SQL语句都没有被提交。 除非两条SQL语句都是正确的。
     
    package jdbc;
     
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.Statement;
     
    public class TestJDBC {
        public static void main(String[] args) {
    		try {
    			Class.forName("com.mysql.jdbc.Driver");
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    
    		try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin");
    			Statement s = c.createStatement();) {
     
                // 有事务的前提下
                // 在事务中的多个操作,要么都成功,要么都失败
     
                c.setAutoCommit(false);
     
                // 加血的SQL
                String sql1 = "update hero set hp = hp +1 where id = 22";
                s.execute(sql1);
     
                // 减血的SQL
                // 不小心写错写成了 updata(而非update)
     
                String sql2 = "updata hero set hp = hp -1 where id = 22";
                s.execute(sql2);
     
                // 手动提交
                c.commit();
     
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
     
        }
    }
    

      




    当有多个线程,每个线程都需要连接数据库执行SQL语句的话,那么每个线程都会创建一个连接,并且在使用完毕后,关闭连接。

    创建连接和关闭连接的过程也是比较消耗时间的,当多线程并发的时候,系统就会变得很卡顿。

    同时,一个数据库同时支持的连接总数也是有限的,如果多线程并发量很大,那么数据库连接的总数就会被消耗光,后续线程发起的数据库连接就会失败。

    与传统方式不同,连接池在使用之前,就会创建好一定数量的连接。
    如果有任何线程需要使用连接,那么就从连接池里面借用,而不是自己重新创建.
    使用完毕后,又把这个连接归还给连接池供下一次或者其他线程使用。
    倘若发生多线程并发情况,连接池里的连接被借用光了,那么其他线程就会临时等待,直到有连接被归还回来,再继续使用。
    整个过程,这些连接都不会被关闭,而是不断的被循环使用,从而节约了启动和关闭连接的时间。
    1. ConnectionPool() 构造方法约定了这个连接池一共有多少连接

    2. 在init() 初始化方法中,创建了size条连接。 注意,这里不能使用try-with-resource这种自动关闭连接的方式,因为连接恰恰需要保持不关闭状态,供后续循环使用

    3. getConnection, 判断是否为空,如果是空的就wait等待,否则就借用一条连接出去

    4. returnConnection, 在使用完毕后,归还这个连接到连接池,并且在归还完毕后,调用notifyAll,通知那些等待的线程,有新的连接可以借用了。
    package jdbc;
     
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
     
    public class ConnectionPool {
     
        List<Connection> cs = new ArrayList<Connection>();
     
        int size;
     
        public ConnectionPool(int size) {
            this.size = size;
            init();
        }
     
        public void init() {
             
            //这里恰恰不能使用try-with-resource的方式,因为这些连接都需要是"活"的,不要被自动关闭了
            try {
                Class.forName("com.mysql.jdbc.Driver");
                for (int i = 0; i < size; i++) {
                    Connection c = DriverManager
                            .getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8", "root", "admin");
     
                    cs.add(c);
     
                }
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
     
        public synchronized Connection getConnection() {
            while (cs.isEmpty()) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            Connection c = cs.remove(0);
            return c;
        }
     
        public synchronized void returnConnection(Connection c) {
            cs.add(c);
            this.notifyAll();
        }
     
    }
    

      

  • 相关阅读:
    vue-cli 3.0 路由懒加载
    vue 路由拦截、axios请求拦截
    vue-cli 3.0 图片路径问题(何时使用 public 文件夹)
    vue 监听页面宽度变化 和 键盘事件
    WGS84、GCJ-02(火星坐标)、百度坐标,Web墨卡托坐标
    Java学习之道:Java项目打包发布
    ora-14550问题解决
    费氏搜寻法之算法分析与实现
    [置顶] woff格式字体怎么打开和编辑?
    C++小知识之Vector排序
  • 原文地址:https://www.cnblogs.com/tao7/p/12218092.html
Copyright © 2011-2022 走看看