本案例要求使用JDBC向Emp表中插入和更新数据,详细要求如下:
1.向Emp表中插入一条记录。其中为列empno、 ename、 job、 mgr、hiredate、 sal、 comm、deptno的数据分别为1001、"rose"、 "Analyst"、 7901、 "2014-05-01", 3000.00、500.00、10。
2.更新职员ID为1001的薪资为4500。
Statement对象提供了executeUpdate方法,该方法可以执行指定的sql语句,该语句可以是insert、update、delete。应用代码如下:
int result = stmt.executeUpdate(sql);
另外,我们在设计add方法时,该方法的参数是Emp类型,方法的声明如下:
public void add(Emp emp) {}
之所以把Emp类作为add方法的参数,是因为我们要保存的职员数据在Emp表的有8个字段,也就是说有9项内容需要存入数据中。如果不用Emp类型作为add方法的参数类型,那么add方法将有8个参数,造成参数过多。对于数据库中的表来说8个字段不算多,在企业中做项目的时候可能会有几十个字段的情况。所以使用对象封装方法参数是十分有必要的。另外,update方法的设计与add方法的设计类似。
Emp类是数据库表Emp和Java实体类之间的映射,创建该类遵守以下规则:
1.如果类的成员变量的名字是xxx,那么为了更改或获取成员变量的值,即更改或获取属性的值,在类中可以使用getter和setter方法,方法的命名如下:
2.对于boolean类型的成员变量,即布尔逻辑类型的属性,允许使用"is"代替上述的"get"和"set"。
3.getter和setter方法须为public的。
4.类中如果有构造方法,那么这个构造方法为public的并且是无参数的。
实现此案例需要按照如下步骤进行。
步骤一:创建Emp类
创建Emp类,该类为数据库表Emp与实体类之间的映射,代码如下所示:
public class Emp { private int empNo; private String ename; private String job; private int mgr; private String hiredate; private double sal; private double comm; private int deptno; public Emp() { super(); } public Emp(int empNo, String ename, String job, int mgr, String hiredate, double sal, double comm, int deptno) { super(); this.empNo = empNo; this.ename= ename; this.job = job; this.mgr = mgr; this.hiredate = hiredate; this.sal = sal; this.comm = comm; this.deptno = deptno; } public int getEmpNo() { return empNo; } public void setEmpNo(int empNo) { this.empNo = empNo; } public String getEname() { return ename; } public void setEname(String ename) { this.ename = ename; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public int getMgr() { return mgr; } public void setMgr(int mgr) { this.mgr = mgr; } public String getHiredate() { return hiredate; } public void setHiredate(String hiredate) { this.hiredate = hiredate; } public double getSal() { return sal; } public void setSal(double sal) { this.sal = sal; } public double getComm() { return comm; } public void setComm(double comm) { this.comm = comm; } public int getDeptno() { return deptno; } public void setDeptno(int deptno) { this.deptno = deptno; } }
步骤二:在EmpDAO类中添加add方法
在EmpDAO类中添加add方法,用于实现向数据库的Emp表中添加数据,代码如下所示:
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class EmpDAO { public static void main(String[] args) { // 1.select EmpDAO dao = new EmpDAO(); dao.findAll(); } public void findAll() { ... ... } #cold_bold public void add(Emp emp) { #cold_bold } }
步骤三:拼写insert语句
在add方法中定义insert语句,代码如下所示:
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class EmpDAO { public static void main(String[] args) { // 1.select EmpDAO dao = new EmpDAO(); dao.findAll(); } public void findAll() { ... ... } public void add(Emp emp) { #cold_bold String sql = "insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values(" #cold_bold + emp.getEmpNo() #cold_bold + ", " #cold_bold + " '" #cold_bold + emp.getEname() #cold_bold + "', " #cold_bold + " '" #cold_bold + emp.getJob() #cold_bold + "', " #cold_bold + emp.getMgr() #cold_bold + "," #cold_bold + "to_date('" #cold_bold + emp.getHiredate() #cold_bold + "','yyyy-mm-dd'), " #cold_bold + emp.getSal() #cold_bold + ", " #cold_bold + emp.getComm() + ", " + emp.getDeptno() + ")"; } }
步骤四:执行插入语句
首先创建数据库连接;然后通过连接创建Statement对象;最后使用Statement对象的updateExecute方法,执行插入语句并处理异常,代码如下所示:
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class EmpDAO { public static void main(String[] args) { // 1.select EmpDAO dao = new EmpDAO(); dao.findAll(); } public void findAll() { ... ... } public void add(Emp emp) { #cold_bold Connection con = null; #cold_bold Statement stmt = null; #cold_bold int flag = -1; String sql = "insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values(" + emp.getEmpNo() + ", " + " '" + emp.getEname() + "', " + " '" + emp.getJob() + "', " + emp.getMgr() + "," + "to_date('" + emp.getHiredate() + "','yyyy-mm-dd'), " + emp.getSal() + ", " + emp.getComm() + ", " + emp.getDeptno() + ")"; #cold_bold try { #cold_bold con = ConnectionSource.getConnection(); #cold_bold stmt = con.createStatement(); #cold_bold #cold_bold flag = stmt.executeUpdate(sql); #cold_bold if (flag > 0) { #cold_bold System.out.println("新增记录成功!"); #cold_bold } #cold_bold } catch (SQLException e) { #cold_bold System.out.println("数据库访问异常!"); #cold_bold throw new RuntimeException(e); #cold_bold } finally { #cold_bold try { #cold_bold if (stmt != null) { #cold_bold stmt.close(); #cold_bold } #cold_bold if (con != null) { #cold_bold con.close(); #cold_bold } #cold_bold } catch (SQLException e) { #cold_boldSystem.out.println("释放资源发生异常"); #cold_bold } #cold_bold } } }
步骤五:测试插入数据是否成功
在EmpDAO类的main方法中,调用add方法,代码如下所示:
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class EmpDAO { public static void main(String[] args) { // 1.select EmpDAO dao = new EmpDAO(); //dao.findAll(); // 2.insert #cold_bold Emp emp = new Emp(1001, "rose", "Analyst", 7901, "2014-05-01", 3000.00, #cold_bold 500.00, 10); #cold_bold dao.add(emp); } public void findAll() { ... ... } public void add(Emp emp) { Connection con = null; Statement stmt = null; int flag = -1; String sql = "insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values(" + emp.getEmpNo() + ", " + " '" + emp.getEname() + "', " + " '" + emp.getJob() + "', " + emp.getMgr() + "," + "to_date('" + emp.getHiredate() + "','yyyy-mm-dd'), " + emp.getSal() + ", " + emp.getComm() + ", " + emp.getDeptno() + ")"; try { con = ConnectionSource.getConnection(); stmt = con.createStatement(); flag = stmt.executeUpdate(sql); if (flag > 0) { System.out.println("新增记录成功!"); } } catch (SQLException e) { System.out.println("数据库访问异常!"); throw new RuntimeException(e); } finally { try { if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (SQLException e) { System.out.println("释放资源发生异常"); } } } }
运行上述代码,数据库Emp表中记录如图-1所示:
图-1
通过上述运行结果,会发现在数据库Emp表中添加了一条职员ID为1001的记录。
步骤六:对Emp表中的数据执行更新
在EmpDAO类中,添加update方法,在方法实现将员工ID为1001的薪资更新为4500,代码如下所示:
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; publicclass EmpDAO { publicstaticvoid main(String[] args) { // 1.select EmpDAO dao = new EmpDAO(); dao.findAll(); // 2.insert Emp emp = new Emp(1001, "rose", "Analyst", 7901, "2014-05-01", 3000.00, 500.00, 10); dao.add(emp); } publicvoid findAll() { ... ... } publicvoid add(Emp emp) { ... ... } #cold_bold #cold_bold publicvoid update(Emp emp) { #cold_bold Connection con = null; #cold_bold Statement stmt = null; #cold_bold int flag = -1; #cold_bold String sql = "update emp set sal = " + emp.getSal() + "," + " comm = " #cold_bold + emp.getComm() + " where empno = " + emp.getEmpNo(); #cold_bold #cold_bold try { #cold_bold con = ConnectionSource.getConnection(); #cold_bold stmt = con.createStatement(); #cold_bold #cold_bold flag = stmt.executeUpdate(sql); #cold_bold if (flag > 0) { #cold_bold System.out.println("更新记录成功!"); #cold_bold } #cold_bold } catch (SQLException e) { #cold_bold System.out.println("数据库访问异常!"); #cold_bold thrownew RuntimeException(e); #cold_bold } finally { #cold_bold try { #cold_bold if (stmt != null) { #cold_bold stmt.close(); #cold_bold } #cold_bold if (con != null) { #cold_bold con.close(); #cold_bold } #cold_bold } catch (SQLException e) { #cold_bold System.out.println("释放资源发生异常"); #cold_bold } #cold_bold } #cold_bold } }
步骤七:测试update方法更新数据是否成功
在EmpDAO的main方法中添加代码,调用update方法,代码如下所示:
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; publicclass EmpDAO { publicstaticvoid main(String[] args) { // 1.select EmpDAO dao = new EmpDAO(); //dao.findAll(); // 2.insert #cold_bold Emp emp = new Emp(1001, "rose", "Analyst", 7901, "2014-05-01", 4500.00, #cold_bold 500.00, 10); // dao.add(emp); // 3.update #cold_bold emp.setSal(4500.00); #cold_bold dao.update(emp); } publicvoid findAll() { ... ... } publicvoid add(Emp emp) { ... ... } publicvoid update(Emp emp) { ... ... } }
运行上述代码,数据库Emp表中记录如图-2所示:
图-2
从运行结果可以看出,职员ID为1001的薪资被更新为4500。
本案例中,类Emp完整代码如下所示:
public class Emp { private int empNo; private String ename; private String job; private int mgr; private String hiredate; private double sal; private double comm; private int deptno; public Emp() { super(); } public Emp(int empNo, String ename, String job, int mgr, String hiredate, double sal, double comm, int deptno) { super(); this.empNo = empNo; this.ename= ename; this.job = job; this.mgr = mgr; this.hiredate = hiredate; this.sal = sal; this.comm = comm; this.deptno = deptno; } public int getEmpNo() { return empNo; } public void setEmpNo(int empNo) { this.empNo = empNo; } public String getEname() { return ename; } public void setEname(String ename) { this.ename= ename; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public int getMgr() { return mgr; } public void setMgr(int mgr) { this.mgr = mgr; } public String getHiredate() { return hiredate; } public void setHiredate(String hiredate) { this.hiredate = hiredate; } public double getSal() { return sal; } public void setSal(double sal) { this.sal = sal; } public double getComm() { return comm; } public void setComm(double comm) { this.comm = comm; } public int getDeptno() { return deptno; } public void setDeptno(int deptno) { this.deptno = deptno; } }
EmpDAO类的完整代码如下所示:
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class EmpDAO { public static void main(String[] args) { // 1.select EmpDAO dao = new EmpDAO(); dao.findAll(); // 2.insert Emp emp = new Emp(1001, "rose", "Analyst", 7901, "2014-05-01", 4500.00, 500.00, 10); // dao.add(emp); // 3.update emp.setSal(4500.00); dao.update(emp); } public void findAll() { Connection con = null; Statement stmt = null; ResultSet rs = null; try { con = ConnectionSource.getConnection(); stmt = con.createStatement(); rs = stmt .executeQuery("select empno, ename, sal, hiredate from emp"); while (rs.next()) { System.out.println(rs.getInt("empno") + "," + rs.getString("ename") + "," + "," + rs.getDouble("sal") + "," + rs.getDate("hiredate")); } } catch (SQLException e) { System.out.println("数据库访问异常!"); throw new RuntimeException(e); } finally { try { if(rs!=null){ rs.close(); } if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (SQLException e) { System.out.println("释放资源发生异常"); } } } public void add(Emp emp) { Connection con = null; Statement stmt = null; int flag = -1; String sql = "insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values(" + emp.getEmpNo() + ", " + " '" + emp.getEname() + "', " + " '" + emp.getJob() + "', " + emp.getMgr() + "," + "to_date('" + emp.getHiredate() + "','yyyy-mm-dd'), " + emp.getSal() + ", " + emp.getComm() + ", " + emp.getDeptno() + ")"; try { con = ConnectionSource.getConnection(); stmt = con.createStatement(); flag = stmt.executeUpdate(sql); if (flag > 0) { System.out.println("新增记录成功!"); } } catch (SQLException e) { System.out.println("数据库访问异常!"); throw new RuntimeException(e); } finally { try { if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (SQLException e) { System.out.println("释放资源发生异常"); } } } public void update(Emp emp) { Connection con = null; Statement stmt = null; int flag = -1; String sql = "update emp set sal = " + emp.getSal() + "," + " comm = " + emp.getComm() + " where empno = " + emp.getEmpNo(); try { con = ConnectionSource.getConnection(); stmt = con.createStatement(); flag = stmt.executeUpdate(sql); if (flag > 0) { System.out.println("更新记录成功!"); } } catch (SQLException e) { System.out.println("数据库访问异常!"); throw new RuntimeException(e); } finally { try { if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (SQLException e) { System.out.println("释放资源发生异常"); } } } }
db.properties文件的内容与之前案例一样没有变化,该文件完整内容如下所示:
jdbc.driverClassName=oracle.jdbc.OracleDriver jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl jdbc.username=scott jdbc.password=tiger #<!-- 初始化连接 --> dataSource.initialSize=10 #<!-- 最大空闲连接 --> dataSource.maxIdle=20 #<!-- 最小空闲连接 --> dataSource.minIdle=5 #最大连接数量 dataSource.maxActive=50 #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 --> dataSource.maxWait=1000
ConnectionSource类与之前案例一样没有变化,该类完整内容如下所示:
import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import org.apache.commons.dbcp.BasicDataSource; public class ConnectionSource { private static BasicDataSource dataSource = null; public ConnectionSource() { } public static void init() { Properties dbProps = new Properties(); // 取配置文件可以根据实际的不同修改 try { dbProps.load(ConnectionSource.class.getClassLoader().getResourceAsStream( "day01/v4/db.properties")); } catch (IOException e) { e.printStackTrace(); } try { String driveClassName = dbProps.getProperty("jdbc.driverClassName"); String url = dbProps.getProperty("jdbc.url"); String username = dbProps.getProperty("jdbc.username"); String password = dbProps.getProperty("jdbc.password"); String initialSize = dbProps.getProperty("dataSource.initialSize"); String minIdle = dbProps.getProperty("dataSource.minIdle"); String maxIdle = dbProps.getProperty("dataSource.maxIdle"); String maxWait = dbProps.getProperty("dataSource.maxWait"); String maxActive = dbProps.getProperty("dataSource.maxActive"); dataSource = new BasicDataSource(); dataSource.setDriverClassName(driveClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); // 初始化连接数 if (initialSize != null) dataSource.setInitialSize(Integer.parseInt(initialSize)); // 最小空闲连接 if (minIdle != null) dataSource.setMinIdle(Integer.parseInt(minIdle)); // 最大空闲连接 if (maxIdle != null) dataSource.setMaxIdle(Integer.parseInt(maxIdle)); // 超时回收时间(以毫秒为单位) if (maxWait != null) dataSource.setMaxWait(Long.parseLong(maxWait)); // 最大连接数 if (maxActive != null) { if (!maxActive.trim().equals("0")) dataSource.setMaxActive(Integer.parseInt(maxActive)); } } catch (Exception e) { e.printStackTrace(); System.out.println("创建连接池失败!请检查设置!!!"); } } public static synchronized Connection getConnection() throws SQLException { if (dataSource == null) { init(); } Connection conn = null; if (dataSource != null) { conn = dataSource.getConnection(); } return conn; } }
Oracle数据库中用户表users的表结构如表-1所示:
表-1用户表users 信息
用户表 users中的示例数据,如图-3所示:
图-3
本案例详细要求如下:
1.使用Statement实现用户名和密码的验证功能,并测试用户名为“tarena”、密码为“tarena123”以及用户名为“goodman”、密码为“a' OR 'b'='b”是否能登录成功。
2.使用PreparedStatement实现用户名和密码的验证功能,并测试用户名为“goodman”、密码为“a' OR 'b'='b”是否能登录成功。
实现本案例的方案如下所示:
sql = "select * from users where username = '" + username + "' and password = '" + password+"'"; System.out.println(sql); con = ConnectionSource.getConnection(); stmt = con.createStatement(); rs = stmt.executeQuery(sql);
上述代码存在SQL注入的问题,可以使用PreparedStatement来解决SQL注入的问题。
2.使用PreparedStatement实现用户名和密码的验证功能。PreparedStatement是Statement的子类,表示预编译的SQL语句的对象。在使用PreparedStatement对象执行SQL命令时,命令被数据库编译和解析,并放入命令缓冲区。缓冲区中的预编译SQL命令可以重复使用。示例代码如下所示:
sql = "select * from users where username = ? and password = ?"; con = ConnectionSource.getConnection(); stmt = con.prepareStatement(sql); stmt.setString(1, username); stmt.setString(2, password); rs = stmt.executeQuery();
使用PreparedStatement来执行SQL语句。在SQL语句中有2个问号,在代码中要给它们分别设置值,规则是:从左到右,对应1,2,...。
3,关于SQL注入。对于JDBC而言,SQL注入攻击只对Statement有效,对PreparedStatement是无效的,这是因为PreparedStatement不允许在插入时改变查询的逻辑结构。如果有一条SQL语句为:
"select * from 表 where 用户名 = '用户名'"
Statement的SQL语句是这样写的:
"select * from 表 where 用户名 = '"+ 变量值 +"'"
而PreparedStatement的SQL语句是这样写的:
"select * from 表 where 用户名 = ?"
这样输入 "aa' or '1' = '1",使用Statement执行的SQL语句为:
"select * from 表 where 用户名 = 'aa' or '1' = '1'"
而使用PreparedStatement是将 "aa' or '1' = '1" 作为一个字符串赋值给问号“?”,使其作为"用户名"字段的对应值,这样来防止SQL注入。
实现机制不同,注入只对SQL语句的准备(编译)过程有破坏作用,而PreparedStatement已经准备好了,执行阶段只是把输入串作为数据处理,不再需要对SQL语句进行解析、准备,因此也就避免了SQL注入问题。
实现此案例需要按照如下步骤进行。
步骤一:创建users表,并插入示例数据
在Oracle数据库中,创建users表,并插入如图-3所示的示例数据,SQL语句如下所示:
create table users( id number(4), username varchar2(30), password varchar2(30) ); insert into users(id,username,password)values(1,'tarena','tarena123'); insert into users(id,username,password)values(2,'syl','syl001');
步骤二:使用Statement实现验证用户名密码是否存在
新建UserDAO类,在该类中添加login1方法,在该方法中使用Statement实现验证用户名密码是否存在的方法,代码如下所示:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; publicclass UserDAO { publicstaticvoid main(String[] args) { } publicvoid login1(String username, String password) { Connection con = null; Statement stmt = null; ResultSet rs = null; String sql = null; try { sql = "select * from users where username = '" + username + "' and password = '" + password+"'"; System.out.println(sql); con = ConnectionSource.getConnection(); stmt = con.createStatement(); rs = stmt.executeQuery(sql); if (rs.next()) { System.out.println("登录成功!"); } else { System.out.println("登录失败!"); } } catch (SQLException e) { System.out.println("数据库访问异常!"); thrownew RuntimeException(e); } finally { try { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (SQLException e) { System.out.println("释放资源发生异常"); } } } }
步骤三:使用用户名为“tarena”、密码为“tarena123”测试是否能成功登录
在UserDAO的main方法中,使用用户名为“tarena”、密码为“tarena123”作为login1方法的参数,测试是否能成功登录,代码如下所示:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; publicclass UserDAO { publicstaticvoid main(String[] args) { // login #cold_bold UserDAO dao = new UserDAO(); #cold_bold dao.login1("tarena", "tarena123"); } publicvoid login1(String username, String password) { Connection con = null; Statement stmt = null; ResultSet rs = null; String sql = null; try { sql = "select * from users where username = '" + username + "' and password = '" + password+"'"; System.out.println(sql); con = ConnectionSource.getConnection(); stmt = con.createStatement(); rs = stmt.executeQuery(sql); if (rs.next()) { System.out.println("登录成功!"); } else { System.out.println("登录失败!"); } } catch (SQLException e) { System.out.println("数据库访问异常!"); thrownew RuntimeException(e); } finally { try { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (SQLException e) { System.out.println("释放资源发生异常"); } } } }
运行UserDAO类,控制台输出结果如下:
select * from users where username = 'tarena' and password = 'tarena123' 登录成功!
从输出结果可以看出,使用用户名为“tarena”、密码为“tarena123”可以登录成功。由于用户名为“tarena”、密码为“tarena123”在数据库中是存在的数据,登录成功符合预期结果。另外,从输出的SQL语句可以看出,最终将变量信息替换为实际传入的参数信息了。
步骤四:测试login1方法
在UserDAO的main方法中,使用用户名为“goodman”、密码为“a' OR 'b'='b”作为login1方法的参数,测试是否能成功登录,代码如下所示:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; publicclass UserDAO { publicstaticvoid main(String[] args) { UserDAO dao = new UserDAO(); //dao.login1("tarena", "tarena123"); #cold_bold dao.login1("tarena", "a' OR 'b'='b"); } publicvoid login1(String username, String password) { Connection con = null; Statement stmt = null; ResultSet rs = null; String sql = null; try { sql = "select * from users where username = '" + username + "' and password = '" + password+"'"; System.out.println(sql); con = ConnectionSource.getConnection(); stmt = con.createStatement(); rs = stmt.executeQuery(sql); if (rs.next()) { System.out.println("登录成功!"); } else { System.out.println("登录失败!"); } } catch (SQLException e) { System.out.println("数据库访问异常!"); thrownew RuntimeException(e); } finally { try { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (SQLException e) { System.out.println("释放资源发生异常"); } } } }
运行UserDAO类,控制台输出结果如下:
select * from users where username = 'tarena' and password = 'a' OR 'b'='b' 登录成功!
从输出结果可以看出,使用用户名为“goodman”、密码为“a' OR 'b'='b”可以登录成功。但是用户名为“goodman”、密码为“a' OR 'b'='b”在数据库中是不存在的数据,登录成功不符合预期结果。问题出在哪里?
问题出在SQL语句上。查看上述控制台输出结果,可以看出SQL语句的where子句中使用or关键字连接两个表达式,即or关键字后边的表达式如果返回true,那么整个where条件的结果将是true。而or关键字后边的表达式为“'b'='b'”,该表达式的返回结果永远为true,因此,where条件的返回结果一定为true。这种现象在编程中称为SQL注入,是应该避免的编程方式。可以使用PreparedStatement来防止SQL 注入。
步骤五:使用PreparedStatement实现验证用户名密码功能
在UserDAO类中添加login方法,在该方法中使用PreparedStatement执行SQL语句,来实现验证用户名密码功能,代码如下所示:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; publicclass UserDAO { publicstaticvoid main(String[] args) { // login UserDAO dao = new UserDAO(); //dao.login1("tarena", "tarena123"); dao.login1("tarena", "a' OR 'b'='b"); } publicvoid login1(String username, String password) { ... ... } #cold_bold publicvoid login(String username, String password) { #cold_bold Connection con = null; #cold_bold PreparedStatement stmt = null; #cold_bold ResultSet rs = null; #cold_bold String sql = null; #cold_bold #cold_bold try { #cold_bold sql = "select * from users where username = ? and password = ?"; #cold_bold con = ConnectionSource.getConnection(); #cold_bold stmt = con.prepareStatement(sql); #cold_bold stmt.setString(1, username); #cold_bold stmt.setString(2, password); #cold_bold rs = stmt.executeQuery(); #cold_bold #cold_bold if (rs.next()) { #cold_bold System.out.println("登录成功!"); #cold_bold } else { #cold_bold System.out.println("登录失败!"); #cold_bold } #cold_bold } catch (SQLException e) { #cold_bold System.out.println("数据库访问异常!"); #cold_bold thrownew RuntimeException(e); #cold_bold } finally { #cold_bold try { #cold_bold if (rs != null) { #cold_bold rs.close(); #cold_bold } #cold_bold if (stmt != null) { #cold_bold stmt.close(); #cold_bold } #cold_bold if (con != null) { #cold_bold con.close(); #cold_bold } #cold_bold } catch (SQLException e) { #cold_bold System.out.println("释放资源发生异常"); #cold_bold } #cold_bold } #cold_bold } }
步骤六:测试login方法
在UserDAO的main方法中,使用用户名为“goodman”、密码为“a' OR 'b'='b”作为login方法的参数,测试是否能成功登录,代码如下所示:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; publicclass UserDAO { publicstaticvoid main(String[] args) { // login UserDAO dao = new UserDAO(); //dao.login1("tarena", "tarena123"); //dao.login1("tarena", "a' OR 'b'='b"); dao.login("tarena", "a' OR 'b'='b"); } publicvoid login1(String username, String password) { ... ... } publicvoid login(String username, String password) { Connection con = null; PreparedStatement stmt = null; ResultSet rs = null; String sql = null; try { sql = "select * from users where username = ? and password = ?"; con = ConnectionSource.getConnection(); stmt = con.prepareStatement(sql); stmt.setString(1, username); stmt.setString(2, password); rs = stmt.executeQuery(); if (rs.next()) { System.out.println("登录成功!"); } else { System.out.println("登录失败!"); } } catch (SQLException e) { System.out.println("数据库访问异常!"); thrownew RuntimeException(e); } finally { try { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (SQLException e) { System.out.println("释放资源发生异常"); } } } }
运行EmpDAO类,控制台输出结果如下所示:
登录失败!
从输出结果可以看出,登录失败。用户名为“goodman”、密码为“a' OR 'b'='b”的数据在数据库users表中是不存在的数据,登录失败符合测试的预期,有效的防止了SQL注入的问题。
本案例中,创建users表,并插入示例数据,SQL语句如下所示:
create table users( id number(4), username varchar2(30), password varchar2(30) ); insert into users(id,username,password)values(1,'tarena','tarena123'); insert into users(id,username,password)values(2,'syl','syl001');
UserDAO类的完整代码如下所示:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; public class UserDAO { public static void main(String[] args) { // login UserDAO dao = new UserDAO(); //dao.login1("tarena", "tarena123"); //dao.login1("tarena", "a' OR 'b'='b"); dao.login("tarena", "a' OR 'b'='b"); } public void login1(String username, String password) { Connection con = null; Statement stmt = null; ResultSet rs = null; String sql = null; try { sql = "select * from users where username = '" + username + "' and password = '" + password+"'"; System.out.println(sql); con = ConnectionSource.getConnection(); stmt = con.createStatement(); rs = stmt.executeQuery(sql); if (rs.next()) { System.out.println("登录成功!"); } else { System.out.println("登录失败!"); } } catch (SQLException e) { System.out.println("数据库访问异常!"); throw new RuntimeException(e); } finally { try { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (SQLException e) { System.out.println("释放资源发生异常"); } } } public void login(String username, String password) { Connection con = null; PreparedStatement stmt = null; ResultSet rs = null; String sql = null; try { sql = "select * from users where username = ? and password = ?"; con = ConnectionSource.getConnection(); stmt = con.prepareStatement(sql); stmt.setString(1, username); stmt.setString(2, password); rs = stmt.executeQuery(); if (rs.next()) { System.out.println("登录成功!"); } else { System.out.println("登录失败!"); } } catch (SQLException e) { System.out.println("数据库访问异常!"); throw new RuntimeException(e); } finally { try { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (SQLException e) { System.out.println("释放资源发生异常"); } } } }
db.properties文件的内容与之前案例一样没有变化,该文件完整内容如下所示:
jdbc.driverClassName=oracle.jdbc.OracleDriver jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl jdbc.username=scott jdbc.password=tiger #<!-- 初始化连接 --> dataSource.initialSize=10 #<!-- 最大空闲连接 --> dataSource.maxIdle=20 #<!-- 最小空闲连接 --> dataSource.minIdle=5 #最大连接数量 dataSource.maxActive=50 #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 --> dataSource.maxWait=1000
ConnectionSource类与之前案例一样没有变化,该类完整内容如下所示:
import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import org.apache.commons.dbcp.BasicDataSource; public class ConnectionSource { private static BasicDataSource dataSource = null; public ConnectionSource() { } public static void init() { Properties dbProps = new Properties(); // 取配置文件可以根据实际的不同修改 try { dbProps.load(ConnectionSource.class.getClassLoader().getResourceAsStream( "day01/v4/db.properties")); } catch (IOException e) { e.printStackTrace(); } try { String driveClassName = dbProps.getProperty("jdbc.driverClassName"); String url = dbProps.getProperty("jdbc.url"); String username = dbProps.getProperty("jdbc.username"); String password = dbProps.getProperty("jdbc.password"); String initialSize = dbProps.getProperty("dataSource.initialSize"); String minIdle = dbProps.getProperty("dataSource.minIdle"); String maxIdle = dbProps.getProperty("dataSource.maxIdle"); String maxWait = dbProps.getProperty("dataSource.maxWait"); String maxActive = dbProps.getProperty("dataSource.maxActive"); dataSource = new BasicDataSource(); dataSource.setDriverClassName(driveClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); // 初始化连接数 if (initialSize != null) dataSource.setInitialSize(Integer.parseInt(initialSize)); // 最小空闲连接 if (minIdle != null) dataSource.setMinIdle(Integer.parseInt(minIdle)); // 最大空闲连接 if (maxIdle != null) dataSource.setMaxIdle(Integer.parseInt(maxIdle)); // 超时回收时间(以毫秒为单位) if (maxWait != null) dataSource.setMaxWait(Long.parseLong(maxWait)); // 最大连接数 if (maxActive != null) { if (!maxActive.trim().equals("0")) dataSource.setMaxActive(Integer.parseInt(maxActive)); } } catch (Exception e) { e.printStackTrace(); System.out.println("创建连接池失败!请检查设置!!!"); } } public static synchronized Connection getConnection() throws SQLException { if (dataSource == null) { init(); } Connection conn = null; if (dataSource != null) { conn = dataSource.getConnection(); } return conn; } }