JDBC
SQL注入
- 概述:是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息,导致数据泄露。
- SQL语句通过
or
被拼接:
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SQL注入 {
// 模拟登录业务
public static void login(String username,String password){
Connection conn =null;
Statement st = null;
ResultSet rs = null;
try {
conn = jdbcUtils.getConnection();
st = conn.createStatement();
String sql = "select * from users where `NAME`='"+username+"' AND `password` ='"+password+"'";
rs = st.executeQuery(sql); //查询完毕会返回一个结果集
while (rs.next()){
System.out.println(rs.getString("NAME"));
System.out.println(rs.getString("password"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
jdbcUtils.release(conn,st,rs);
}
}
}
这两个sql语句:
-- 正常sql语句
SELECT * FROM users WHERE `Name` = 'zhangsan' AND `password` = '123456';
-- 将name和password设置为空或者1=1
SELECT * FROM users WHERE `Name` = '' or '1=1' AND `password` = '' or '1=1';
正常登录:
public static void main(String[] args) {
login("zhangsan","123456");
}
运行结果:
or
拼接登录:
public static void main(String[] args) {
login(" 'or '1=1"," 'or'1=1");
}
运行结果:将所有用户信息查询出来
PreparedStatement对象
可以防止SQL注入,效率更好!
- 防止SQL注入:
原理:把传递进来的参数当做字符,假设其中存在转义字符,比如说 ’ 会被直接转义。
import java.sql.*;
public class 防止SQL注入 {
public static void main(String[] args) {
login(" 'or '1=1"," 'or'1=1");
}
// 登录业务
public static void login(String username,String password){
Connection conn =null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = jdbcUtils.getConnection();
String sql = "select * from users where `NAME`=? and `PASSWORD`=?"; // Mybatis
st = conn.prepareStatement(sql);
st.setString(1,username);
st.setString(2,password);
rs = st.executeQuery(); //查询完毕会返回一个结果集
while (rs.next()){
System.out.println(rs.getString("NAME"));
System.out.println(rs.getString("password"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
jdbcUtils.release(conn,st,rs);
}
}
}
运行结果:查询不到结果。
- 使用PreparedStatement对象的插入数据操作:
import java.sql.Connection;
import java.util.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = jdbcUtils.getConnection();
// 使用? 占位符代替参数
String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`) values(?,?,?,?,?)";
st = conn.prepareStatement(sql);
// 手动给参数赋值
st.setInt(1,4); //id
st.setString(2,"qinjiang");
st.setString(3,"1232112");
st.setString(4,"24734673@qq.com");
st.setDate(5,new java.sql.Date(new Date().getTime()));
//执行
int i = st.executeUpdate();
if (i>0){
System.out.println("插入成功!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
jdbcUtils.release(conn,st,null);
}
}
}
- 和Statement对象的区别:
使用PreparedStatement对象编写是要经过预编译SQL,即先写sql,然后不执行,再手动给参数赋值;
使用Statement对象编写则是将编写好的SQL语句直接进行执行操作;故使用PreparedStatement对象可以防止SQL注入,效率更好!
IDEA连接数据库
打开IDEA:
选择好MySQL后会出现下面的操作,填写User和Password后,点击Test Connection,显示Successful表示连接成功:
最后点击Apply再点击OK,完成连接;
点击设置标志,导入需要的数据库:
查看表内容:双击表名即可查看表信息,下面为sql语句;
修改表内容后必须点击下面的绿色箭头,完成保存:
在IDEA中编写SQL代码:
Java代码实现事务
事务描述见我的前面的博文:事务
1、关闭自动提交,开启事务 conn.setAutoCommit(false);
2、一组业务执行完毕,提交事务;
3、可以在catch 语句中显示的定义 回滚语句,但默认失败就会回滚。
模拟转账用户A给用户B转账100:我们加入int x = 1/0;
这个错误,报错后,默认回滚
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Test {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = jdbcUtils.getConnection();
conn.setAutoCommit(false);
String sql1 = "update account set money = money-100 where name = 'A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
int x = 1/0; // 报错
String sql2 = "update account set money = money+100 where name = 'B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
//业务完毕,提交事务
conn.commit();
System.out.println("成功!");
} catch (SQLException e) {
// 如果失败,则默认回滚
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
jdbcUtils.release(conn,st,rs);
}
}
}
数据库连接池
- 概述:数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
- 影响因素:
1)最小连接数
是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费。
2)最大连接数
是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。
3)最小连接数与最大连接数差距
最小连接数与最大连接数相差太大,那么最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放。 - 开源数据源实现:
- DBCP
需要的jar包:commons-dbcp-1.4 、 commons-pool-1.6 - C3P0
需要的jar包:c3p0-0.9.5.5、mchange-commons-jiava-0.2.19
需要的jar包:百度网盘链接,提取码:s09s
- DBCP数据库连接池的测试:
工具类提取:
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils_DBCP {
private static DataSource dataSource = null;
static {
try{
InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties properties = new Properties();
properties.load(in);
//创建数据源 工厂模式 --> 创建
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection(); //从数据源中获取连接
}
//释放连接资源
public static void release(Connection conn, Statement st, ResultSet rs){
if (rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试代码:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;
public class TestDBCP {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = JdbcUtils_DBCP.getConnection();
String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`) values(?,?,?,?,?)";
st = conn.prepareStatement(sql);
st.setInt(1,4); //id
st.setString(2,"qinjiang");
st.setString(3,"1232112");
st.setString(4,"24734673@qq.com");
st.setDate(5,new java.sql.Date(new Date().getTime()));
//执行
int i = st.executeUpdate();
if (i>0){
System.out.println("插入成功!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils_DBCP.release(conn,st,null);
}
}
}
//下篇再见…谢谢