目录
1. JDBC原理
2. 导入JDBC驱动包
3. 编写JDBC代码实现Insert
3.1 创建并初始化一个数据源
3.2 和数据库服务器建立连接
3.3 构造SQL语句
3.4 执行SQL语句
3.5 释放必要的资源
4. JDBC代码的优化
4.1 从控制台输入
4.2 避免SQL注入的SQL语句
5. 编写JDBC代码实现Select
1. JDBC原理
1. 各种数据库如MySQL、Oracle、SQLServer等,在开始时会提供一组编程接口(API),
API即application programming interface,即代码层次上的提供的功能,API往往是通过函数或类的形式来提供的。
2. 不同的数据库系统的API是不同的JDBC就是统一Java与数据库连接的一套规范的API:
3.Java程序员如果想要进行数据库开发,就需要在项目中导入对应数据库的驱动包,才能编写代码。
4. 驱动包是数据库厂商提供的,此处以MySQL为例,获取方式有:
(1)从MySQL官网获取(现为Oracle官网的一个子网);
(2)github;
(3)maven中央仓库;
注:中央仓库可以理解为一个服务器,托管了各种软件程序包,maven就类似于应用商店,通过应用商店就可以访问到应用程序包并进行下载;
2. 导入JDBC驱动包
3. 编写JDBC代码实现Insert
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JDBCInsert {
public static void main(String[] args) throws SQLException {
// 1. 创建并初始化一个数据源;
DataSource dataSource = new MysqlDataSource();
// 把dataSource对象转为MysqlDataSource类型
// setUrl是MysqlDataSource类的方法要调用需先将对象转为MysqlDataSource类型
((MysqlDataSource)dataSource).setUrl
("jdbc:mysql://127.0.0.1:3306/JDBCProgram?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("xxxxx");
// 2. 和数据库服务器建立连接;
Connection connection = dataSource.getConnection();
// 3. 构造 SQL 语句
String sql = "insert into student values(1, 'Mike')";
// 使用PreparedStatement对sql语句进行预编译
PreparedStatement statement = connection.prepareStatement(sql);
// 4. 执行 SQL 语句
int ret = statement.executeUpdate();
System.out.println("ret = "+ ret);
// 5. 释放必要的资源
statement.close();
connection.close();
}
}
运行代码,在idea控制台有:
并在MySQL中查看Student表结果:
mysql> select* from student;
+------+------+
| id | name |
+------+------+
| 1 | Mike |
+------+------+
1 row in set (0.00 sec)
编写JDBC代码需要以下五个步骤:
3.1 创建并初始化一个数据源
(1)数据源即数据的源头,此处数据来源于数据库,即此处要描述数据库服务器在哪里;
数据库中使用DataSourse接口进行描述;
(2)在创建并初始化一个数据源,也可以无需向上转型+向下转型,直接使用MysqlDataSource:
MysqlDataSource dataSource = new MysqlDataSource();
但这种写法后续会使得MysqlDataSource类名扩散到代码其他地方;
向上转型+向下转型的写法可以使MysqlDataSource类名不会扩散到代码的其他地方,后续如果需要修改数据库为别的数据库,代码改动比较小,即mysql与程序之间的耦合较低;
二种方式均可使用;
(3)URL即唯一资源定位符,用于描述网络上某个资源所在的位置,此处设置为:
((MysqlDataSource)dataSource).setUrl
("jdbc:mysql://127.0.0.1:3306/JDBCProgram?characterEncoding=utf8&useSSL=false");
① jdbc:mysql表示:此url用于给jdbc操作mysql数据库使用的,如果使用其他数据库如Oracle则写为:jdbc:oracle;
② 127.0.0.1为本地回环地址,表示本主机;
③ 3306为数据库服务器默认端口号,标记某一主机上的进程;
④ JDBCProgram为数据库名(自行创建);
⑤ 在URL中?表示访问资源时需要的参数;
⑥ characterEncoding=utf8&useSSL=false分别表示字符集为utf8和不加密,SSL是一个加密协议,此处设置为false表示不加密;
(3)除设置URL之外,还需设置User和Passward才能访问数据库服务器,用户名默认为root,这是mysql默认自带的用户,密码为安装数据库时的密码;
(4)经过第一步后,只是描述了数据库的位置与用户名、密码等,还没有进行连接;
3.2 和数据库服务器建立连接
(1)使用getConnection方法与数据库服务器建立连接,并用Connection类型的变量来接受返回值,注意选择第一个jdbc的Connection:
(2)如果getConnection方法正常运行则连接建立成功,如果连接建立失败会直接抛异常:
执行SQL或操作数据库中出现问题,一般都会抛出SQLException异常;
(3)注意区别连接(Connection)与链接(Link):
连接:进行网络通信双方的抽象关系;
链接:快捷方式;
3.3 构造SQL语句
基于以下数据库与数据表:
mysql> use jdbcprogram
Database changed
mysql> show tables;
+-----------------------+
| Tables_in_jdbcprogram |
+-----------------------+
| student |
+-----------------------+
1 row in set (0.00 sec)
mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
(1)构造的SQL语句与在MySQL中构造的规定相同;
(2)如果请求是个SQL字符串,服务器是可以处理的。服务器就需要对SQL进行解析。
客户端数目庞大时会导致服务器压力很大,故而在客户端使用PreparedStatement对SQL语句进行预编译,对SQL语句进行解析检查,解析完毕后把结构化数据发给数据库服务器,就可以减轻服务器的压力;
3.4 执行SQL语句
(1)注意SQL语句的insert、delete和update操作都是使用executeUpdate方法进行执行的,返回值是int类型数据,表示影响的行数;
(2)select操作使用的是executeQuery方法;
3.5 释放必要的资源
(1)数据库的客户端与服务器进行通信时,会消耗一定的系统资源,如CPU、内存、硬盘、带宽等等。为了防止服务器同时处理多个客户端造成系统资源受限,当客户端不使用服务器时,就对资源进行释放;
(2)语句与连接均需要释放,需要先释放语句再释放连接。
释放的顺序与创建的顺序是相反的。
(3)除Datsource之外,还有一种DriverManager的写法,这种写法是通过反射的方式加载驱动包中的类,进一步进行后续操作的。
但并不建议使用这种写法,反射属于java开发的特殊手段,其代码可读性非常差,编译期难以对代码的正确性进行检查,容易产生运行时异常,建议不到万不得已不要使用反射;
并且DataSource内置了数据库连接池,可以复用连接,提高连接服务器的效率;
4. JDBC代码的优化
对于上文的JDBC代码,要插入的数据是硬编码,但是让用户编码是不现实的,故而需要将数据通过其他方式供用户输入。
4.1 从控制台输入
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;
public class JDBCInsert {
public static void main(String[] args) throws SQLException {
Scanner scanner = new Scanner(System.in);
// 1. 创建并初始化一个数据源;
DataSource dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl
("jdbc:mysql://127.0.0.1:3306/JDBCProgram?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("xxxxx");
// 2. 和数据库服务器建立连接;
Connection connection = dataSource.getConnection();
// 3. 从控制台读取用户输入的内容
System.out.println("请输入学生姓名:");
String name = scanner.next();
System.out.println("请输入学生学号:");
int id = scanner.nextInt();
// 4. 构造 SQL 语句
String sql = "insert into student values(" + id + ", '"+name + "')";
// 预编译
PreparedStatement statement = connection.prepareStatement(sql);
// 5. 执行 SQL 语句
int ret = statement.executeUpdate();
System.out.println("ret = "+ ret);
// 6. 释放必要的资源
statement.close();
connection.close();
}
}
运行代码,在控制台输入一下信息:
在mysql中查看Student表:
mysql> select* from student;
+------+------+
| id | name |
+------+------+
| 1 | Mike |
| 2 | Mary |
+------+------+
2 rows in set (0.00 sec)
4.2 避免SQL注入的SQL语句
在上例代码中,构造的SQL语句为:
String sql = "insert into student values(" + id + ", '"+name + "')";
如果用户输入的name形如:王五');select* from ***,导致看似一条SQL语句变为多个语句,就会出现SQL注入问题,如果再携带drop database之类的语句,可能会对系统造成更大的伤害。
针对以上问题,可以借助PreparedStatement的拼装功能实现:
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;
public class JDBCInsert {
public static void main(String[] args) throws SQLException {
Scanner scanner = new Scanner(System.in);
// 1. 创建并初始化一个数据源;
DataSource dataSource = new MysqlDataSource();
// 把dataSource对象转为MysqlDataSource类型
// setUrl是MysqlDataSource类的方法要调用需先将对象转为MysqlDataSource类型
((MysqlDataSource)dataSource).setUrl
("jdbc:mysql://127.0.0.1:3306/JDBCProgram?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("xxxxx");
// 2. 和数据库服务器建立连接;
Connection connection = dataSource.getConnection();
// 3. 从控制台读取用户输入的内容
System.out.println("请输入学生姓名:");
String name = scanner.next();
System.out.println("请输入学生学号:");
int id = scanner.nextInt();
// 4. 构造 SQL 语句
String sql = "insert into student values(?, ?)";
// 使用PreparedStatement对sql语句进行预编译
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1, id);
statement.setString(2, name);
// 打印statement需在拼接数据之后
System.out.println(statement);
// 5. 执行 SQL 语句
int ret = statement.executeUpdate();
System.out.println("ret = "+ ret);
// 6. 释放必要的资源
statement.close();
connection.close();
}
}
输入学生姓名与学号后,控制台输出结果如下:
在mysql中查看Student表:
mysql> select* from student;
+------+------+
| id | name |
+------+------+
| 1 | Mike |
| 2 | Mary |
| 3 | John |
+------+------+
3 rows in set (0.00 sec)
注:(1)构造的SQL语句中的2个?是两个占位符,statement.setInt与statement.setString方法就可以把占位符替换为指定的值,
statement.setInt(1, id);
statement.setString(2, name);
两个参数,第一个数字表示对应的问号的序号,从1开始计数:
分别表示将第一个占位符替换为id的值,第二个占位符替换为name的值,当用户输入给id和name赋值后,就会通过该方法自动替换;
(2)可以使用打印statement的方法查看具体拼接情况,需将该语句置于拼接数据之后;
假如代码执行出错了也可以把statement打印出来查看具体语法是否出错;
5. 编写JDBC代码实现Select
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JDBCSelect {
public static void main(String[] args) throws SQLException {
// 1. 创建并初始化数据源
DataSource dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl
("jdbc:mysql://127.0.0.1:3306/JDBCProgram?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("xxxxx");
// 2. 建立连接
Connection connection = dataSource.getConnection();
// 3. 构造SQL语句
String sql = "select* from Student";
PreparedStatement statement = connection.prepareStatement(sql);
// 4. 执行SQL语句
ResultSet resultSet = statement.executeQuery();
// 5. 遍历结果集合
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
System.out.println("id = " + id +", name = " + name);
}
// 6. 释放资源
resultSet.close();
statement.close();
connection.close();
}
}
控制台输出结果为:
注:(1)执行SQL的语句为:
ResultSet resultSet = statement.executeQuery();
对比SQL实现Insert的executeUpdate方法返回的是一个int类型数据,实现Select的executeQuery方法返回的是一个ResultSet类型对象。
该对象可以视为是一张表,初始时光标指向表首行,可以使用getXXX方法获取当前光标指向的行的数据。每调用一次next,就使光标下移一行,当光标遍历完整张表,再调用next时,就会返回false;
(2)getXXX方法用于取出这一行指定列的值,使用的方法要与列的类型匹配;
参数可以是第几列的下标,也可以是列名,更推荐使用列名;
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
System.out.println("id = " + id +", name = " + name);
}
(3)实现Select的程序在释放资源时,相较于Insert,需要多释放一个resultSet,可以将查询结果的临时表视为一个resultSet;