1.1 由于需要将动态数据拼接到sql语句中,这导致程序复杂度高,容易出错
1.2 拼接的数据若含有sql语法内容就会导致拼接后的sql语法含义改变而出现sql注入攻击
1.3 当大批量执行语义相同,但是含有动态数据的sql时效率很差2 使用statement执行sql语句不好的原因2.1 当执行一条sql语句发送到数据库时,数据库先将该sql解析并生成一个执行计划(这个过程会消耗资源和性能),如果多次执行一样的sql语句,数据库会重用执行计划,但是若多次执行语义相同但是含有动态数据的sql时,数据库会生成不同的执行计划,严重影响数据库的开销
2.2 例如执行 select * from userifo_fury 生成一个执行计划再次执行select * from userifo_fury 就会重用上面的执行计划(因为这是静态的sql语句
但是,执行insert into userifo values(1, 'jack','122314','141234@qq.com','fury',15600) ) 生成一个执行计划,再执行执行insert into userifo values(2, 'rose','122314','141234@qq.com','fury',15600)由于内容不同,会再次生成另外一个执行计划,若执行1000次上述情况的insert,数据库会产生1000个执行计划,这样就严重影响了数据库的效率
因此,statement只适合执行静态的sql语句,不适合执行动态的sql语句
3 利用preparedstatement代替statement编写简单没有sql注入问题批量执行语义相同的sql语句会重用执行计划 1 package cn.xiangxu.entity; 2 3 import java.io.serializable; 4 5 public class user implements serializable { 6 7 private static final long serialversionuid = -5109978284633713580l; 8 9 private integer id;10 private string name;11 private string pwd;12 public user() {13 super();14 // todo auto-generated constructor stub15 }16 public user(integer id, string name, string pwd) {17 super();18 this.id = id;19 this.name = name;20 this.pwd = pwd;21 }22 @override23 public int hashcode() {24 final int prime = 31;25 int result = 1;26 result = prime * result + ((id == null) ? 0 : id.hashcode());27 return result;28 }29 @override30 public boolean equals(object obj) {31 if (this == obj)32 return true;33 if (obj == null)34 return false;35 if (getclass() != obj.getclass())36 return false;37 user other = (user) obj;38 if (id == null) {39 if (other.id != null)40 return false;41 } else if (!id.equals(other.id))42 return false;43 return true;44 }45 public integer getid() {46 return id;47 }48 public void setid(integer id) {49 this.id = id;50 }51 public string getname() {52 return name;53 }54 public void setname(string name) {55 this.name = name;56 }57 public string getpwd() {58 return pwd;59 }60 public void setpwd(string pwd) {61 this.pwd = pwd;62 }63 @override64 public string tostring() {65 return user [id= + id + , name= + name + , pwd= + pwd + ];66 }67 68 69 70 }
user表对应的实体类
1 package testjdbc; 2 3 import java.sql.connection; 4 import java.sql.drivermanager; 5 import java.sql.preparedstatement; 6 import java.sql.resultset; 7 import java.sql.sqlexception; 8 import java.util.arraylist; 9 import java.util.list;10 11 import org.junit.test;12 13 import cn.xiangxu.entity.user;14 15 public class testcase {16 @test17 public void test01() {18 connection conn = null;19 preparedstatement ps = null;20 resultset rs = null;21 try {22 class.forname(com.mysql.jdbc.driver); // 加载数据库驱动23 24 conn = drivermanager.getconnection( // 初始化连接对象25 jdbc:mysql://localhost:3306/test, root, 182838);26 27 28 string sql = select * from user where pwd = ? ; // 拼接sql语句,位置参数用?代替29 30 ps = conn.preparestatement(sql); // 初始化预编译执行对象31 32 ps.setstring(1, 182838); // 设置sql语句中的位置位置参数(注意:是从1开始数不是从0开始数)33 34 rs = ps.executequery(); // 执行sql语句35 36 list<user> users = new arraylist<user>(); // 创建一个集合来存放记录对象37 while(rs.next()) { // 遍历结果集38 // system.out.println(====================);39 // system.out.println(rs.getint(id));40 // system.out.println(rs.getstring(name));41 // system.out.println(rs.getstring(pwd));42 user user = new user();43 user.setid(rs.getint(id));44 user.setname(rs.getstring(name));45 user.setpwd(rs.getstring(pwd));46 users.add(user); // 向集合中添加元素47 }48 49 system.out.println(users); // 打印输出集合50 for(user user : users) {51 system.out.println(user);52 }53 54 // 释放资源55 rs.close();56 ps.close(); 57 conn.close();58 59 } catch (exception e) {60 // todo auto-generated catch block61 e.printstacktrace();62 } finally {63 if(rs != null) {64 try {65 rs.close();66 } catch (sqlexception e) {67 // todo auto-generated catch block68 e.printstacktrace();69 }70 }71 if(ps != null) {72 try {73 ps.close();74 } catch (sqlexception e) {75 // todo auto-generated catch block76 e.printstacktrace();77 }78 }79 if(conn != null) {80 try {81 conn.close();82 } catch (sqlexception e) {83 // todo auto-generated catch block84 e.printstacktrace();85 }86 }87 }88 89 }90 91 }
使用预编译statement的实例
4 利用properties对象读取properties配置文件中的信息4.1 properties继承了hashtable类,properties对象也是使用键值对的方式来保存数据,但是properties对象的键和值都是字符串类型class properties extends hashtable<object,object>
4.2 properties 类中的主要方法4.2.1 public synchronized void load(inputstream instream) throws ioexception将properties属性文件的文件输入流加载到properties对象
4.2.2 public void store(outputstream out, string comments) throws ioexception 将properties对象中的属性列表保存到输出流文件中
注意:第二个参数表示注释信息(注意:properties文件中不能用中文),在注释信息后面会自动添加一个时间信息
注意:新创建的文件在项目的根目录下面(问题:为什么在eclipse中没有,但是到文件夹中却能找到???)
4.2.3 public string getproperty(string key)获取属性值,参数是属性的键
4.2.4 public synchronized object setproperty(string key, string value)修改属性值,参数1是属性的键,参数2是属性的新值
4.3 案例要求:读取properties配置文件总的属性值,将读取到的属性值进行修改后保存到另外一个properties配置文件中
1 package cn.xiangxu.entity; 2 3 import java.io.fileinputstream; 4 import java.io.fileoutputstream; 5 import java.io.inputstream; 6 import java.util.iterator; 7 import java.util.properties; 8 9 public class test {10 public static void main(string[] args) {11 try {12 properties prop = new properties(); // 创建properties对象13 14 // prop.load(new fileinputstream(config.properties)); // 使用这种方式时,配置文件必须放在项目的根目录下15 inputstream is = test.class.getclassloader().getresourceasstream(config/config.properties); // 读取属性文件16 17 prop.load(is); // 加载属性列表18 19 iterator<string> it=prop.stringpropertynames().iterator(); // 将配置文件中的所有key放到一个可迭代对象中20 while(it.hasnext()){ // 利用迭代器模式进行迭代21 string key=it.next(); // 读取下一个迭代对象的下一个元素22 system.out.println(key+:+prop.getproperty(key)); // 根据key值获取value值(获取属性信息)23 }24 25 is.close(); // 关闭输入流,释放资源26 27 fileoutputstream ofile = new fileoutputstream(b.properties, true);//创建一个输出流文件,true表示追加打开28 prop.setproperty(maxactive, 33); // 修改属性信息29 prop.store(ofile, zhe shi yi ge xin de shu xing pei zhi wen jian.); // 将properties对象中的内容放到刚刚创建的文件中去30 ofile.close(); // 关闭输出流,释放资源31 32 } catch (exception e) {33 // todo auto-generated catch block34 e.printstacktrace();35 } 36 }37 }
读取属性配置文件信息
等待读取的properties配置文件的位置如下图所示
5 数据库连接池5.1 什么是数据库连接池程序启动时就创建足够多的数据库连接,并将这些连接组成一个连接池,由程序自动地对池中的连接进行申请、使用、释放
5.2 数据库连接池的运行机制》程序初始化时创建连接池
》需要操作数据库时向数据库连接池申请一个可用的数据库连接
》使用完毕后就将数据库连接还给数据库连接池(注意:不是关闭连接,而是交给连接池)
》整个程序退出时,断开所有连接,释放资源(即:管理数据库连接池的那个线程被杀死后才关闭所有的连接)
5.3 数据库连接池的编程步骤5.3.1 导包
5.3.2 声明threadlocal、basicdatasource成员变量(注意:这两个成员变量是静态的)
5.3.3 在静态代码块中实例化那两个成员变量,并通过properties对象读取配置文件信息,利用这些配置文件信息给basicdatasource对象进行初始化处理
5.3.4 编写创建连接静态方法利用basicdatasource对象实例化一个连接对象
将这个连接对象放到threadlocal对象中
5.3.5 编写释放连接静态方法从threadlocal对象中获取连接对象
清空threadlocal对象
判断连接对象是否释放
6 利用数据库连接池操作数据库项目结构图
1 # zhe shi zhu shi , yi ban bu yong zhong wen 2 # deng hao liang bian mei you kong ge, mo wei mei you fen hao3 # hou mian bu neng you kong ge4 driverclassname=com.mysql.jdbc.driver5 url=jdbc:mysql://localhost:3306/test6 username=root7 password=1828388 maxactive=1009 maxwait=3000
properties配置文件
1 <project xmlns="http://maven.apache.org/pom/4.0.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://maven.apache.org/pom/4.0.0 "> 2 <modelversion>4.0.0</modelversion> 3 <groupid>cn.xiangxu</groupid> 4 <artifactid>testjdbc</artifactid> 5 <version>0.0.1-snapshot</version> 6 <dependencies> 7 <dependency> 8 <groupid>mysql</groupid> 9 <artifactid>mysql-connector-java</artifactid>10 <version>5.1.37</version>11 </dependency>12 <dependency>13 <groupid>junit</groupid>14 <artifactid>junit</artifactid>15 <version>4.12</version>16 </dependency>17 <dependency>18 <groupid>commons-dbcp</groupid>19 <artifactid>commons-dbcp</artifactid>20 <version>1.4</version>21 </dependency>22 </dependencies>23 </project>
maven依赖文件
1 package cn.xiangxu.tools; 2 3 import java.io.ioexception; 4 import java.io.inputstream; 5 import java.sql.connection; 6 import java.sql.sqlexception; 7 import java.util.properties; 8 9 import org.apache.commons.dbcp.basicdatasource;10 11 public class dbutil {12 /*13 * threadlocal用于线程跨方法共享数据使用14 * threadlocal内部有一个map, key为需要共享数据的线程本身,value就是其需要共享的数据15 */16 private static threadlocal<connection> tl; // 声明一个类似于仓库的东西17 private static basicdatasource datasource; // 声明一个数据库连接池对象18 19 // 静态代码块,在类加载的时候执行,而且只执行一次20 static {21 tl = new threadlocal<connection>(); // 实例化仓库对象22 datasource = new basicdatasource(); // 实例数据库连接池对象23 24 properties prop = new properties(); // 创建一个properties对象用(该对象可以用来加载配置文件中的属性列表)25 inputstream is = dbutil.class.getclassloader().getresourceasstream(config/mysql.properties); // 读取配置文件信息26 try {27 prop.load(is); // 加载配置文件中的属性列表28 29 string driverclassname = prop.getproperty(driverclassname); // 获取属性信息30 string url = prop.getproperty(url);31 string username = prop.getproperty(username);32 string password = prop.getproperty(password);33 integer maxactive = integer.parseint(prop.getproperty(maxactive));34 integer maxwait = integer.parseint(prop.getproperty(maxwait));35 36 datasource.setdriverclassname(driverclassname); // 初始化数据库连接池(即:配置数据库连接池的先关参数)37 datasource.seturl(url);38 datasource.setusername(username);39 datasource.setpassword(password);40 datasource.setmaxactive(maxactive);41 datasource.setmaxwait(maxwait);42 43 is.close(); // 关闭输入流,释放资源44 } catch (ioexception e) {45 // todo auto-generated catch block46 e.printstacktrace();47 } 48 49 }50 51 /**52 * 创建连接对象(注意:静态方法可以直接通过类名来调用)53 * @return 连接对象54 * @throws exception55 */56 public static connection getconnection() throws exception { 57 try {58 connection conn = datasource.getconnection(); // 创建连接对象(利用数据库连接池进行创建)59 tl.set(conn); // 将连接对象放到仓库中60 return conn; 61 } catch (exception e) {62 // todo auto-generated catch block63 e.printstacktrace();64 throw e;65 }66 }67 68 /**69 * 关闭连接对象(注意:静态方法可以通过类名直接调用)70 * @throws exception71 */72 public static void closeconnection() throws exception {73 connection conn = tl.get(); // 从仓库中取出连接对象74 tl.remove(); // 清空仓库75 if(conn != null) { // 判断连接对象是否释放资源76 try {77 conn.close();78 } catch (exception e) {79 // todo auto-generated catch block80 e.printstacktrace();81 throw e;82 }83 }84 }85 86 }
数据库连接池类
1 package testjdbc; 2 3 import java.sql.connection; 4 import java.sql.preparedstatement; 5 import java.sql.resultset; 6 7 import org.junit.test; 8 9 import cn.xiangxu.tools.dbutil;10 11 public class testdbutil {12 @test13 public void test01() {14 try {15 connection conn = dbutil.getconnection(); // 创建连接对象16 string sql = select * from user ; // 拼接sql语句17 preparedstatement ps = conn.preparestatement(sql); // 创建执行对象18 resultset rs = ps.executequery(sql); // 执行sql语句19 while(rs.next()) { // 遍历结果集20 system.out.println(rs.getstring(name));21 }22 } catch (exception e) {23 e.printstacktrace();24 } finally { // 关闭连接,释放资源25 try {26 dbutil.closeconnection();27 } catch (exception e) {28 e.printstacktrace();29 }30 }31 }32 }
数据库连接池的应用
以上就是jdbc连接数据库实例讲解的详细内容。