JDBC (java语言连接数据库)是sun公司制定的一套接口
JDBC 代码编写的六步
1、注册驱动
2、获取连接
3、获取数据库操作对象
4、执行sql语句
5、处理查询结果集
6、释放资源
1、注册驱动
有两种方式:
//第一种 Driver driver=new com.mysql.jdbc.Driver(); DriverManager.registerDriver(driver); //第二种注册方式(常用) Class.forName("com.mysql.jdbc.Driver");
在编写JDBC的时候可以通过使用资源绑定器(xxx.properties) 绑定属性配置文件和工具类的创建去让代码看起来更简介方便。
将连接数据库的所有信息配置到文件当中xxx.properties,因为实际开发中不建议把连接数据库的信息 写死到java程序中
案例:模拟用户登录
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.*; /** * 实现功能 * 1. 需求:模拟用户登录功能的实现 * 2. 业务描述: * 程序运行的 时候提供一个输入的入口 让用户可以输入用户名和密码 * 用户输入后提交信息 java程序收集到用户信息 * java程序验证用户名和密码是否合法 * 合法:显示登录成功 * 不合法:显示登录失败 * 3. 数据的准备 * 实际开发中表的设计会使用专业的建模工具 * +----+----------+-----------+----------+ * | id | loginPwd | loginName | realName | * +----+----------+-----------+----------+ * | 1 | 111 | 1 | 1 | * | 2 | 222 | 2 | 2 | * +----+----------+-----------+----------+ * 4.当前程序存在的问题 * 用户名:fdsa * 密码:fdsa' or '1'='1 * 登录成功! * 这种现象叫做SQL注入(安全隐患)(黑客经常使用) * 5.导致SQL注入的主要原因 * where loginPwd = 'fdsa' or '1'='1' * 这样会导致sql 的代码变成上面那样 因为1=1 恒成立 所以会查询到所有字段 * 用户输入的信息含有sql语句的关键字, * 而最重要的是这些关键字参与了sql语句 的编译过程 * 导致sql语句原意扭曲 进而达到sql注入 */ public class JDBC_Test06 { public static void main(String[] args) { //初始化一个界面 /* 该方法要返回一个容器 既可以存用户名 又可以存密码*/ Map<String,String> userLoginInfo = initUI(); //验证用户名loginName和密码loginPwd boolean loginResult = login(userLoginInfo); System.out.println(loginResult ? "登录成功!":"登录失败!"); } /** * 初始化用户界面 * @return loginName loginPwd 等登录信息 */ private static Map<String,String> initUI(){ Scanner scanner=new Scanner(System.in); System.out.print("用户名:"); String loginName = scanner.nextLine(); System.out.print("密码:"); String loginPwd = scanner.nextLine(); Map<String,String> userLoginInfo = new HashMap<>(); userLoginInfo.put("loginName",loginName); userLoginInfo.put("loginPwd",loginPwd); return userLoginInfo; } /** * * @param userLoginInfo * @return boolean */ private static boolean login(Map<String,String> userLoginInfo){ //打标记 boolean loginResult = false; /* P_JDBC_Test01.properties中的代码 driver = com.mysql.jdbc.Driver url = jdbc:mysql://localhost:3306/bjpowernode user = root password = 020826 */ //JDBC代码 Connection connection = null; Statement statement=null; ResultSet resultSet=null; ResourceBundle resourceBundle = ResourceBundle.getBundle("P_JDBC_Test01"); String driver = resourceBundle.getString("driver"); String url = resourceBundle.getString("url"); String user = resourceBundle.getString("user"); String password = resourceBundle.getString("password"); try { //1. 注册驱动 Class.forName(driver); //2. 获取连接 connection = DriverManager.getConnection(url,user,password); //3. 获取数据操作对象 statement = connection.createStatement(); //4. 执行sql /* String sql = "select * from t_user where loginName = 'xxx' and loginPwd = ''";*/ /* 变量拼到一个字符串 重点 loginName = '"+userLoginInfo.get("loginName")+"' */ String sql = "select * from t_user where loginName = '"+userLoginInfo.get("loginName")+"' and loginPwd = '"+userLoginInfo.get("loginPwd")+"'"; //上面这行进行了sql语句的拼接 下面这行发送sql语句到DBMS DBMS 进行sql 编译 //正好将用户提供的非法信息编译进去 导致了原sql语句含义改变 resultSet = statement.executeQuery(sql); //5. 处理查询结果集 /* 这个例子中 用户不匹配查不到记录 用户匹配了也只能查到1条记录 所以不需要用while 用if就可与*/ if (resultSet.next()){ //如果为true 说明结果集中又数据 这个例子中 有数据代表用户匹配 loginResult = true; } }catch (Exception e){ e.printStackTrace(); }finally { //6. 释放资源 try { if (resultSet!=null){ resultSet.close(); } }catch (Exception e){ e.printStackTrace(); } try { if (statement != null) { statement.close(); } }catch (Exception e){ e.printStackTrace(); } try { if (connection != null) { connection.close(); } }catch (Exception e){ e.printStackTrace(); } } return loginResult; } }
上述案例存在sql注入现象
解决方法
使用java.sql.preparedStatement(预编译的数据库操作对象)
原理:
preparedStatement预先对sql语句进行了编译 然后再给SQL语句传"值"
用preparedStatement后第三步获取数据库操作对象和第四步执行sql会发生变化
//3. 获取预编译的数据操作对象 // ? 占位符 1个?将来会接收1个值 // SQL语句的框架 String sql = "select * from t_user where loginName = ? and loginPwd = ? "; //程序执行到此会发送sql语句到DBMS 然后DBMS会进行sql语句的预编译 /*prepareStatement 这个没有 d */ preparedStatement = connection.prepareStatement(sql); //给 占位符 ? 传值 第一个 ? 下标是1 第二个是2 JDBC中下标从1开始 preparedStatement.setString(1,userLoginInfo.get("loginName")); preparedStatement.setString(2,userLoginInfo.get("loginPwd")); //4. 执行sql resultSet = preparedStatement.executeQuery();
对比 Statement 和 preparedStatement
Statement 存在SQL注入问题
Statement 编译一次 执行一次
preparedStatement 编译一次 执行n次
preparedStatement大多情况使用 ,只有极少数情况使用Statement,当业务必须需要sql语句拼接 或sql注入的时候。