JDBC要点总结、SQL注入示例(Statement和PreparedStatement)

写在前面:JDBC是sun公司(已被Oracle收购)制定一系列接口标准,由不同厂商(Oracle、MySQL等)实现接口方法并封装成驱动文件,供开发人员操作数据库。也就是说,开发人员可采用统一的代码来操作不同的数据库,而无需关注驱动文件的内部实现。想探究这一原理的设计思想,看这里:《JDBC模型—深入理解JDBC设计思想(探究Class.forName("DBDriver"))》

一、三个重要对象:

   a.Connection

  代表着Java程序与数据库建立的连接。

   b.Statement

  代表SQL发送器,用于发送和执行SQL语句。

   c.ResultSet

  代表封装的数据库返回的结果集,用于获取查询结果。



二、编程步骤:

         1、加载驱动(需要事先将驱动程序对应的jar文件放到classpath对应的路径)

         驱动:就是不同的数据库厂商实现的API。Class.forName(驱动类名);

         2、获得连接。Connection conn = DriverManager.getConnection(url,user,pwd);

         3、获得Statement。Statment stat = conn.creatStatement();

         4、执行SQL。

                   a.执行查询:ResultSet rst = stat.executeQuery(SQL);

                   b.执行删除、更新、插入:int = stat.executeUpdate(SQL);

         5、如果是查询,需要遍历ResultSet

                   遍历:将查询的结果一条条取出来,获取其中的数据

         6、关闭连接

三、示例:

mysql简单使用:

(1)登录MySQL(使用root用户):mysql -uroot;

(2)查看数据库:show databases;

(3)创建数据库(设置缺省字符集utf-8):create database test1 default character set utf8;

(4)使用某个数据库:use test1;

(5)查看当前数据库有哪些表:show tables;

(6)建表:

         create table t_user(

                   id int primary key auto_increment,

                   username varchar(50),

                   pwd varchar(30),

                   age int

         )type=innodb;

         insert into t_user(username,pwd,age)values('jetty','test',23);

         注意:

         auto_increment:自增长列,该列由数据库自动赋值,一般用于主键生成

         type=innodb;表示该表支持事务(常用于大型数据库)


程序访问:

public class TestJdbc {

	static String driver = "com.mysql.jdbc.Driver";
	static String url = "jdbc:mysql://localhost:3306/test1";
	static String dbUser = "root";
	static String dbPwd = "111111";

	public static void main(String[] args) throws Exception {
		// findUsers1("jetty", "test");
		findUsers1("sadfas", "1' or '1'='1");//错误验证依然可以获取用户信息,出现SQL注入
		findUsers2("tom", "tompwd");
	}

	/**
	 * 使用原始Statement(容易出现SQL注入)
	 * 
	 * @param username
	 * @param pwd
	 * @throws Exception
	 */
	public static void findUsers1(String username, String pwd) throws Exception {
		// 1.加载类
		Class.forName(driver);
		// 2.获取连接
		Connection conn = DriverManager.getConnection(url, dbUser, dbPwd);
		System.out.println(conn);
		// 3.获得Statement
		Statement state = conn.createStatement();
		// 4.调用Statement的方法执行SQL
		String sql = "select * from t_user where username = " + "'" + username
				+ "'" + " and pwd = '" + pwd + "'";
		// 5.execute()将SQL语句发送给数据库,数据库执行查询,数据库执行相应查询,查询的结果会封装到ResultSet对象。
		ResultSet rst = state.executeQuery(sql);
		// 可以将ResultSet看成一张表,next()含义是,将指针向下移动一位,如果返回值为true,表示当前有记录可以读取
		while (rst.next()) {
			int id = rst.getInt("id");
			String name = rst.getString("username");
			String realPwd = rst.getString("pwd");
			int age = rst.getInt("age");
			System.out.println("id:" + id + ",username:" + name + ",realPwd:"
					+ realPwd + ",age:" + age);
		}
		// 6.关闭资源,一般只需关闭连接即可
		if (rst != null) {
			rst.close();
		}
		if (state != null) {
			state.close();
		}
		if (conn != null) {
			conn.close();
		}
	}

	/**
	 * 使用PreparedStatement
	 * 
	 * @param username
	 * @param pwd
	 * @throws Exception
	 */
	public static void findUsers2(String username, String pwd) throws Exception {
		Class.forName(driver);
		Connection conn = DriverManager.getConnection(url, dbUser, dbPwd);
		// sql表示一个占位符
		String sql = "select * from t_user where username=? and pwd=?";
		PreparedStatement prep = conn.prepareStatement(sql);
		prep.setString(1, username);
		prep.setString(2, pwd);
		ResultSet rst = prep.executeQuery();
		while (rst.next()) {
			int id = rst.getInt("id");
			int age = rst.getInt("age");
			System.out.println("id:" + id + ",age:" + age);
		}
		// 6.关闭资源,一般只需关闭连接即可
		if (rst != null) {
			rst.close();
		}
		if (prep != null) {
			prep.close();
		}
		if (conn != null) {
			conn.close();
		}
	}

}

运行结果:

com.mysql.jdbc.JDBC4Connection@c2e1f26
id:1,username:jetty,realPwd:test,age:23
id:2,username:tom,realPwd:tompwd,age:26
id:2,age:26

四、Statement和PreparedStatement

PrepareStatement和Statement区别和联系
--PrepareStatement从Statement继承
--都是用于发送和执行SQL语句的
--PrepareStatement是一种预编译的Statement对象。
  Statement对象是在executeUpdate或executeQuery方法时指定sql,此时将sql语句发送和执行。
  PrepareStatement对象是在创建时指定并发送sql,在executeUpdate或executeQuery方法时触发sql执行。

使用原有Statement有以下问题:
--容易遭受注入式攻击
--拼写SQL繁琐和麻烦
通过PrepareStatement可以解决上述问题

PrepareStatement使用步骤:
--编写带?号的sql
--利用con.prepareStatement(sql);方法获取PrepareStatement对象
--利用setXXX()方法给sql的?设置参数值
--调用无参的executeUpdate()或executeQuery()执行sql.

图示:


总结:如图所示,PrepareStatement是一个预编译的Statement,将带占位符?的SQL语句发送给数据库后,SQL语句不会立即执行,数据库会生成一个执行计划,此时SQL语句结构已确定,不可更改注入,然后利用setXXX()方法给sql的?设置参数值,传参后即执行计划,返回结果集。另外,由于执行计划已生成,只要传入参数就可执行计划,这在大批量存入数据时,编码更简单,效率更高。


五、JDBC批处理的使用

 用于处理批量的增删改操作。可一次向数据库端发送多条SQL命令,数据库可以对这些SQL命令批量编译执行。
从而提升了批量操作的性能。
  1)Statement
   Statement stat = ...;
   //将sql放入当前的SQL批次中
   stat.addBatch(sql);
   stat.addBatch(sql1);
   ....
   //发送和执行batch中的SQL
   stat.executeBatch();
  *2)PrepareStatement
   //发送指定sql语句给数据库
   //数据库进行预编译
   PrepareStatement pst = ...;
  pst.setxxx();//设置?参数值
   //将当前一组参数放入batch缓存
  pst.addBatch();
  pst.setxxx();
  pst.addBatch();//将参数放入batch
  //执行批处理
  pst.executeBatch();

  提示:batch中存放的sql语句或操作数不要太多,需要根据机器性能决定,一般采用20个即可。

  示例:

public void saveUsers(List<User> users) throws Exception {
		try {
			conn = DBUtil.getConnection();
			stat = conn
					.prepareStatement("insert into t_user(username,pwd,age)values(?,?,?)");
			for (int i = 0; i < users.size(); i++) {
				User user = users.get(i);
				stat.setString(1, user.getUsername());
				stat.setString(2, user.getPwd());
				stat.setInt(3, user.getAge());
				// stat.executeUpdate();
				stat.addBatch();// 参数先不发送,先保存下来
			}
			stat.executeBatch();// 一次性发送所有保存的参数到数据库执行
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			DBUtil.close(conn);
		}
}

常用方法:

addBatch():添加一条要执行的sql。

executeBatch():执行之前添加的所有sql。

clearBatch():清空之前缓存过的sql内容。(可不写,会自动清空)


转载请注明出处:http://blog.csdn.net/daijin888888/article/details/50965232

  

  • 8
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值