JDBC中有三个主要用于编译的API,分别是Statement接口、PrepareStatement接口以及CallableStatement接口。
1、Statement接口编译
手动连接数据库,创建一个表
package com.wk.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.Test;
/*
* 使用jdbc执行sql语句
*
*/
public class JDBC2 {
private String user = "wk";
private String psw = "199645";
//连接数据库的url
private String url = "jdbc:mysql://localhost:3306/myfirst";
@Test
public void test1(){
Statement state = null; //com.sql.Statement
Connection conn = null;
//加载驱动
//发送sql语句
String sql = "create table teacher("+
"id int primary key auto_increment,"+
"name varchar(10),"+
"class varchar(20));";
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url,user,psw);
state = conn.createStatement();
//更新行数
int count = state.executeUpdate(sql);
System.out.println(count);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
if(state != null)
state.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(conn != null)
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
2、PrepareStatement预编译,该接口是Statement的子接口,在编译sql语句时用占位符?作为参数,在执行前设置参数。
这里使用JdbcUtil工具连接数据库,新增一条记录(可参考——-——JDBC技术)
package com.wk.jdbc;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Test;
import java.sql.PreparedStatement;
import com.wk.jdbc_util.JdbcUtil;
/*
* 使用预编译PrepareStatement执行sql语句
*
*/
public class JDBC4 {
@Test
public void test(){
Connection conn = null;
PreparedStatement pst = null;
try {
conn = JdbcUtil.getConnection();
String sql = "UPDATE STUDENT SET NAME = ? WHERE id = ?";
pst = conn.prepareStatement(sql);
//设置参数,参数索引从1开始
pst.setString(1, "张三");
pst.setInt(2, 1);
int count = pst.executeUpdate();
System.out.println(count);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JdbcUtil.close(conn, pst);
}
}
}
3、前两种是增删改操作,只返回一个更新记录的行数,如果用于查询则返回一个结果集封装在ResultSet对象中。查询一个表操作如下
package com.wk.jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.Test;
import com.wk.jdbc_util.JdbcUtil;
/*
* jdbc执行DQL查询语句
*
*/
public class JDBC3 {
@Test
public void test1(){
Connection conn = null;
Statement state = null;
ResultSet result = null;
try {
conn = JdbcUtil.getConnection();
state = conn.createStatement();
//查询语句
String sql = "select *from student";
//执行sql,返回数据保存在ResultSet对象中
result = state.executeQuery(sql);
while(result.next()){
//列索引取法
//注意这里的游标从1开始,代表表中的第一个字段,getXXX()获取相应数据类型
int id = result.getInt(1);
String name = result.getString(2);
int age = result.getInt(3);
String addr = result.getString(4);
System.out.println(id+"-"+name+"-"+"-"+age+"-"+addr);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
JdbcUtil.close(conn, state, result);
}
}
}
4、CallableStatement编译结果集。属于PrepareStatement接口的子接口,首先在数据库中创建一个存储过程(可参考——-——JDBC技术)
DELIMITER $
CREATE PROCEDURE insertAndQuery()
BEGIN
INSERT INTO student (NAME,age,address) VALUES ('李明',20,'英语教材');
SELECT *FROM student WHERE NAME='李明';
END $
然后在程序中只需要调用SQL语句“CALL insertAndQuery()”即可,这是无参数的存储过程,下面看一个带有输入输出参数的存储过程
DELIMITER $
CREATE PROCEDURE pro_findById(IN sid INT,OUT sname VARCHAR(20))
BEGIN
SELECT NAME INTO sname FROM student WHERE id=sid; -- 表示根据id找姓名
END$
使用CallableStatement来调用存储过程
package com.wk.jdbc;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Test;
import com.wk.jdbc_util.JdbcUtil;
/*
*
* 使用CallableStatement对象调用存储过程
*/
public class JDBC6 {
@Test
public void test(){
Connection conn = null;
CallableStatement cs = null;
try {
conn = JdbcUtil.getConnection();
//第一个占位符为输入参数,第二占位符为输出参数
String sql = "CALL pro_findById(?,?)";
cs = conn.prepareCall(sql);
//设置输入参数
cs.setInt(1, 2);
//注册输出参数
cs.registerOutParameter(2, java.sql.Types.VARCHAR);
//执行查询
cs.executeQuery();
//用变量接收输出参数的值,调用getXXX()方法,参数和占位符所在位置对应
String result = cs.getString(2);
System.out.println(result);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JdbcUtil.close(conn, cs);
}
}
}
5、Statement中的注入问题
这是一个用Statement查询的例子,当输入正确的用户名和密码会返回succeed,否则返回failed,与数据库比较没有该记录,所以返回的是failed
package com.wk.jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.Test;
import com.wk.jdbc_util.JdbcUtil;
/*
*
* Statement的注入问题
*/
public class JDBC5 {
private String name = "liming";
private String passward = "13456";
@Test
public void test(){
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
stat = conn.createStatement();
String sql = "SELECT *FROM admin WHERE userName='"+name+"' AND password='"+passward+"'";
rs = stat.executeQuery(sql);
if(rs.next()){
System.out.println("successed");
}else{
System.out.println("failed");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
if(rs!=null)
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JdbcUtil.close(conn, stat);
}
}
}
failed //返回值
如果输入一个已存在用户
private String name = "小明";
private String passward = "123456";
succeed//返回值
如果再次进行修改
private String name = "小明' OR 1=1 -- ";
private String passward = "3456";
succeed //密码错误依然可以验证成功
这就是Statement中的注入问题,因为“1=1”在sql中是恒成立的,并且后面内容被注释所以输入任何内容都可以访问数据库,所以应该选择PrepareStatement进行预编译比较安全