Java JDBC下执行SQL的不同方式、参数化预编译防御
相关学习资料
http://zh.wikipedia.org/wiki/Java数据库连接http://lavasoft.blog.51cto.com/62575/20588http://blog.csdn.net/cxwen78/article/details/6863696http://www.ibm.com/developerworks/cn/java/jdbc-objects/index.html?ca=drshttp://www.moon-soft.com/doc/37897.htm
目录
1. Java JDBC简介2. Java JDBC下执行SQL的不同方式3. Java JDBC编程实践
1. Java JDBC简介
数据库驱动程序是JDBC程序和数据库之间的转换层,数据库驱动程序负责将JDBC调用映射成特定的数据库调用,类似PHP中的"数据库抽象层"
http://www.php.net/manual/zh/refs.database.vendors.php
使用Java JDBC API进行编程,可以为多种关系数据库提供统一访问,为我们带来跨平台、跨数据库系统的好处
总的来说,JDBC驱动通常有如下4种类型
1. JDBC-ODPC桥:它将JDBC API映射到ODPC API。再让JDBC-ODPC调用数据库本地驱动代码(也就是数据库厂商提供的数据库操作二进制代码库,例如Oracle中的oci.dll)2. 本地API驱动直接将JDBC API映射成数据库特定的客户端API,即通过客户端加载数据库厂商提供的本地代码库(C/C++等) 3. 网络协议驱动这种类型的驱动给客户端提供了一个网络API,客户端上的JDBC驱动程序使用套接字(Socket)来调用服务器上的中间件程序,后者在将其请求转化为所需的具体API调用。4. 本地协议驱动这种类型的驱动使用Socket,直接在客户端和数据库间通信。它是一种直接与数据库实例交互的JDBC这种驱动是智能的,它知道数据库使用的底层协议,也是目前最主流使用的JDBC驱动,我们本章的重点就是它
JDBC编程(连接数据库)步骤
1) 加载数据库驱动通常我们使用Class类的forName()静态方法来加载驱动(由各个数据库厂商自己实现)Class.forName("com.mysql.jdbc.Driver");Class.forName("oracle.jdbc.driver.OracleDriver");"com.mysql.jdbc.Driver"、"oracle.jdbc.driver.OracleDriver"代表了数据库驱动类对应的字符串2) 通过DriverManager获取数据库连接DriverManager.getConnection(String url, String user, String pass); 2.1) url: 数据库连接字符串 2.1.1) Mysql jdbc:mysql://hostname:port/databasename 2.1.2) Oracle jdbc:oracle:thin:@hostname:port:databasename 2.2) user: 数据库的系统用户名 2.3) pass: 数据库的系统密码3) 通过Connection对象创建Statement对象,Connection创建Statement对象的方法有如下3个 3.1) createStatement(String sql):创建基本的Statement对象 3.2) prepareStatement(String sql): 根据传入的SQL语句创建预编译的Statement对象 3.3) prepareCall(String sql): 根据传入的SQL语句创建CallableStatement对象4) 使用Statement执行SQL语句所有的Statement都有如下3个方法来执行SQL语句 4.1) execute(): 可以执行任何SQL语句,但比较麻烦 4.2) executeUpdate(): 主要用于执行DML和DDL语句。执行DML语句返回受SQL影响的行数,执行DDL语句返回0 4.3) executeQuery(): 只能执行查询语句,执行后返回代表查询结果的ResultSet对象5) 操作结果集如果执行的SQL语句是查询语句,则执行结果将返回一个ResultSet对象,该对象里保存了SQL语句查询的结果。程序可以通过操作该ResultSet对象来取出查询结果。ResultSet对象主要提供了如下方法 5.1) 移动记录指针的方法 5.1.1) next() 5.1.2) previous() 5.1.3) first() 5.1.4) last() 5.1.5) beforeFirst() 5.1.6) afterLast() 5.1.7) absolute() 5.2) 获取指针指向的某行的"特定的列值" 5.2.1) getInt() 5.2.2) getString() ... 该方法既可以使用列索引作为参数,也可以使用列名作为参数6) 回收数据库资源包括关闭ResultSet、Statement、Connection等资源
2. Java JDBC下执行SQL的不同方式
JDBC是一个相对比较底层的API接口,它只提供给我们一个执行原生SQL语句的输入接口。所以和JDBC的API接口都定义在
java.sqljavax.sql
我们知道,Java JDBC编程中有3种方法进行DDL、DML语句的执行
1. createStatement返回Statement 1) execute() 2) executeUpdate() 3) executeQuery()2. prepareStatement返回PrepareStatement 1) execute() 2) executeUpdate() 3) executeQuery()3. prepareCall返回CallableStatement
我们来逐一学习
0x1: 基本的Statement对象
Statement提供了3个方法来执行SQL语句,它们都可以用于执行DDL(执行后返回值为0)、DML(执行后返回受影响行数)语句
使用execute执行DDL、DML语句
Statement的execute()方法几乎可以执行任何原生SQL语句,但它执行SQL语句时比较麻烦(它无法直接返回一个ResultSet,而是需要我们手工再次获取),通常情况下,使用executeQuery()、或executeUpdate()方法更加简单。但如果程序员不清楚SQL语句的类型,则只能使用execute()方法来执行该SQL语句了。
注意,使用execute()方法执行SQL语句的返回值"只是"boolean值,它表明了执行该SQL语句是否返回了ResultSet对象,而我们要得到具体的ResultSet对象还需要额外的方法
1. getResult(): 获取该Statement执行查询语句所返回的ResultSet对象2. getUpdateCount(): 获取该Statement执行DML语句所影响的记录行数
code:
import java.util.*;import java.io.*;import java.sql.*;public class ExecuteSQL{ private String driver; private String url; private String user; private String pass; public void initParam(String paramFile)throws Exception { // 使用Properties类来加载属性文件 Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); pass = props.getProperty("pass"); } public void executeSql(String sql)throws Exception { // 加载驱动 Class.forName(driver); try( // 获取数据库连接 Connection conn = DriverManager.getConnection(url , user , pass); // 使用Connection来创建一个Statement对象 Statement stmt = conn.createStatement()) { // 执行SQL,返回boolean值表示是否包含ResultSet boolean hasResultSet = stmt.execute(sql); // 如果执行后有ResultSet结果集 if (hasResultSet) { try( // 获取结果集 ResultSet rs = stmt.getResultSet()) { // ResultSetMetaData是用于分析结果集的元数据接口 ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); // 迭代输出ResultSet对象 while (rs.next()) { // 依次输出每列的值 for (int i = 0 ; i < columnCount ; i++ ) { System.out.print(rs.getString(i + 1) + "\t"); } System.out.print("\n"); } } } else { System.out.println("该SQL语句影响的记录有" + stmt.getUpdateCount() + "条"); } } } public static void main(String[] args) throws Exception { ExecuteSQL es = new ExecuteSQL(); es.initParam("mysql.ini"); System.out.println("------执行删除表的DDL语句-----"); es.executeSql("drop table if exists my_test"); System.out.println("------执行建表的DDL语句-----"); es.executeSql("create table my_test" + "(test_id int auto_increment primary key, " + "test_name varchar(255))"); System.out.println("------执行插入数据的DML语句-----"); es.executeSql("insert into my_test(test_name) " + "select student_name from student_table"); System.out.println("------执行查询数据的查询语句-----"); es.executeSql("select * from my_test"); }}
使用executeUpdate执行DDL、DML语句
import java.util.*;import java.io.*;import java.sql.*;public class ExecuteDDL{ private String driver; private String url; private String user; private String pass; public void initParam(String paramFile) throws Exception { // 使用Properties类来加载属性文件 Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); pass = props.getProperty("pass"); } public void createTable(String sql)throws Exception { // 加载驱动 Class.forName(driver); try( // 获取数据库连接 Connection conn = DriverManager.getConnection(url , user , pass); // 使用Connection来创建一个Statment对象 Statement stmt = conn.createStatement()) { // 执行DDL,创建数据表 stmt.executeUpdate(sql); } } public static void main(String[] args) throws Exception { ExecuteDDL ed = new ExecuteDDL(); ed.initParam("mysql.ini"); ed.createTable("create table jdbc_test " + "( jdbc_id int auto_increment primary key, " + "jdbc_name varchar(255), " + "jdbc_desc text);"); System.out.println("-----建表成功-----"); }}
从代码中我们可以看到,我们并没有把数据库连接信息写在程序里,而是使用一个mysql.ini文件来保存数据库连接信息,这样当需要把程序从开发环境移植到生产环境时,无须修改源代码,只需要修改mysql.ini配置文件即可
mysql.ini:
driver=com.mysql.jdbc.Driverurl=jdbc:mysql://127.0.0.1:3306/select_testuser=rootpass=111
使用executeQuery执行DDL、DML语句
import java.sql.*;public class ConnMySql{ public static void main(String[] args) throws Exception { // 1.加载驱动,使用反射的知识,现在记住这么写。 Class.forName("com.mysql.jdbc.Driver"); try( // 2.使用DriverManager获取数据库连接, // 其中返回的Connection就代表了Java程序和数据库的连接 // 不同数据库的URL写法需要查驱动文档知道,用户名、密码由DBA分配 Connection conn = DriverManager.getConnection( "jdbc:mysql://127.0.0.1:3306/company" , "root" , "111"); // 3.使用Connection来创建一个Statment对象 Statement stmt = conn.createStatement(); // 4.执行SQL语句 /* Statement有三种执行sql语句的方法: 1 execute 可执行任何SQL语句。- 返回一个boolean值, 如果执行后第一个结果是ResultSet,则返回true,否则返回false 2 executeQuery 执行Select语句 - 返回查询到的结果集 3 executeUpdate 用于执行DML语句。- 返回一个整数, 代表被SQL语句影响的记录条数 */ ResultSet rs = stmt.executeQuery("select *" + " from p8_ad_user")) { // ResultSet有系列的getXxx(列索引 | 列名),用于获取记录指针 // 指向行、特定列的值,不断地使用next()将记录指针下移一行, // 如果移动之后记录指针依然指向有效行,则next()方法返回true。 while(rs.next()) { System.out.println(rs.getInt(1) + "\t" + rs.getString(2) + "\t" + rs.getString(3) + "\t" + rs.getString(4)); } } }}
0x2: prepareStatement预编译对象
JDBC中的这个类对于安全人员、和黑客都需要重点关注,它不仅可以提供SQL执行性能,同时还有防御SQL注入的功能。
当我们的业务层中和数据库相关的代码经常要执行一些相似度很高的SQL语句,它们的结果基本相似,只是插入时插入的值不同而已
insert into student_table values(null, "LittleHann", 1);insert into student_table values(null, "LittleHann", 2);
在这种情况下,我们可以使用带占位符(?)参数的SQL语句来代替它
insert into student_table values(null, ?, ?);
为了满足这种功能,JDBC提供了prepareStatement接口,它是Statement接口的子接口,它可以预编译SQL语句,预编译后的SQL语句被存储在prepareStatement对象中,然后可以使用该对象多次高效地执行该语句。prepareStatement同样也使用execute()、executeUpdate()、executeQuery()来执行SQL语句,但这三个方法无须参数,因为prepareStatement在创建的时候已经存储了预编译的SQL语句,在执行SQL语句的时候只要传入参数值即可(setXxx)
Statement和prepareStatement的性能对比
import java.util.*;import java.io.*;import java.sql.*;public class PreparedStatementTest{ private String driver; private String url; private String user; private String pass; public void initParam(String paramFile)throws Exception { // 使用Properties类来加载属性文件 Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); pass = props.getProperty("pass"); // 加载驱动 Class.forName(driver); } public void insertUseStatement()throws Exception { long start = System.currentTimeMillis(); try( // 获取数据库连接 Connection conn = DriverManager.getConnection(url , user , pass); // 使用Connection来创建一个Statment对象 Statement stmt = conn.createStatement()) { // 需要使用100条SQL语句来插入100条记录 for (int i = 0; i < 1000 ; i++ ) { stmt.executeUpdate("insert into student_table values(" + " null ,'姓名" + i + "' , 1)"); } System.out.println("使用Statement费时:" 再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow