目录
前言
我们在获取数据库连接后,需要对数据库进行相关操作,Java以前提供了Statement来进行进行相关操作,不过由于Statement存在sql注入问题,后面改用Statement的子类PreparedStatement来进行相关操作。由于直接上来讲什么是sql注入不太方便理解,我们先讲解如何使用PreparedStatement来操作数据库,在这之后在来叙述和解释Statement中的缺点,同时为了方便,我们在介绍增删改查之前先展示如何将获取连接和关闭连接封装到一个类中。最后需要说明的是,这篇文章只是提供相关的基础操作,对于如何写出比较通用的增删改查请关注后续文章。
封装获取和关闭连接
我们知道每次获取连接需要经历如下步骤:将相关配置文件加载到程序中---->读取配置文件相关信息---->加载驱动---->获取连接;在使用完连接后,我们需要将相关资源关闭。获取连接和关闭相关资源是我们在使用Java操作数据库时必然会进行的操作,为了方便我们将这些操作封装到一个类的静态方法中,使用时只需提供相应的参数便可以获取和关闭连接。
封装获取连接
在获取连接时,对我们来说唯一需要注意的变化就是配置文件的名称,所以我们只需要将配置文件的名称通过参数的形式传入即可。封装代码如下:
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import com.mysql.cj.protocol.Resultset;
public class ConnectionUtil {
//获取数据库连接
public static Connection getConnection(String fileName) throws Exception {
//通过反射创建指向配置文件的流
InputStream inputStream=ConnectionUtil.class.getClassLoader().getResourceAsStream(fileName);
//通过Properties对象来读取配置文件信息
Properties properties = new Properties();
properties.load(inputStream);
String driver=properties.getProperty("driver");
String user=properties.getProperty("user");
String password=properties.getProperty("password");
String url=properties.getProperty("url");
//通过反射加载驱动
Class.forName(driver);
//获取连接
Connection connect=DriverManager.getConnection(url,user,password);
//关闭流
inputStream.close();
return connect;
}
}
封装关闭相关资源
在操作完数据库后,我们一定需要关闭的资源有数据库连接和PreparedStatement;如果我们是查找数据的话还需要关闭ResultSet。所以我们提供两个重载的方法来完成这些操作。封装代码如下:
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import com.mysql.cj.protocol.Resultset;
public class ConnectionUtil {
//关闭相关资源
public static void closeConnection(Connection connect,PreparedStatement preparedStatement) {
//关闭数据库连接
try {
if(connect!=null)
connect.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//关闭PreparedStatement
try {
if(preparedStatement!=null)
preparedStatement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void closeConnection(Connection connect,PreparedStatement preparedStatement,ResultSet resultSet) {
//关闭数据库连接和PreparedStatement
closeConnection(connect, preparedStatement);
//关闭ResultSet
try {
if(resultSet!=null)
resultSet.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
PreparedStatement说明
- PreparedStatement是Statement的子类。
- PreparedStatement代表着一个预编译sql语句。
- 使用Connection对象调用preparedStatement(String sql)来获取一个PreparedStatement对象。
增加数据
步骤
- 获取连接。
- 预编译sql语句,返回PreparedStatement对象实例(调用Connection对象的preparedStatement(String sql)方法)。
- 填充占位符(调用PreparedStatement对象的setObject()方法)。
- 执行sql语句(调用PreparedStatement对象的excute()方法)。
- 关闭PreparedStatement和数据库连接。
例:创建数据库test,创建数据表test_table,包含user数据类型为int和password数据类型为int两个字段。通过Java操作数据库的方式向数据表中添加三条记录。
创建数据库和数据表
CREATE DATABASE IF NOT EXISTS test;
USE test;
CREATE TABLE IF NOT EXISTS test_table(
`user` INT,
`password` INT
);
使用Java操作数据库的方式添加数据
@Test
public void test1() throws Exception {
//获取连接
Connection connect=ConnectionUtil.getConnection("test.properties");
//调用Connection的preparedStatement(String sql)进行预编译sql语句,获取PreparedStatement对象
String sql="insert into test_table values (?,?)";
PreparedStatement ps=connect.prepareStatement(sql);
Scanner scanner=new Scanner(System.in);
for(int i=1;i<=3;i++) {
System.out.println("输入账号:");
Integer user=scanner.nextInt();
System.out.println("输入密码:");
Integer password=scanner.nextInt();
//填充占位符
ps.setObject(1, user);
ps.setObject(2, password);
//执行sql语句
ps.execute();
System.out.println("添加成功");
}
//关闭相关资源
ConnectionUtil.closeConnection(connect, ps);
}
运行结果如下
输入账号:
123456
输入密码:
123456
添加成功
输入账号:
133290
输入密码:
133290
添加成功
输入账号:
105651
输入密码:
191292
添加成功
在数据库中查找结果如下
修改、删除数据
步骤与增加数据的步骤一样
例:删除上面添加的数据
使用Java操作数据库删除数据
@Test
public void test2() throws Exception {
//获取连接
Connection connect=ConnectionUtil.getConnection("test.properties");
//预编译sql语句
String sql="delete from test_table where user = ?";
PreparedStatement ps=connect.prepareStatement(sql);
Scanner scanner = new Scanner(System.in);
for(int i=1;i<=3;i++) {
System.out.println("输入账号");
String user=scanner.next();
//填充占位符
ps.setObject(1, user);
//执行sql语句
ps.execute();
}
//关闭相关资源
ConnectionUtil.closeConnection(connect, ps);
}
运行结果如下
123456
输入账号
133290
输入账号
105651
数据库中查询结果如下
查找数据
查询结果是一个结果集,所以我们需要对结果集进行进一步操作,如将其输出到控制台中,才能查阅相关的查询结果。为此需要使用一个新的接口:ResultSet来进行相关操作。我们先介绍ResultSet的一些常用方法再介绍如何使用ResultSet处理结果集中的数据。
ResultSet中常用方法
- boolean next():判断结果集中是否还存在下一条记录,如果有则返回true,没有则返回false同时指针下移。
- Xxx getXxx(int columnIndex):获取指针所指向数据对应索引位置的值。
- ResultSetMetaData getMetaData():获取结果集的元数据
步骤
- 步骤1到3和增加数据的一样
- 执行sql语句获取ResultSet对象(结果集)(调用PreparedStatement对象的excuteQuery()方法)
- 处理结果集
- 关闭相关资源
例:查询添加数据中账号为133290的账号和密码
使用Java操作数据库查询数据
@Test
public void test3() throws Exception {
//获取连接
Connection connect=ConnectionUtil.getConnection("test.properties");
//预编译sql语句
String sql="select * from test_table where `user`=?";
PreparedStatement ps=connect.prepareStatement(sql);
Scanner scanner=new Scanner(System.in);
System.out.println("输入账号:");
String user=scanner.next();
//填充占位符
ps.setObject(1, user);
//执行sql语句获取结果集
ResultSet rs=ps.executeQuery();
//处理结果集
/*使用next()来判读结果集下一条元素是否存在,并且将指针下移*/
while(rs.next()) {
int user1;
int password1;
user1=rs.getInt(1);
password1=rs.getInt(2);
System.out.println("user="+user1+" 密码="+password1);
}
//关闭相关连接
ConnectionUtil.closeConnection(connect, ps, rs);
}
运行结果如下
输入账号:
133290
user=133290 密码=133290
使用Statement的弊端
- 需要拼写sql语句,比较麻烦。
- 存在sql注入问题。
以如下例子来解释说明什么叫拼写sql语句和sql注入,同时大家也能感受一下Statement的弊端。
例:要求用户输入账号密码,然后通过Java语句来调用相关sql语句查询数据库中是否有相应的账号密码,如果有则登录成功,否则登录失败。
拼写sql语句解释:
假定我们将账号存储在String 对象user中,密码存储在String对象password中。
我们需要通过sql语句:
SELECT user,password FROM test WHERE user = '账号值' AND password = '密码值';
来查询数据库。但是我们需要将上面的sql语句通过Java调用,就是把上面的语句保存在String对象中,这就变成了下面的句子:
String sql="SELECT user,password FROM test WHERE user = '"+user+"'AND password = '"+password+"'";
这是因为我们要通过user和password对象来传入具体数据,所以出现了字符串拼接问题。
sql注入解释:
如果我们将下面的值代替账号和密码
user:1' OR
password:1' OR '1' = '1
则我们的sql语句在传入数据后会变成
SELECT user,password FROM test WHERE user ='1' OR 'AND password = '1' OR '1' = '1';
可以看到sql的条件由原来的判断账号密码是否存在变成了三个并列条件,且有一个条件是'1'='1',这是一定正确的。也就是说这一定会返回所有账号密码的sql语句,我们一定可以登录成功。
这就是sql注入问题。
对于上面的弊端我们是没办法接受的,所以我们使用PreparedStatement来代替Statement进而解决上面的问题。
版权归原作者 COWARD_LOVE1 所有, 如有侵权,请联系我们删除。