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();
    }
 
}

  

原文地址:https://www.cnblogs.com/tao7/p/12218092.html