目录
1/2/3 Statement 和 Preparedstatement 的区别
4 读取properties配置文件
5 数据库连接池
6 利用数据库连接池连接数据库
1 使用Statement执行含有动态信息的SQL语句时有几个不足:
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 stub 15 } 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 @Override 23 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 @Override 30 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 @Override 64 public String toString() { 65 return "User [id=" + id + ", name=" + name + ", pwd=" + pwd + "]"; 66 } 67 68 69 70 }
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 @Test 17 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 block 61 e.printStackTrace(); 62 } finally { 63 if(rs != null) { 64 try { 65 rs.close(); 66 } catch (SQLException e) { 67 // TODO Auto-generated catch block 68 e.printStackTrace(); 69 } 70 } 71 if(ps != null) { 72 try { 73 ps.close(); 74 } catch (SQLException e) { 75 // TODO Auto-generated catch block 76 e.printStackTrace(); 77 } 78 } 79 if(conn != null) { 80 try { 81 conn.close(); 82 } catch (SQLException e) { 83 // TODO Auto-generated catch block 84 e.printStackTrace(); 85 } 86 } 87 } 88 89 } 90 91 }
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 block 34 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 hao 3 # hou mian bu neng you kong ge 4 driverClassName=com.mysql.jdbc.Driver 5 url=jdbc:mysql://localhost:3306/test 6 username=root 7 password=182838 8 maxActive=100 9 maxWait=3000
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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 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>
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 block 46 e.printStackTrace(); 47 } 48 49 } 50 51 /** 52 * 创建连接对象(注意:静态方法可以直接通过类名来调用) 53 * @return 连接对象 54 * @throws Exception 55 */ 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 block 63 e.printStackTrace(); 64 throw e; 65 } 66 } 67 68 /** 69 * 关闭连接对象(注意:静态方法可以通过类名直接调用) 70 * @throws Exception 71 */ 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 block 80 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 @Test 13 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 }