关于JDBC的总结
最近学习了mybatis框架,用起来很舒服,但是对之前的JDBC有些遗忘了,写一篇博客来回顾一下。
1.关于JDBC
(1) 什么是JDBC
> JDBC 是一种用于执行SQL语句(DML,DDL,DQL)的Java API,可以为多种关系数据库(oracle,mysql,SQL server)提供统一访问,它由一组用Java语言编写的类和接口组成。
>我的理解:一种数据库的存取技术(可以对数据库进行增删查改)。
>注:由于数据库有很多种,每种数据库的操作方式都不同;为了统一,JAVA做了一组规范(接口),然后由各大数据库的厂商去实现这个规范(实现并且打成jar包),我们使用那种数据库就需要导入这个数据库需要的jar包。
(2)使用JDBC完成数据的查询
> 主要步骤: 加载JDBC驱动程序 → 建立数据库连接Connection → 创建执行SQL的语句Statement →
> 处理执行结果ResultSet → 释放资源
public void selectOne(long id) {
Connection c = null;
Statement cs = null;
//注册驱动
try {
//第一步:注册驱动
Class.forName("com.mysql.jdbc.Driver");
//第二步:通过驱动和数据库建立连接(通过URL地址告诉JDBC程序连接哪个数据库)
c = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");
//第三步:创建执行SQL语句的statement(创建一个Statement对象(也就是语句对象)来将SQL语句发送到数据库)
cs = c.createStatement();
String sql = "select * from user where id = " + id;
//第四步:执行sql语句 Statement(语句对象)里面的方法
ResultSet eQ = cs.executeQuery(sql);
//处理查询结果集
while(eQ.next()){
System.out.println(eQ.getLong("id") + "," + eQ.getString("name") +"," + eQ.getBoolean("sex"));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (c!=null)
c.close();//释放资源
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (cs!=null)
cs.close();//释放资源
} catch (SQLException e) {
e.printStackTrace();
}
}
}
上面这种写法
Ⅰ:在定义sql语句的时候,需要字符串拼接,很麻烦
Ⅱ:还有sql注入的安全问题
如:select * from user where userID = ' anything ' or 'b'='b' ,不管userID 对不对,都能进行查询,因为'b'='b'这一条件永远满足。
(3)使用PreparedStatement
使用PreparedStatement就可以解决上述问题
①.PreparedStatement是什么?
PreparedStatement是Statement的子接口,Statement是一个语句对象,表示静态SQL语句对象(见JDBC第三步),
而PreparedStatement表示预编译SQL语句对象。
②什么是预编译?
通过Statement对象执行SQL语句时,需要将SQL语句发送给DBMS,由 DBMS首先进行编译后再执行。预编译语句和Statement不同,在创建PreparedStatement 对象时就指定了SQL语句,该语句立即发送给DBMS进行编译。当该编译语句被执行时,DBMS直接运行编译后的SQL语句,而不需要像其他SQL语句那样首先将其编译,在执行。
③PreparedStatement和Statement对比
-
Statement效率低,原因:
1)Statement静态Sql对象,等到执行的时候才发送Sql到数据库,,
2)发送Sql到数据库,数据库,验证你的语法,编译成二进制,之后才会执行Sql -
PreparedStatement 效率高
conn.prepareStatement(sql);已经把Sql发送到了数据库验证语法,编译成二进制等待你的运行
使用PreparedStatement来完成上面的查询
public void selectOne(long id) {
Connection conn = null;
PreparedStatement ps = null;//Statement
//注册驱动
try {
//第一步:注册驱动
Class.forName("com.mysql.jdbc.Driver");
//第二步:通过驱动和数据库建立连接(通过URL地址告诉JDBC程序连接哪个数据库)
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");
//问号(?)--相当于占位符
String sql = "select * from user where id = ?";
//第三步:获得预编译Sql对象:已经把sql发送到了数据库了
ps = conn.prepareStatement(sql);
//注意填充Sql位置,索引从1开始
ps.setInt(1, 2);
//第四步:行的时候不需要传sql
ps.executeQuery();
ResultSet eQ = ps.executeQuery();
while(eQ.next()){
System.out.println(eQ.getLong("id") + "," + eQ.getString("name") +"," + eQ.getBoolean("sex"));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (conn!=null)
conn.close();//释放资源
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (ps!=null)
ps.close();//释放资源
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2. 连接池DataSource
(1)关于连接池
每次请求都会创建一个connection,因此会浪费资源(内存),拿到连接对象每次都需要验证用户名和密码,浪费时间。
解决办法:创建一个连接池:就是用来装连接对象的容器。
取到连接也需要用户名和密码,也需要时间,但是Java代码取到连接只需要从连接池里面拿到,不需要用户名和密码,用完之后,还回到连接池。
(2)使用连接池和不使用连接池的区别在哪里?
- 从代码上:
//不使用连接池: Conenction对象由DriverManager获取.
Connection conn = DriverManager.getConnection(url,username,password);
使用连接池:(只有一个不同点,现在的连接需要从DataSource里面拿,DataSource需要从数据库拿,
所以我们需要把用户名和密码给连接池,连接池帮我们从数据库拿连接)
如何创建DataSource对象,如何在DataSource中设置url,账号,密码.
//1.直接获得连接对象
new BasicDataSource();
//2.连接池工厂:可以自己匹配字段,自己读取配置文件。(配置文件的key必须标准)
BasicDataSourceFactory.createDataSource(p);
//1.需要自己设置连接信息
static Properties p = new Properties();
//创建一个连接池对象bs
private static BasicDataSource bs = new BasicDataSource();
static{//执行的时机:类加载的时候,调用静态方法,调用类的构造方法
try {
//1.读取配置文件,获得字节输入流
InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("dbcp.properties");
//2.配置对象,加载流资源
p.load(resourceAsStream);
bs.setDriverClassName(p.getProperty("driverClassName"));
bs.setUsername(p.getProperty("username"));
bs.setPassword(p.getProperty("password"));
bs.setUrl(p.getProperty("url"));
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConn() {
try {
//从连接池取连接对象
return bs.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//2.通过BasicDataSourceFactory工厂拿到连接,可以自己匹配字段,自己读取配置文件。(配置文件的key必须标准)
static Properties p = new Properties();
private static DataSource bs;
static{
try {
//1.读取配置文件,获得字节输入流
InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("dbcp.properties");
//2.配置对象,加载流资源
p.load(resourceAsStream);
bs = BasicDataSourceFactory.createDataSource(p);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConn() {
try {
//向连接池取连接对象
return bs.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}