JDBC是一个访问各大数据库的接口,专为java程序访问数据库而出品,学习web开发,JDBC是必不可少的临门一脚,因为web系统基本都需要连接数据库。
1.原始jdbc访问数据库(没有封装过)
步骤:1.装载驱动程序 2.建立数据库连接 3.执行数据库语句 4.获取执行结果 5.清理环境
以下以book图书表的按类型查找类型为例子: import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; /** * Created by *** on 2017/10/22. */ public class OriginalJDBC { private final static String DRIVEFORNAME = "com.mysql.jdbc.Driver"; private final static String URL = "jdbc:mysql://localhost:3306/book"; private final static String USERNAME = "root"; private final static String PASSWORD = "1234"; /** * 查找type="{}"的数据 * * @return */ public List<Book> findByType(String type) { //初始化 Connection con = null; PreparedStatement pstmt = null; String sql = null; ResultSet rs = null; List<Book> bookList = new ArrayList<Book>(); try { //装载驱动程序 Class.forName(DRIVEFORNAME); //建立数据库连接 con = DriverManager.getConnection(URL, USERNAME, PASSWORD); //编写sql,参数用?代替 sql = "select * from bookta where type=?"; //获得语句对象,承载sql语句 pstmt = con.prepareStatement(sql); //设置参数 pstmt.setString(1, type); //执行sql语句 rs = pstmt.executeQuery(); //获取执行结果 while (rs.next()) { Book book = new Book(); //数据库记录->实体对象 book.setIsbn(rs.getString(1)); book.setTitle(rs.getString(2)); book.setType(rs.getString(3)); book.setPrice(rs.getDouble(4)); //对象加入集合 bookList.add(book); } } catch (Exception e) { e.printStackTrace(); } finally { //finally代码块特点:抛出异常也会运行 //负责关闭资源,先申请的资源的后关闭,此处代码有误,应该先关闭rs对象 try { if (con != null) { con.close(); } if (pstmt != null) { pstmt.close(); } if (rs != null) { rs.close(); } } catch (Exception e1) { //ignore } return bookList; } } }
亮点:(1).清理环境(关闭资源),无论是jdbc的哪一步骤出现了异常,代码总能使用finally代码块将已申请的资源释放掉,假如系统使用过程中出现了bug,就不会产生连接等资源的堆积,造成系统性能变差,适合在上线时候使用。
(2)与一步骤一try catch(写上通俗易懂的异常提醒)相比,此代码块将步骤全都放在一个try catch块内,异常提示能力虽然有待提高,但资源做到了创建一个关闭一个,一步骤一try catch并没有去关闭异常出现前的创建好的资源,一旦此处出现异常,数据访问一多,系统未释放的资源数变多,性能就变差,当然,一般是在开发阶段使用,服务器关闭资源就被释放了,不会出现这样情况。
测试:
public class Test { public static void main(String[] args) { //测试 OriginalJDBC originalJDBC=new OriginalJDBC(); List<Book> bookList =originalJDBC.findByType("文学"); //遍历 for(Book book:bookList){ System.out.println(book.getTitle()+"这本书价格为"+book.getPrice()); } } }
2.sql注入
sql注入是使用拼接sql语句造成的一种数据库漏洞,“不法分子”改变sql语义来获取数据库信息,比如登录验证,select * from user where username="张三" and password="123" 种的username前台填成张三' or 1=1;-- ,这样下来or 1=1成了永真式,--后的密码成了注释,于是结果用户登录成功。
解决方案:使用preparedstatement,创建这个语句对象时需要传递sql语句,此时已经确定了sql语句,也不需要为String类型的字段加上单引号,相比statement更方便和安全。
程序:
/** * 测试sql注入 */ public List<Book> findByTypeSQLInsert(String type) { //初始化 Connection con = null; Statement statement = null; String sql = null; ResultSet rs = null; List<Book> bookList = new ArrayList<Book>(); try { //装载驱动程序 Class.forName(DRIVEFORNAME); //建立数据库连接 con = DriverManager.getConnection(URL, USERNAME, PASSWORD); //编写sql sql = "select * from bookta where type='"+type+"'"; //获得语句对象 statement = con.createStatement(); //执行sql语句 rs = statement.executeQuery(sql); //获取执行结果 while (rs.next()) { Book book = new Book(); //数据库记录->实体对象 book.setIsbn(rs.getString(1)); book.setTitle(rs.getString(2)); book.setType(rs.getString(3)); book.setPrice(rs.getDouble(4)); //对象加入集合 bookList.add(book); } } catch (Exception e) { e.printStackTrace(); } finally { //finally代码块特点:抛出异常也会运行 //负责关闭资源,先申请的资源的后关闭,此处代码有误,应该先关闭rs对象 try { if (con != null) { con.close(); } if (statement != null) { statement.close(); } if (rs != null) { rs.close(); } } catch (Exception e1) { //ignore } return bookList; } }
在此程序中使用statement对象,测试语句段如下:
//测试sql注入 System.out.println("测试sql注入"); //测试sql注入,数据库中其实没有type="爱情"依然能查询数据库 //select * from bookta where type='爱情' or 1=1,-- '; bookList=originalJDBC.findByTypeSQLInsert("爱情' or 1=1;-- "); for (int i = 0; i < bookList.size(); i++) { System.out.println(bookList.get(i).getTitle() + "价格为" + bookList.get(i).getPrice()); }
该程序仍能查询所有的书籍信息,网络上还有很多这样的sql注入,所以,开发系统时一定要使用preparedstatement对象,且要杜绝提供客户端删除表、限制删除记录等权限。