jdbc是Java Database Connectivity(java数据库连接)是一种用于执行sql语句的java api
示例源码(防注入、使用PrepareStatement):
首先要利用反射注册加载驱动,然后是建立连接,这个过程需要指明url(类似于ip,用于识别数据库的位置,其写法为jdbc:mysql://192.168.88.107:3306/Mrs_WuHo_O,其中jdbc表示协议、mysql表示子协议、192.168.88.107:3306是要链接到的主机和端口,最后是数据库的名字)、用户名、密码。这样,就建立好了连接。
下一步是创建并执行我们要做的sql语句,执行语句时,我们有两个类可以选择,一个是Statement,一个是PrepareStatement
二者的主要区别是
1)语句不同,PreparedStatement需要预编译以及需要参数
2)由于PreparedStatement有缓存区,所以效率更高
3)由于PreparedStatement有缓存区,所以更安全,防止了注入(1=1)
解释下什么是“注入”,如SQL串为“update student set name=\"jdbc update\" where id=6”,其中用户输入的是6,修改结果是
如果用户输入"6 or 2=2",则会出现该sql无论如何都会改变所有结果的危险。执行sql"update student set name=\"jdbc update\" where id=6 or 2=2"的后结果集为:
那么,为什么Preparestatement可以防止注入呢?这与Preparestatement的预编译有关,SQL语句在程序运行前已经进行了预编译,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or '1=1'也数据库会作为一个参数一个字段的属性值来处理,而不会作为一个SQL指令。也就真正将用户的输入不再存在当作“SQL指令”去执行,统一都作为值去对待。
所以,Preparestatement是优于Statement的,况且我们现在是略有水平对的开发人员了,所以我们选用Preparestatement,
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.Statement;
public class JdbcTest {
public static String driverName ="com.mysql.jdbc.Driver";
public static String connection_url = "jdbc:mysql://192.168.88.107:3306/tianliang";
public static String userName = "root";
public static String userPwd = "tianliangedu";
public static void main(String[] args) {
try {
Class.forName(driverName);
Connection dbConn = DriverManager.getConnection(connection_url, userName,
userPwd);
String sql="update student set classname=\"计算机科学与技术1班\" where classname= ? ";
PreparedStatement ps=dbConn.prepareStatement(sql);
ps.setString(1,"计科1班 or 2=2");
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
}
DBCP数据库连接池:
为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
其中要设置连接池中初始化连接个数,池中最大连接数和最小连接数、等待时间。
import org.apache.commons.dbcp2.BasicDataSource;
import java.sql.Connection;
public class DBCPTest {
//声明连接池对象引用,方便其它线程去共享使用
public static BasicDataSource dataSource=null;
static {
//1、初始化连接池对象
dataSource = new BasicDataSource();
// 2、为数据源实例指定必须的属性,用户名、密码、url、驱动名称等
dataSource.setUsername("root");
dataSource.setPassword("tianliangedu");
dataSource.setUrl("jdbc:mysql://192.168.88.107:3306/tianliang");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
// 3、指定数据源的一些可选属性
// 3.1、指定数据库连接池中初使化连接数的个数
dataSource.setInitialSize(2);
// 3.2、指定最大连接数:同一时刻可以同时向数据库申请的连接数
dataSource.setMaxTotal(3);
// 3.3、指定最小连接数:在数据库连接池中保存的最少的空闲连接的数量
dataSource.setMinIdle(2);
// 3.4、等待数据库连接池分配连接的最长时间。单位为毫秒。超出时间将抛出异常
dataSource.setMaxWaitMillis(5*1000);
}
public static void main(String args[]) throws Exception{
//用5个线程去测试获取和关闭连接
for(int i=0;i<5;i++){
new Thread(new ConnectionRunnable(dataSource)).start();
}
}
}
//定义线程runnable类,方便对线程池的测试
class ConnectionRunnable implements Runnable{
private BasicDataSource basicDataSource;
public ConnectionRunnable(BasicDataSource basicDataSource){
this.basicDataSource=basicDataSource;
}
public void run(){
//每个线程分别得到和关闭连接各10次
for(int i=0;i<10;i++){
try{
//从连接池中拿到一个数据库连接
Connection conn=basicDataSource.getConnection();
System.out.println(conn);
//获取链接池正在被使用的链接数量
System.out.println("active="+basicDataSource.getNumActive());
//获取链接池中空闲链接数量
System.out.println("idle="+basicDataSource.getNumIdle());
//将用完的数据库连接还回到连接池中,该close方法已被dbcp重写
conn.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}