1.1 JDBC的概述
1.1.1 什么是JDBC
JDBC(Java Data Base Connectivity,java数据库连接)
是一种用于执行SQL语句的Java API,由一组用Java语言编写的类和接口组成。
同时JDBC也是个商标名。
1.1.2 什么是数据库驱动
1.1.3 为什么学习JDBC
没有JDBC的时候,如果现在要开发一套系统,使用Java连接MySQL数据库,那么这时候Java程序员需要了解MySQL驱动API,如果使用Java连接Oracle数据库,那么这个时候Java程序员需要了解Oracle数据库驱动API。
1.2 JDBC的入门
1.2.1 JDBC的环境准备
1.2.2 入门案例
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class demo01{
public static void main(String[] args) throws Exception{
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获得连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/emp","root","266531");
//3.基本操作:执行SQL
//3.1获得执行SQL语句的对象
Statement statement = conn.createStatement();
//3.2编写SQL语句
String sql = "select * from emp";
//3.3执行SQL
ResultSet rs = statement.executeQuery(sql);
//3.4遍历emp表
while (rs.next()){
System.out.print(rs.getInt("id")+" ");
System.out.print(rs.getString("name")+" ");
System.out.print(rs.getInt("salary")+" ");
System.out.print(rs.getInt("age")+" ");
System.out.println(rs.getInt("rid"));
}
//4.释放资源
rs.close();
statement.close();
conn.close();
}
}
2.1 JDBC的API详解
2.1.1 DriverManager:驱动管理类
2.1.1.1 作用一:注册驱动
这个方法可以完成驱动的注册,但是实际开发中一般不会使用这个方法完成驱动的注册。
原因:
如果需要注册驱动,使用DriverManager.registerDriver(new Driver());
但是查看源代码发现,在代码中有一段静态代码块,静态代码块已经调用了注册驱动的方法。
如果再手动调用该方法注册驱动,就会导致驱动被注册两次。实际开发中一般会采用:
2.1.1.2 作用二:获得连接
这个方法就是用来获得与数据库连接的方法:这个方法中有三个参数:
- url:与数据库连接的路径
- user:与数据库连接的用户名
- password:与数据库连接的密码
主要关注url的写法
jdbc:mysql://localhost:3306/emp
- jdbc:连接数据库的协议
- mysql:是jdbc的子协议
- localhost:要连接的MySQL数据库服务器的主机地址(连接是本机就可以写成localhost,如果连接的不是本机的,就需要写上连接主机的IP地址)
- 3306:MySQL数据库服务器的端口号
- emp:数据库名称
url如果连接的是本机的路径,可以简化为如下格式:
jdbc:mysql:///emp
2.1.2 Connection:与数据库连接对象
2.1.2.1 作用一:创建执行SQL语句的对象
执行SQL语句对象:
- Statement:执行SQL
- CallableStatement:执行数据库中存储过程
- PreparedStatement:执行SQL,对SQL进行预处理,解决SQL注入漏洞。
2.1.2.2 作用二:管理事务
2.1.3 Statement:执行SQL
2.1.3.1 作用一:执行SQL
- 执行SQL的方法
boolean execute(String sql); 用来执行查询,修改,添加,删除的SQL语句。
1.ResultSet executeQuery(String sql); 用来执行查询(执行select语句)。
2.Int executeUpdate(String sql); 用来执行修改,添加,删除的SQL语句,返回的int表示影响的行数
2.1.3.2 作用二:执行批处理
2.1.4 ResultSet:表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
通过select语句的查询结果。
只有查询语句才有结果集
2.1.4.1 结果集的遍历
代码实现:
2.1.4.2 结果集的获取
- 结果集获取可以使用结果集中的:
getXXX();方法通常都会有一个重载的方法。
getXXX(int 列号)
getXXX(String 列名)
通常使用后者
2.2 JDBC的资源释放
JDBC程序执行结束后,将与数据库进行交互的对象释放掉,通常是ResultSet,Statement,Connection。
这几个对象尤其是Connection对象是非常稀有的。所以这个对象一定要做到尽量晚创建,尽早释放。
- 将资源释放的代码写入到finally的代码块中。
- 资源释放的代码应该写的标准
资源释放标准代码:
if(rs!=null){
try{
rs.close();
}catch (SQLException e){
e.printStackTrace();
}
rs = null;
}
if(statement!=null){
try{
rs.close();
}catch (SQLException e){
e.printStackTrace();
}
rs = null;
}
if(conn!=null){
try{
rs.close();
}catch (SQLException e){
e.printStackTrace();
}
rs = null;
}
2.3 JDBC的CRUD操作
2.3.1 保存操作代码实现
public static void save() {
Connection conn = null;
Statement stmt = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///emp", "root", "266531");
stmt = conn.createStatement();
String sql = "insert into emp values(null,'Spike',1500,4,5)";
int num = stmt.executeUpdate(sql);
if (num > 0) {
System.out.println("保存用户成功!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
conn = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
stmt = null;
}
}
}
2.3.2 修改操作代码实现
public static void change(){
Connection conn = null;
Statement stmt = null;
try{
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///emp", "root", "266531");
stmt = conn.createStatement();
String sql="update emp set salary=5000 where name='Rarity'";
int num = stmt.executeUpdate(sql);
if(num>0){
System.out.println("信息修改成功");
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(conn!=null){
try{
conn.close();
}catch (SQLException se){
se.printStackTrace();
}
conn=null;
}
if(stmt!=null){
try{
stmt.close();
}catch (SQLException se){
se.printStackTrace();
}
stmt=null;
}
}
}
2.3.3 删除操作代码实现
2.3.4 查询操作代码实现
2.3.4.1 查询多条记录
3.1 JDBC的工具类的抽取
3.1.1 抽取一个JDBC 的工具类
因为传统JDBC的开发,注册驱动,获得连接,释放资源这些代码都是重复编写的。所以可以将重复的代码提取到一个类中来完成。
public class JDBCUtils {
private static final String driverClassName;
private static final String url;
private static final String username;
private static final String password;
static{
driverClassName = "com.mysql.jdbc.Driver";
url="jdbc:mysql:///emp";
username="root";
password="266531";
}
/*注册驱动*/
public static void loadDriver(){
try {
Class.forName(driverClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/*获得连接的方法*/
public static Connection getConnection(){
Connection conn = null;
try {
loadDriver();
conn = DriverManager.getConnection(url, username, password);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return conn;
}
/*释放资源的方法*/
public static void release(Statement stmt,Connection conn){
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
conn = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
stmt = null;
}
}
public static void release(Statement stmt, Connection conn, ResultSet rs){
if(conn!=null){
try{
conn.close();
}catch (SQLException se){
se.printStackTrace();
}
conn = null;
}
if (stmt!=null){
try{
stmt.close();
}catch (SQLException se){
se.printStackTrace();
}
stmt=null;
}
if (rs!=null){
try{
rs.close();
}catch (SQLException se){
se.printStackTrace();
}
rs=null;
}
}
3.2 JDBC的配置信息提取到配置文件
3.2.1 配置文件
- 属性文件
格式:扩展名是.properites
内容:key=value - XML文件
3.2.2 提取信息到配置文件
- 定义一个配置文件
3.2.3 在工具类中解析属性文件
- 获取到具体内容为常量赋值
//获取属性文件中的内容:
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src/db.properties"));
} catch (IOException e) {
e.printStackTrace();
}
driverClassName = properties.getProperty("driveClassName");
url=properties.getProperty("url");
username=properties.getProperty("username");
password=properties.getProperty("password");
4.1 JDBC的SQL注入漏洞
4.1.1 什么是SQL注入漏洞
- 输入用户名
Rarity’ or ‘1=1 密码随意
Rarity’ – 密码随意
4.1.2 JDBC的SQL注入漏洞分析
输入的用户名为:
- aaa’ or '1=1
- aaa’ –
原因:
- 在变量中存在SQL的关键字
or
–
都是SQL中的关键字
4.1.3 JDBC的SQL注入漏洞解决:PreparedStatement
需要采用PreparedStatement对象解决SQL注入漏洞。这个对象会将SQL预先进行编译,使用?
作为占位符。?
所代表内容是SQL所固定。再次传入变量(包含SQL关键字)
易错点:已经过PrepareStatement预编译的sql语句执行时不必再传入sql
public class UserDao {
public static void main(String[] args) {
boolean login = login("Rarity",5000);
System.out.println(login);
}
public static boolean login(String username,int salary) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select * from emp where name = ? and salary = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setInt(2, salary);
rs = pstmt.executeQuery(); //此时这里面就不再加sql语句了
if (rs.next()) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.release(pstmt, conn, rs);
}
return false;
}
/* public static boolean login(String name,int salary){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
conn = JDBCUtils.getConnection();
stmt = conn.createStatement();
String sql = "select salary from emp where name ='" + name +"' and salary =" + salary;
rs = stmt.executeQuery(sql);
if (rs.next()){
return true;
}
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtils.release(stmt,conn);
}
return false;
}*/
}
4.1.4 PreparedStatement的CRUD操作
4.1.4.1 保存操作代码实现
public static void saveUser(String name,String local){
Connection conn = null;
PreparedStatement pstmt = null;
try{
conn = JDBCUtils.getConnection();
String sql = "insert into user values(null,?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1,name);
pstmt.setString(2,local);
int num = pstmt.executeUpdate();
if(num>0){
System.out.println("用户信息保存成功");
}
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtils.release(pstmt,conn);
}
System.out.println("用户信息保存失败");
}
4.1.4.2 修改操作代码实现
public static void updateUser(int id,String name,String local){
Connection conn = null;
PreparedStatement pstmt = null;
try{
conn = JDBCUtils.getConnection();
String sql = "update user set uname = ? ,local = ? where uno = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1,name);
pstmt.setString(2,local);
pstmt.setInt(3,id);
int num = pstmt.executeUpdate();
if(num > 0){
System.out.println("信息修改完成");
}
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtils.release(pstmt,conn);
}
}
4.1.4.3 删除操作代码实现
public static void deleteUser(int id){
Connection conn = null;
PreparedStatement pstmt = null;
try{
conn = JDBCUtils.getConnection();
String sql = "delete from user where uno = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1,id);
int num = pstmt.executeUpdate();
if(num>0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtils.release(pstmt,conn);
}
}
4.1.4.4 查询操作代码实现
public static void selectUser(int id){
Connection conn = null;
ResultSet rs = null;
PreparedStatement pstmt = null;
try{
conn = JDBCUtils.getConnection();
String sql = "select * from user where uno = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1,id);
rs = pstmt.executeQuery();
if(rs.next()){
System.out.println(rs.getInt("uno") + " " + rs.getString("uname") + " " + rs.getString("local"));
}
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtils.release(pstmt,conn,rs);
}
}
5.1 JDBC的批处理操作
5.1.1 什么是批处理
之前进行JDBC的操作的时候,都是一条SQL语句执行。现在如果使用批处理,可以将一批SQL一起执行。
5.1.2 批处理的基本使用
public static void demo(){
Connection conn = null;
Statement stmt = null;
try{
conn = JDBCUtils.getConnection();
stmt = conn.createStatement();
//编写一批SQL语句:
String sql1 = "create database test1";
String sql2 = "use test1";
String sql3 = "create table user(id int primary key auto_increment,name varchar(20))";
String sql4 = "insert into user values(null,'Rarity')";
String sql5 = "insert into user values(null,'TwilightSparkle')";
String sql6 = "insert into user values(null,'FlutterShy')";
//添加到批处理
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
stmt.addBatch(sql4);
stmt.addBatch(sql5);
stmt.addBatch(sql6);
//执行批处理:
stmt.executeBatch();
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtils.release(stmt,conn);
}
}
5.1.3 批量插入(使用PreparedStatement)
批处理参数:rewriteBatchStatements=true
6.1 JDBC-事务
public static void tranfer(){
Connection conn = null;
PreparedStatement pstmt = null;
try{
conn = JDBCUtils.getConnection();
//开启事务
conn.setAutoCommit(false);
String sql = "UPDATE Remainder SET money = money+ ? WHERE NAME= ?";
pstmt = conn.prepareStatement(sql);
pstmt.setDouble(1,-1000);
pstmt.setString(2,"Rarity");
pstmt.executeUpdate();
pstmt.setDouble(1,1000);
pstmt.setString(2,"TwilightSparkle");
pstmt.executeUpdate();
//提交事务
conn.commit();
}catch (Exception e){
//如果出现异常,就进行回滚
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.release(pstmt,conn);
}
}