五、SQL注入
5.1什么是SQL注入
所谓 SQL 注入,就是通过把含有 SQL 语句片段的参数插入到需要执行的 SQL 语句中,最终达到欺骗数据库服务器执行恶意操作的 SQL 命令。
5.2SQL注入案例
statement容易产生sql注入是因为,它将参数和SQL语句组装后再交给MySQL编译。
package cn.it.bz.JDBC; import java.sql.*; //SQL注入测试类 public class TestSQLInjection { public void sqlInjection(String username,int userage){ Connection conn = null; Statement statement = null; ResultSet resultSet = null; try { conn = JdbcUtils.getConnection(); statement = conn.createStatement(); String sql = "select * from users where username ='"+username+"'and userage="+userage; System.out.println(sql); resultSet = statement.executeQuery(sql); while (resultSet.next()) { String name = resultSet.getString("username"); int age = resultSet.getInt("userage"); System.out.println(name + " "+age); } }catch (Exception e){ e.printStackTrace(); }finally { JdbcUtils.closeDQLResource(resultSet,statement,conn); } } public static void main(String[] args) throws SQLException { TestSQLInjection testSQLInjection = new TestSQLInjection(); testSQLInjection.sqlInjection("lisi' or 1=1 -- ",12); } }
5.3解决SQL注入
就是将statement换成PreparedStatement,使用PreparedStatement时数据库驱动会先对sql语句进行编译处理被数据库识别,然后再将参数绑定到已经编译完成的sql语句上,然后再去执行SQL语句。
六、JDBC批量添加数据
6.1批量添加数据简介
在JDBC中通过PreparedStatement的对象的addBatch()和executeBatch()方法进行数据的批量插入。
- addBatch()把若干SQL语句装载到一起,然后一次性传送到数据库执行,即是批量处理sql数据的。
- executeBatch()会将装载到一起的SQL语句执行。
注意:
MySql默认情况下是不开启批处理的。
数据库驱动从5.1.13开始添加了一个对rewriteBatchStatement的参数的处理,该参数能够让MySql开启批处理。在url中添加该参数:rewriteBatchedStatements=true
6.2Mysql的URL参数说明
useUnicode | [true | false] | 是否使用编码集,需配合 characterEncoding 参数使用。 |
characterEncoding | [utf-8 | gbk | ...] | 编码类型。 |
useSSL | [true | false] | 是否使用SSL协议。 |
rewriteBatchedStatements | [true | false] | 可以重写向数据库提交的SQL语句。 |
url = jdbc:mysql://localhost:3306/test?&useSSL=false&serverTimezone=UTC
6.3实现批量数据添加
在url中开启批量添加
url = jdbc:mysql://localhost:3306/test?&useSSL=false&rewriteBatchedStatements=true
6.3.1实现方式一
将数据放在缓存中这样进行一次交互就能将数据添加到数据库中。
package cn.it.bz.JDBC; import java.sql.Connection; import java.sql.PreparedStatement; public class TestAddBatch1 { public void addBatch1(){ Connection con = null; PreparedStatement ps = null; try { //创建连接 con = JdbcUtils.getConnection(); //创建PreparedStatement ps = con.prepareStatement("insert into users values(default,?,?)"); //参数绑定 for (int i = 0; i < 1000; i++) { //绑定username ps.setString(1,"zhangsan"+i); //绑定userage ps.setInt(2,20); //缓存数据,实现一次交互提高性能 ps.addBatch(); } //将1000个数据一次性添加到数据库中 ps.executeBatch(); }catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.closeDMLResource(ps,con); } } public static void main(String[] args) { TestAddBatch1 add = new TestAddBatch1(); add.addBatch1(); } }
6.3.2方式二
一次缓存大量的数据可能造成内存溢出的情况,需要分批将数据添加到数据库中。
if (i % 500 == 0) { ps.executeBatch(); //清除缓存 ps.clearBatch(); }
七、JDBC事务处理
事务简介
- 事务:
事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 - 事务操作流程:
- 开启事务
- 提交事务
- 回滚事务
JDBC中事务处理特点
在JDBC中,使用Connection对象来管理事务,默认为自动提交事务。可以通过setAutoCommit(boolean autoCommit)方法设置事务是否自动提交,参数为boolean类型,默认值为true,表示自动提交事务,如果值为false则表示不自动提交事务,需要通过commit方法手动提交事务或者通过rollback方法回滚事务。建议采用这种方式处理事务。
事务处理实现
package cn.it.bz.JDBC; import java.sql.Connection; import java.sql.PreparedStatement; public class TestAddBatch2 { /** * 批量添加数据方式二 * 支持事务处理 */ public void addBatch(){ Connection conn = null; PreparedStatement ps =null; try{ //创建连接 conn = JdbcUtils.getConnection(); //设置事务的提交方式,将自动提交修改为手动提交 conn.setAutoCommit(false); //创建PreparedStatement ps = conn.prepareStatement("insert into users values(default ,?,?)"); //参数绑定 for(int i=1;i<=1000;i++){ //绑定username ps.setString(1,"zhangsan"+i); //绑定userage ps.setInt(2,20); //缓存sql ps.addBatch(); if(i%500 == 0){ //执行sql ps.executeBatch(); //清除缓存 ps.clearBatch(); } if(i==501){ String str = null; str.length(); } } //提交事务 JdbcUtils.commit(conn); }catch(Exception e){ e.printStackTrace(); //出问题回滚 JdbcUtils.rollback(conn); }finally{ JdbcUtils.closeDMLResource(ps,conn); } } public static void main(String[] args) { TestAddBatch2 testAddBatch2 = new TestAddBatch2(); testAddBatch2.addBatch(); } }
八、模糊查询
package cn.it.bz.JDBC; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; //模糊查询 public class TestFuzzyQuery { //根据用户名字模糊查找用户信息 public List<Users> fuzzyQuery(String username){ List<Users> list = new ArrayList<>(); Connection con = null; PreparedStatement ps = null; ResultSet resultSet = null; try { //获取数据库连接 con = JdbcUtils.getConnection(); //创建PreparedStatement对象 ps = con.prepareStatement("select * from users where username like ?"); //参数绑定 ps.setString(1,username); resultSet = ps.executeQuery(); while (resultSet.next()) { Users user = new Users(); user.setUserid(resultSet.getInt("userid")); user.setUsername(resultSet.getString("username")); user.setUserage(resultSet.getInt("userage")); list.add(user); } }catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.closeDQLResource(resultSet,ps,con); } return list; } public static void main(String[] args) { TestFuzzyQuery testFuzzyQuery = new TestFuzzyQuery(); List<Users> usersList = testFuzzyQuery.fuzzyQuery("%张%"); for (Users users : usersList) { System.out.println(users); } } }
九、动态条件查询
package cn.it.bz.JDBC; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; //动态条件查询 public class TestDynamicConditionQuery { public List<Users> dynamicConditionQuery(Users users){ List<Users> list = new ArrayList<>(); Connection con = null; PreparedStatement ps = null; ResultSet resultSet = null; try { //获取数据库连接 con = JdbcUtils.getConnection(); //拼接查询SQL String sql = this.combineSQL(users); System.out.println(sql); //创建PreparedStatement对象 ps = con.prepareStatement(sql); resultSet = ps.executeQuery(); while (resultSet.next()) { Users user = new Users(); user.setUserid(resultSet.getInt("userid")); user.setUsername(resultSet.getString("username")); user.setUserage(resultSet.getInt("userage")); list.add(user); } }catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.closeDQLResource(resultSet,ps,con); } return list; } //SQL拼接 private String combineSQL(Users user){ StringBuilder stringBuilder = new StringBuilder("select * from users where 1=1 "); stringBuilder.append(user.getUserid() == 0?" ":" and userid="+user.getUserid()); stringBuilder.append(user.getUsername() == null||user.getUsername().equals("") ? " ":" and username= '"+user.getUsername()+"'"); stringBuilder.append(user.getUserage()==0?" ":" and userage = "+user.getUserage()); return stringBuilder.toString(); } public static void main(String[] args) { TestDynamicConditionQuery testDynamicConditionQuery = new TestDynamicConditionQuery(); List<Users> usersList = testDynamicConditionQuery.dynamicConditionQuery(new Users(0, "", 12)); for (Users users : usersList) { System.out.println(users); } } }
十、分页查询
10.1分页查询简介
当一个操作数据库进行查询的语句返回的结果集内容如果过多,那么内存极有可能溢出,所以在查询中含有大数据的情况下分页是必须的。
分页查询分类:
- 物理分页:
- 在数据库执行查询时(实现分页查询),查询需要的数据—依赖数据库的SQL语句
- 在SQL查询时,从数据库只检索分页需要的数据
- 通常不同的数据库有着不同的物理分页语句
- MySql物理分页采用limit关键字
- 逻辑分页:
- 在sql查询时,先从数据库检索出所有数据的结果集,在程序内,通过逻辑语句获得分页需要的数据
如何在MySql中实现物理分页查询
select * from tableName limit m,n
其中m与n为数字。n代表需要获取多少行的数据项,而m代表从哪开始(以0为起始)。
例如我们想从users表中先获取前两条数据SQL为:
select * from users limit 0,2;
那么如果要继续看下两条的数据则为:
select * from users limit 2,2;
以此类推
分页公式:(当前页-1)*每页大小;