Top

JAVA JDBC DAY02

  1. 更新和插入Emp数据
  2. 用户名密码验证功能

1 更新和插入Emp数据

1.1 问题

本案例要求使用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。

1.2 方案

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的并且是无参数的。

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建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。

1.4 完整代码

本案例中,类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;
	}
}

2 用户名密码验证功能

2.1 问题

Oracle数据库中用户表users的表结构如表-1所示:

表-1用户表users 信息

用户表 users中的示例数据,如图-3所示:

图-3

本案例详细要求如下:

1.使用Statement实现用户名和密码的验证功能,并测试用户名为“tarena”、密码为“tarena123”以及用户名为“goodman”、密码为“a' OR 'b'='b”是否能登录成功。

2.使用PreparedStatement实现用户名和密码的验证功能,并测试用户名为“goodman”、密码为“a' OR 'b'='b”是否能登录成功。

2.2 方案

实现本案例的方案如下所示:

			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注入问题。

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建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注入的问题。

2.4 完整代码

本案例中,创建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;
	}
}