概述

JDBC在我们学习J2EE的时候已经接触到了,但是仅是照搬步骤书写,其中的PreparedStatement防sql注入原理也是一知半解,然后就想回头查资料及敲测试代码探索一下。再有就是我们在项目中有一些配置项是有时候要变动的,比如数据库的数据源,为了在修改配置时不改动编译的代码,我们把要变动的属性提取到一个配置文件中,比如properties,因为properties里面都是键值对的形式,所以非常便于阅读和维护。

一、首先说说读取properties文件,这个相对路径和绝对路径的问题:

package com.test.properties;

import java.io.File;
import java.io.IOException; public class TestProperties { private static final String dataSourcePath = "resources/dataSource.properties";
private static final String
absoluteDataSourcePath = "D:\\Workspace\\Blogs\\TEST_Preparedstatement\\resources\\dataSource.properties"; public static void main(String[] args) { try {
// 1.getPath() 方法跟创建 File 对象时传入的路径参数有关,返回构造时传入的路径
// 2.getAbsolutePath() 方法返回文件的绝对路径,如果构造的时候是全路径就直接返回全路径,
// 如果构造时是相对路径,就返回当前目录的路径 + 构造 File 对象时的路径
// 3.getCanonicalPath() 方法返回绝对路径,会把 ..\ 、.\ 这样的符号解析掉 // 1.相对路径读取文件
//user.dir为当前用户目录(即项目路径),java.io 包中的类总是根据当前用户目录来解析相对路径名,
//当File对象入参不是以"/"开始的时候,则判断为相对路径方式构造,使用当前用户目录+相对路径的方式构造文件对象
System.out.println("当前用户目录:" + System.getProperty("user.dir"));
File file = new File(dataSourcePath);
System.out.println("入参路径:" + file.getPath());//入参路径
System.out.println("绝对路径:" + file.getAbsolutePath());//绝对路径
System.out.println("绝对路径:" + file.getCanonicalPath());//绝对路径 // 2.绝对路径读取文件
//绝对路径名:是完整的路径名,从根目录定位文件位置,不需要参照其他文件路径,
//windows中从某个分区磁盘如"c://"开始定位,linux表示从根目录"/"开始定位。
File file2 = new File(absoluteDataSourcePath);
System.out.println("");
System.out.println("入参路径2:" + file2.getPath());//入参路径
System.out.println("绝对路径2:" + file2.getAbsolutePath());//绝对路径
System.out.println("绝对路径2:" + file2.getCanonicalPath());//绝对路径
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

下面截图是控制台输出的结果:对应的解释我都写在代码的注释里了。

二、java读取Properties文件三种方式

搞清楚了绝对路径和相对路径,我们就可以借助java.util包中的Properties来读取项目中的配置文件了,下面是常用的三种方法:

1.其中第一种方法在上面的相对路径读取文件中也讲了,这里使用相对路径,java.io解析时会自动加上项目路径,也就是说等于是绝对路径,这里也可以使用绝对路径,但项目不推荐这样做是因为指定死了盘符,项目移动别的系统平台时就要改动。

2.第2、3种方法大同小异,就是要注意下class时使用加”/”是classes根目录下,所以要加”/”,而getClassLoader时直接是获得的classPath,所以不需要加根目录”/”

package com.test.preparedstatement;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties; public class TestJdbc { private static final String dataSourcePath = "resources/dataSource.properties"; public static void getPathByJavaUtilProperties() {
try {
// 1.使用java.util.Properties类的load(InputStream in)方法加载properties文件
InputStream iStream = new BufferedInputStream(new FileInputStream(new File(dataSourcePath)));
Properties properties = new Properties();
properties.load(iStream);
System.out.println("用户名1:" + properties.getProperty("username"));
System.out.println(""); // getResourceAsStream()参数与getResouce()是一样的,它相当于你用getResource()取得File文件后,
// 再new InputStream(file)一样的结果 // 2.使用class变量的getResourceAsStream()方法
Properties properties2 = new Properties();
InputStream iStream2 = TestJdbc.class.getResourceAsStream("/dataSource.properties");
// 这里为什么入参是加"/"的呢?,看一下下面的输出就明白了,Object是以class文件开始定位的
System.out.println(TestJdbc.class.getResource(""));//从编译根目录下的包目录下取
System.out.println(TestJdbc.class.getResource("/"));//获取编译目录的根目录classes
properties2.load(iStream2);
System.out.println("用户名2:" + properties2.getProperty("username"));
System.out.println(""); // 3.使用class.getClassLoader()的getResourceAsStream()方法
Properties properties3 = new Properties();
InputStream iStream3 = TestJdbc.class.getClassLoader().getResourceAsStream("dataSource.properties");
//这里又为什么不加"/",还是输出一下看,Object.class.getClassLoader()则是以classPath定位,所以不需要加"/"
System.out.println(TestJdbc.class.getClassLoader().getResource(""));
System.out.println(TestJdbc.class.getClassLoader().getResource("/"));
properties3.load(iStream3);
System.out.println("用户名3:" + properties3.getProperty("username"));
System.out.println(""); } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} } public static void main(String[] args) {
TestJdbc.getPathByJavaUtilProperties();
// TestJdbc.getDataBaseData();
}
}

下面是输出结果,这里要注意TestJdbc.class.getClassLoader().getResource("/")是null

三.测试statement和preparedStatement效率

加载好配置文件后,我来测试一下jdbc连接mysql数据库时,批量插入数据使用statement和preparedStatement效率,不多比比,代码来说话:

package com.test.preparedstatement;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties; public class TestJdbc { private static final String dataSourcePath = "resources/dataSource.properties"; public static void getDataBaseData() { try {
InputStream iStream = new BufferedInputStream(new FileInputStream(new File(dataSourcePath)));
Properties properties = new Properties();
properties.load(iStream);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url"); // 1.statement方式
long start = System.currentTimeMillis();
//加载驱动
Class.forName(driver);
//建立连接
Connection connection = DriverManager.getConnection(url, username, password);
//创建statement
Statement statement = connection.createStatement();
for (int i = 0; i < 50; i++) {
statement.execute("insert into test values("+i+",'a"+i+"')");
}
statement.close();
connection.close();
System.out.println("statment花费时间:"+String.valueOf(System.currentTimeMillis()-start)); // 2.preparedStatement方式
long start2 = System.currentTimeMillis();
//加载驱动
Class.forName(driver);
//建立连接
Connection connection2 = DriverManager.getConnection(url, username, password);
//创建preparedStatement
PreparedStatement preparedStatement = connection2.prepareStatement("insert into test values(?,?)");
for (int j = 50; j < 100; j++) {
preparedStatement.setInt(1, j);
preparedStatement.setString(2, "b"+j);
preparedStatement.execute();
} preparedStatement.close();
connection2.close();
System.out.println("preparedStatement花费时间:"+String.valueOf(System.currentTimeMillis()-start2)); } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
TestJdbc.getDataBaseData();
}
}

数据库数据插入了100条数据,执行成功:

然后看看控制台执行时间比较:

可以看到statement执行时间是preparedstatement执行时间的3倍,在批量处理上,preparedstatement效率更高。

总结:

prepared是准备的意思,也就是mysql的预编译在起作用。

1.使用statement时,每次执行sql,statement都会直接把sql扔给数据库执行,而且每次执行都要经过编译sql,执行sql,获得结果的过程,50次操作,50次编译。

2.preparedstatement不同的是在创建preparedstatement对象时就把sql语句结构输入进去了,并把这个sql预编译成函数并保存起来,然后加载参数,执行sql,返回结果。当批量处理时,后面49个处理都是使用这个函数,因为sql结构没变,所以不用二次编译,直接赋值,执行。

3.PreparedStatement继承自Statement,可以说对statement做了优化

4.下面这句是网上说的,具体我没测过:JDBC驱动程序5.0.5以后版本 默认预编译都是关闭的。jdbc:mysql://localhost:3306/mybatis?&useServerPrepStmts=true&cachePrepStmts=tru在MySQL中,既要开启预编译也要开启缓存。因为如果只是开启预编译的话效率还没有不开启预编译效率高。

5.因为preparedstatement使用?作占位符,所以就算恶意的sql进入到后台,在preparedStatement.execute();之前已经对sql做了预编译,就是说sql的执行函数已经确定,所以不会再破坏sql的结构。所以可以防止sql注入。

JDBC及PreparedStatement防SQL注入的更多相关文章

  1. 回头探索JDBC及PreparedStatement防SQL注入原理

    概述 JDBC在我们学习J2EE的时候已经接触到了,但是仅是照搬步骤书写,其中的PreparedStatement防sql注入原理也是一知半解,然后就想回头查资料及敲测试代码探索一下.再有就是我们在项 ...

  2. 【Hibernate实战】源码解析Hibernate参数绑定及PreparedStatement防SQL注入原理

        本文采用mysql驱动是5.1.38版本. 本篇文章涉及内容比较多,单就Hibernate来讲就很大,再加上数据库驱动和数据库相关,非一篇文章或一篇专题就能说得完.本文从使用入手在[Spr ...

  3. mysql之数据库连接的方法封装及防sql注入

    一.定义数据库和表 create database animal; CREATE TABLE `pet` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `name ...

  4. 【荐】PDO防 SQL注入攻击 原理分析 以及 使用PDO的注意事项

    我们都知道,只要合理正确使用PDO,可以基本上防止SQL注入的产生,本文主要回答以下几个问题: 为什么要使用PDO而不是mysql_connect? 为何PDO能防注入? 使用PDO防注入的时候应该特 ...

  5. C#语言Winform防SQl注入做用户登录的例子

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...

  6. PHP防SQL注入不要再用addslashes和mysql_real_escape_string

    PHP防SQL注入不要再用addslashes和mysql_real_escape_string了,有需要的朋友可以参考下. 博主热衷各种互联网技术,常啰嗦,时常伴有强迫症,常更新,觉得文章对你有帮助 ...

  7. PreparedStatement解决sql注入问题

    总结 PreparedStatement解决sql注入问题 :sql中使用?做占位符 2.得到PreparedStatement对象 PreparedStatement pst=conn.prepar ...

  8. nginx服务器防sql注入/溢出攻击/spam及禁User-agents

    本文章给大家介绍一个nginx服务器防sql注入/溢出攻击/spam及禁User-agents实例代码,有需要了解的朋友可进入参考. 在配置文件添加如下字段即可  代码如下 复制代码 server { ...

  9. C#防SQL注入代码的实现方法

    对于网站的安全性,是每个网站开发者和运营者最关心的问题.网站一旦出现漏洞,那势必将造成很大的损失.为了提高网站的安全性,首先网站要防注入,最重要的是服务器的安全设施要做到位. 下面说下网站防注入的几点 ...

随机推荐

  1. React Native初始化项目0.47.1报错

    首先看一下安卓和iOS报错: iOS终端运行报错: Xcode运行报错: 解决办法一: 将版本降到0.44.3,修改package,具体请看我的另一篇博客: http://www.cnblogs.co ...

  2. Swift的if let和guard let的使用 <一看就懂哟>

    // // ViewController.swift // 可选项的判断 // // Created by 思 彭 on 16/9/16. // Copyright © 2016年 思 彭. All ...

  3. Java数组(2):数组与泛型

    通常,数组与泛型不能很好的结合,你不能实例化具有参数化类型的数组.擦除会移除参数类型信息,而数组必须知道它们所持有的确切类型.但是我们可以参数化数组本身. import java.util.Array ...

  4. httpd配置Rewrite 301 302

    在系统做一些大的.比较耗时的发布的时候,往往需要停服很长时间,这期间有用户访问的话,就需要展示一个升级说明的页面,这个页面放在反向代理服务器中:反向代理服务器如httpd有请求URL重写模块,通过它可 ...

  5. 解读GitHub EntityComponentSystemSamples

    出自Unity官方的ECS项目示例,该项目的第一次Commit是在2018年3月20号,距离现在一年半的时间,这期间ECS本身的生态在快速发展,稳定性也是逐步提升,期待在2020年的Unity版本中作 ...

  6. BasePage基础页面的封装

    Paget Object设计模式,不多说,但在接触了poium测试库后,个人感觉BasePage都是多余的..但在次还是做个记录 class Page(): def __init__(self, dr ...

  7. 关于安装Git后,项目目录右键菜单无Git Bash Here命令的选项

    修改注册表配置 1.第一步,window + R,输入regedit回车进入注册表 2.依次进入HKEY_CLASSES_ROOT --> Directory -->Background ...

  8. flask内置的信号

    from flask import Flask,request,template_rendered,render_template,got_request_exception from signals ...

  9. 使用PowerShell 自动安装IIS 及自动部署网站

    执行环境:Windows Server 2012 R2 安装iis核心代码,可自定义安装项 注意这里不能使用add-windowsfeature  "Web-Filtering", ...

  10. Linux命令---ln、readlink

    ln 无参数--------创建硬链接 -s  -------------创建软链接 用法:ln  [option]  源文件  目标文件 ln test.txt test_hard.txt 只有在同 ...