主要作用:
注册驱动 实际开发中使用Class.forName("com.mysql,jdbc.Drive");这种方式,因为之前的方式会导致注册两次驱动
获得连接 Connection getConnection(String url,String username,String password)
url写法:jdbc:mysql://localhost:3306/jdbctest
jdbc 协议
mysql 子协议
localhost 主机名
3306 端口号
jdbctest 数据库名
如果连接的是本机,可以简写为jdbc:mysql:///jbdctest
Connection 连接对象
主要作用:
创建用来执行SQL语句的对象
Statement createStatement() 执行SQL语句,有SQL注入漏洞威胁
PrepareStatement prepareStatement(String sql) 预编译SQL语句,解决SQL注入
CallableStatement prepareCall(String sql) 执行SQL中的存储过程
事务的管理
setAutoCommit(boolean autoCommit) 设置事务是否自动提交
commit() 事务提交
rollback() 事务回滚
Statement 执行SQL
主要作用:
执行SQL语句
boolean execute(String sql) 执行SQL,执行查询语句返回true,否则返回false
ResultSet execute(String sql) 执行SQL中的查询语句
int executeUpdate(String sql) 执行SQL中的插入、更新、删除语句
执行批处理操作
addBatch(String sql) 添加到批处理
executeBatch() 执行批处理
clearBatch() 清空批处理
ResultSet 结果集
主要作用:
获取查询到的结果
next() 判断是否存在下一条记录
针对不同类型的数据可以使用getXXX()获取数据
getObject() 通用获取数据,可以获取任何类型的数据
JDBC的SQL注入漏洞问题
package com.kernel.test;
import org.junit.Test;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* 演示JDBC注入漏洞
*/
public class JDBCDemo04 {
@Test
/**
* 测试SQL注入
*/
public void demo1() {
//boolean result = login("aaa", "11");
//boolean result = login("aaa' or '1=1", "5454545");
boolean result = login("aaa' -- ", "sjjkhnjkhnk");
System.out.println(result);
}
public boolean login(String username, String password) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
statement = connection.createStatement();
String sql = "select * from user where username='" + username + "' and password='" + password + "'";
resultSet = statement.executeQuery(sql);
if (resultSet.next()) {
return true;
} else {
return false;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtil.release(resultSet, statement, connection);
}
return false;
}
}
我们来看下这是什么原因,有这样一行代码
boolean result = login("aaa' or '1=1", "5454545");
这时,sql就变成了这样
sql = "select * from user where username='aaa' or '1=1' and password='545445'";
SQL会首先判断and这个语句,and的结果为false,username='aaa',结果为true,true or false返回true
再来看第二条
sql "select * from user where username='aaa' -- and password='sjjkhnjkhnk'
-- 是注释的意思,意思就是注释后面的password=xxx,肯定返回true
如何解决呢
PrepareStatement是Statement的子接口,它的实例对象可以通过调用Connection.prepareStatement(sql)方法获得,相对于Statement对象而言:
PrepareStatement可以避免SQL注入漏洞的问题
Statement会使数据库频繁的编译SQL,可能造成数据库缓冲区溢出,而PrepareStatement可以对SQL进行预编译,提高数据库执行效率
PrepareStatement允许使用占位符替换SQL中的参数,简化编写
package com.kernel.test;
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* 演示JDBC注入漏洞
*/
public class JDBCDemo04 {
@Test
/**
* 测试SQL注入
*/
public void demo1() {
//boolean result = login("aaa", "11");
//boolean result = login("aaa' or '1=1", "5454545");
boolean result = logon("aaa' -- ", "sjjkhnjkhnk");
System.out.println(result);
}
/**
* 避免SQL注入漏洞
* @param username
* @param password
* @return
*/
public boolean logon(String username, String password) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
String sql = "select * from user where username=? and password=?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, username);
preparedStatement.setString(2, password);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
return true;
} else {
return false;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtil.release(resultSet, (Statement) preparedStatement, connection);
}
return false;
}
/**
* 产生SQL注入漏洞
*
* @param username
* @param password
* @return
*/
public boolean login(String username, String password) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
statement = connection.createStatement();
String sql = "select * from user where username='" + username + "' and password='" + password + "'";
resultSet = statement.executeQuery(sql);
if (resultSet.next()) {
return true;
} else {
return false;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtil.release(resultSet, statement, connection);
}
return false;
}
}
为什么使用它就可以避免SQL注入漏洞呢?那是因为创建对象的时候就将sql传递进去,并进行了预编译,即使后面通过变量传递了关键字进来,也会认为这是字符串
数据库连接池
连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用
应用程序直接获得链接的缺点
用户每次请求都需要向数据库获得连接,而数据库创建连接通常需要消耗较大的资源,创建时消耗的事件也比较长,在高并发业务场景下,如果采用这种方式获得连接,极大浪费数据库的资源,并且容易造成数据库服务器内存溢出
那么连接池是怎么解决这个问题的呢
连接池中默认存放了若干个数据库连接对象,当用户请求与数据库进行连接时,直接从数据库中取出,当用户完成操作需要释放数据库连接资源时,销毁的连接回到连接池继续等待下一次用户的请求
C3P0的使用
package com.kernel.test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* 演示连接池
*/
public class DataSourseDemo01 {
@Test
/**
* 手动设置连接池
*/
public void demo1() {
//获得连接
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//创建连接池
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//设置连接池参数
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/jdbctest");
dataSource.setUser("root");
dataSource.setPassword("123456");
dataSource.setMaxPoolSize(20);
connection = dataSource.getConnection();
String sql = "select * from user";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getInt("uid") + "\t"
+ resultSet.getString("username") + "\t"
+ resultSet.getString("password") + "\t"
+ resultSet.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtil.release(resultSet, preparedStatement, connection);
}
}
@Test
/**
* 读取配置文件设置连接池,默认读取src目录下的c3p0-config.xml文件
*/
public void demo2() {
//获得连接
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//创建连接池
connection = DBUtil.getConnection();
String sql = "select * from user";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getInt("uid") + "\t"
+ resultSet.getString("username") + "\t"
+ resultSet.getString("password") + "\t"
+ resultSet.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DButil2.release(resultSet, preparedStatement, connection);
}
}
}
编写C3P0的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///jdbctest</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
</c3p0-config>
©著作权归作者所有:来自51CTO博客作者灰白世界的原创作品,如需转载,请注明出处,否则将追究法律责任