预编译语句对象PreparedStatement
之前的代码存在的问题
观察之前写的JDBC代码
//保存操作
@Override
public void save(Student student) {
String sql = "INSERT INTO t_student (sname, age)" + "VALUES ('" + student.getSname() + "', " + student.getAge() + ")";
...
}
//删除操作
@Override
public void delete(Long id) {
String sql = "DELETE FROM t_student WHERE id=" + id;
...
}
//更改数据
@Override
public void update(Long id, Student student) {
String sql = "UPDATE t_student SET sname='" + student.getSname() + "', age=" + student.getAge() + " WHERE id=" + id;
...
}
//查询操作
@Override
public Student get(Long id) {
String sql = "SELECT * FROM t_student WHERE id=" + id;
...
}
存在的问题: 拼接SQL语句太麻烦, 传递参数不方便
解决: 使用预编译语句对象, 即PreparedStatement
PreparedStatement
-
以前的JDBC步骤
- 加载注册驱动
- 获取连接对象
- 创建语句对象
- 执行SQL语句
- 释放资源
-
使用预编译语句对象
- 加载注册驱动
- 获取连接对象
- 创建预编译语句对象(或者说"准备语句对象")
- 执行SQL语句
- 释放资源
也就是说, 与之前相比, 只有创建语句对象这一步有不同
-
编写SQL语句, 需要传递的参数用"?"表示, "?"表示一个占位符参数
-
创建预编译语句对象(准备语句对象), 以第一步的sql为参数.
- 创建
Statement
对象时是不需要参数的, 这个很好理解: - 使用
Statement
对象时, SQL语句被写死, 只用在执行的时候再传入SQL - 而
PreparedStatement
对象的SQL语句中, 参数可能会变化, 所以需要预先传入SQL语句, 然后给占位符参数设置具体的值
- 创建
-
通过
setXxx(arg0, arg1)
给占位符参数设置具体的值- Xxx表示参数的基本数据类型(一切情况下均可以使用
setObject()
) - arg0表示是第几个占位符参数
- arg1表示具体的值
- Xxx表示参数的基本数据类型(一切情况下均可以使用
上面的步骤没看懂就算了, 下面这段代码可以很容易看懂Statement
和PrepareStatement
的区别
/**
*向user表中插入"Superman"和"Batman"两条数据
*/
public class PreparedStatementTest {
//使用Statement
@Test
public void saveByStatement() throws Exception{
//获取连接对象
Connection conn = JdbcUtil.getConnection();
//编写Superman的SQL, 此时SQL是被写死的
String sql_s = "INSERT INTO `user` (sname, age) VALUES ('Superman', 27)";
//编写Batman的SQL, 此时SQL是被写死的
String sql_b = "INSERT INTO `user` (sname, age) VALUES ('Batman', 35)";
//创建语句对象
Statement st = conn.createStatement();
//执行SQL
st.execute(sql_s);
st.execute(sql_b);
//释放资源
JdbcUtil.close(conn, st, null);
}
//使用PreparedStatement
@Test
public void saveByPreparedStatement() throws SQLException{
//获取连接对象
Connection conn = JdbcUtil.getConnection();
//编写SQL, 此时SQL中的参数由?占位
String sql = "INSERT INTO `user` (sname, age) VALUES (?, ?)";
//创建PreparedStatement对象
//注意是conn.prepareStatement(sql), 而不是conn.preparedStatement(sql);
PreparedStatement ps = conn.prepareStatement(sql);
//给第1个?设置值"Superman"
ps.setString(1, "superman");
//给第2个?设置值27
ps.setInt(2, 27);
//执行SQL, 此时不需要传递参数
ps.executeUpdate();
//-------------------------------------
//给第1个?设置值"Batman"
ps.setString(1, "Batman");
//给第2个?设置值35
ps.setInt(2, 35);
//执行SQL, 此时不需要传递参数
ps.executeUpdate();
//释放资源
JdbcUtil.close(conn, ps, null);
}
}
PreparedStatement
和Statement
区别简而言之:
- Statement
- 加载注册驱动 --> Class.forName("")
- 获取连接对象 --> ManagerDriver.getConncetion("", “”, “”);
- 创建语句对象 --> conn.createStatement();
- 执行SQL语句 --> statement.execute(String sql);
- 释放资源
- PreparedStatement
- 加载注册驱动 --> Class.forName("")
- 获取连接对象 --> ManagerDriver.getConncetion("", “”, “”);
- 创建语句对象 --> conn.prepareStatement(String sql);
- 执行SQL语句 --> statement.execute();
- 释放资源
获取自动生成的主键
Statement
Statement st = conn.createStatement();
//除了传入SQL语句, 还需要传入Statement.RETURN_GENERATED_KEYS
st.executeUpdate(String sql, Statement.RETURN_GENERATED_KEYS);
//使用getGeneratedKeys()方法获取自动生成的主键
ResultSet rs = st.getGeneratedKeys();
PreparedStatement
//除了传入SQL语句, 还需要传入Statement.RETURN_GENERATED_KEYS
PreparedStatement ps = conn.prepareStatement(String sql, Statement.RETURN_GENERATED_KEYS);
ps.executeUpdate();
//使用getGeneratedKeys()方法获取自动生成的主键
ResultSet rs = ps.getGeneratedKeys();
总结: 什么时候传入SQL语句, 就什么时候传入Statement.RETURN_GENERATED_KEYS
, 最后通过getGeneratedKeys()
方法获取自动生成的主键