目录
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. 各种数据库如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就类似于应用商店,通过应用商店就可以访问到应用程序包并进行下载;
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代码需要以下五个步骤:
(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)经过第一步后,只是描述了数据库的位置与用户名、密码等,还没有进行连接;
(1)使用getConnection方法与数据库服务器建立连接,并用Connection类型的变量来接受返回值,注意选择第一个jdbc的Connection:
(2)如果getConnection方法正常运行则连接建立成功,如果连接建立失败会直接抛异常:
执行SQL或操作数据库中出现问题,一般都会抛出SQLException异常;
(3)注意区别连接(Connection)与链接(Link):
连接:进行网络通信双方的抽象关系;
链接:快捷方式;
基于以下数据库与数据表:
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语句进行解析检查,解析完毕后把结构化数据发给数据库服务器,就可以减轻服务器的压力;
(1)注意SQL语句的insert、delete和update操作都是使用executeUpdate方法进行执行的,返回值是int类型数据,表示影响的行数;
(2)select操作使用的是executeQuery方法;
(1)数据库的客户端与服务器进行通信时,会消耗一定的系统资源,如CPU、内存、硬盘、带宽等等。为了防止服务器同时处理多个客户端造成系统资源受限,当客户端不使用服务器时,就对资源进行释放;
(2)语句与连接均需要释放,需要先释放语句再释放连接。
释放的顺序与创建的顺序是相反的。
(3)除Datsource之外,还有一种DriverManager的写法,这种写法是通过反射的方式加载驱动包中的类,进一步进行后续操作的。
但并不建议使用这种写法,反射属于java开发的特殊手段,其代码可读性非常差,编译期难以对代码的正确性进行检查,容易产生运行时异常,建议不到万不得已不要使用反射;
并且DataSource内置了数据库连接池,可以复用连接,提高连接服务器的效率;
对于上文的JDBC代码,要插入的数据是硬编码,但是让用户编码是不现实的,故而需要将数据通过其他方式供用户输入。
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)
在上例代码中,构造的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打印出来查看具体语法是否出错;
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;