目录
回顾
上篇文章总结了使用JDBC与数据库建立连接的过程,方式一到方式五不断演进,推荐平时使用方式五。数据库连接建立后,我们就可以进一步对数据库的数据进行操作。
操作数据库的方式
上一篇文章我们创建了Connection对象,如果想操作数据库还要创建Statement对象,才能执行SQL语句。如果是执行查询操作,还需要用到ResultSet对象。
在java.sql包中有三个接口分别定义了对数据库的调用的不同方式:
- Statement接口
- PreparedStatement接口(Statement的子接口)
- CallableStatement接口(PreparedStatement的子接口)
数据库准备
安装的MySQL一般都会包含test数据库,这个数据库里没有表,我们先创建一张学生表,设置name和password两个字段,为后面的操作做准备。
USE test;
CREATE TABLE stuinfo(name VARCHAR(20), password VARCHAR(20));
INSERT INTO stuinfo(name, password) VALUES('zhang', '123');
INSERT INTO stuinfo(name, password) VALUES('liu', '456');
Statement方式
获取连接后,创建Statement对象,定义sql语句,执行查询。
这段代码只是做个演示,没有关闭资源。
//StatementTest.java
import org.junit.Test;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import java.util.Scanner;
public class StatementTest {
@Test
public void login() throws Exception {
//1.获取连接
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties prop = new Properties();
prop.load(is);
String driverClass = prop.getProperty("driverClass");
String url = prop.getProperty("url");
String user = prop.getProperty("user");
String password = prop.getProperty("password");
Class.forName(driverClass);
Connection conn = DriverManager.getConnection(url, user, password);
//2.创建Statement对象
Statement st = conn.createStatement();
//3.定义SQL语句
Scanner sc = new Scanner(System.in);
System.out.print("请输入name:");
String input_name = sc.nextLine();
System.out.println(input_name);
System.out.print("请输入password:");
String input_password = sc.nextLine();
System.out.println(input_password);
String sql = "SELECT * FROM stuinfo WHERE name = '" + input_name + "' AND password = '" + input_password + "';";
//4.返回查询结果,如果为空则登录失败,不为空则登陆成功
ResultSet rs = st.executeQuery(sql);
System.out.println(rs.next() ? "登录成功" : "登陆失败");
}
}
测试结果
- 测试一
请输入name:zhang
请输入password:123
登录成功
- 测试二
请输入name:zhang
请输入password:456
登陆失败
使用Statement方式的弊端
- 存在拼接sql命令字符串的操作,很麻烦。
- 存在SQL注入问题。(其实SQL注入就是拼串造成的。)
当name是1' OR
,password是=1 OR '1'='1
时,即使这条记录不存在数据库表中,也会查询成功,结果是所有的记录。因为拼接之后的SQL语句是这样的:SELECT * FROM stuinfo WHERE name = '1' OR ' AND password = '=1 OR '1'='1';
测试三
请输入name:1' OR 请输入password:=1 OR '1'='1 登录成功
PreparedStatement方式
如何避免上述Statement方式的弊端呢?我们可以用PreparedStatement方式。
插入
下面演示在数据库stuinfo表中插入记录。
代码:
//StatementTest.java
import org.junit.Test;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;
public class StatementTest {
@Test
public void login() {
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取连接
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties prop = new Properties();
prop.load(is);
String driverClass = prop.getProperty("driverClass");
String url = prop.getProperty("url");
String user = prop.getProperty("user");
String password = prop.getProperty("password");
Class.forName(driverClass);
conn = DriverManager.getConnection(url, user, password);
//2.预编译sql语句,返回PreparedStatement实例
String sql = "insert into stuinfo(name, password) values(?, ?)";
ps = conn.prepareStatement(sql);
//3.填充占位符
Scanner scanner = new Scanner(System.in);
System.out.print("请输入name:");
String input_name = scanner.nextLine();
System.out.println(input_name);
System.out.print("请输入password:");
String input_password = scanner.nextLine();
System.out.println(input_password);
ps.setString(1, input_name);
ps.setString(2, input_password);
//4.执行操作
ps.execute();
System.out.println("插入成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
//5.资源关闭
try {
if (ps != null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试结果
请输入name:wang
请输入password:789
插入成功
数据库stuinfo表
改进代码
上述代码每次操作数据库都要读取配置文件,建立连接,操作,关闭连接。可以把这些方法封装到一个JDBCUtils类里。
//JDBCUtils.java
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JDBCUtils {
public static Connection getConnection() throws Exception {
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties prop = new Properties();
prop.load(is);
String driverClass = prop.getProperty("driverClass");
String url = prop.getProperty("url");
String user = prop.getProperty("user");
String password = prop.getProperty("password");
Class.forName(driverClass);
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
public static void closeResource(Connection conn, Statement st) {
try {
if (st!=null){
st.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
更新
更新数据库表中的某个字段的值。
代码:
//UpdateTest.java
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
public class UpdateTest {
@Test
public void update() {
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库的连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句,返回PreparedStatement实例
String sql = "update stuinfo set password = ? where name = ?";
ps = conn.prepareStatement(sql);
//3.填充占位符
ps.setString(1,"000");
ps.setString(2,"zhang");
//4.执行
ps.execute();
System.out.println("更新成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
//5.资源的关闭
JDBCUtils.closeResource(conn, ps);
}
}
}
测试结果
更新成功
数据库stuinfo表
改进代码
除了查询以外,不管是增加,修改,删除,都是对数据表的更新,而且都没有返回值,操作都大致相同,即定义一个SQL语句,然后返回PreparedStatement实例,填充占位符,执行。那么,我们可以把这三种操作用一个update方法来定义。
删除
//UpdateTest.java
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
public class UpdateTest {
public void update(String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
ps.execute();
System.out.println("执行完毕");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps);
}
}
@Test
public void test() {
String sql = "delete from stuinfo where name=? and password=?";
update(sql, "zhang", "000");
}
}
测试结果
执行完毕
数据库stuinfo表
查询
查询操作和增删改不一样,查询需要有返回,我们必须接收这个返回值。使用ResultSet接收。关闭资源时,ResultSet也需要关闭,所以我们再JDBCUtils类中再增加一个关闭资源的静态方法。
public static void closeResource(Connection conn, Statement st, ResultSet rs) {
closeResource(conn, st);
try {
if (rs!=null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//RetrieveTest.java
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class RetrieveTest {
@Test
public void test() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select name, password from stuinfo where name=?";
ps = conn.prepareStatement(sql);
ps.setString(1, "liu");
rs = ps.executeQuery();
while (rs.next()) {
String name = rs.getString(1);
String password = rs.getString(2);
System.out.println("name=" + name + ", password=" + password);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps, rs);
}
}
}
测试结果
name=liu, password=456
改进代码
在JDBC编程中有ORM编程思想,我们可以写出一个对于所有表更加通用的查询操作。这里篇幅已经很长了,在下一篇说。