JDBC
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC相关的知识,希望对你有一定的参考价值。
JDBC常用对象
DriverManager 驱动管理类
主要作用:
- 注册驱动 实际开发中使用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") + " "
+ resultSet.getString("username") + " "
+ resultSet.getString("password") + " "
+ 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") + " "
+ resultSet.getString("username") + " "
+ resultSet.getString("password") + " "
+ 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>
以上是关于JDBC的主要内容,如果未能解决你的问题,请参考以下文章