通过Java代码实现的操作数据库的程序,本质上是一个MySQL客户端,我们不能实现MySQL服务器,因为数据库服务器非常复杂.
那么我们具体要如何实现这样的客户端呢?
像主流的一些数据库,为了方便程序员实现客户端程序,会提供一些API(Application Programming Interface)接口,称为"数据库SDK(software development kit,SDK的概念更广泛,除了可以提供一些API之外,还会提供一些可执行程序(工具)辅助开发/调试)",可以借助这些API比较方便的访问数据库服务器.
【补充】:
Java这套操作数据库的API,就称为JDBC
【说明】:
驱动包的概念,各数据库厂商为了能够适配JDBC就需要写一些额外的程序来完成这个工作,厂商提供的这套额外的程序,就称为"JDBC驱动包"
也就是说,当我们使用Java操作MySQL时,就需要安装MySQL提供的驱动包,当我们使用Java操作Oracle时,就需要安装Oracle提供的驱动包,使用Java操作各种数据库,就需要安装对应数据库提供的驱动包,才能正常使用
首先我们需要先找到MySQL驱动包,我们可以在中央仓库中去找,驱动包属于是"第三方库",因为第三方库有很多,于是就有大佬把第三方库都收集起来,放在了一个统一的网站上,这个网站类似于我们手机上的应用商店,我们可以在应用商店里下载各种APP.
具体步骤:
(1)登录中央仓库
中央仓库
(2)
(3)选择版本(要和数据库服务器的版本相匹配)
(4)下载相应的版本
下载完之后就得到了一个这样的文件
对于这个以.jar为后缀的文件,叫做jar包,本质上类似于rar的压缩包
jar包里主要包含的是一些.class文件和配置文件
JDBC,即Java Database Connectivity,java数据库连接。是一种用于执行SQL语句的Java API,它是Java中的数据库连接规范。这个API由java.sql.* 和javax.sql.* 包中的一些类和接口组成,它为Java开发人员操作数据库提供了一个标准的API,可以为多种关系数据库提供统一访问
【小结】:
JDBC本质上就是Java对于对于各种数据库差异性的封装,目的是简化程序员的学习成本
JDBC 为多种关系数据库提供了统一访问方式,作为特定厂商数据库访问API的一种高级抽象,它主要包含一些通用的接口类
JDBC访问数据库的层次结构:
JDBC的优势:
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
public class JDBCHello {
public static void main(String[] args) {
DataSource dataSource=new MysqlDataSource();//向上转型
((MysqlDataSource)dataSource).setUrl();//向下转型
//为了调用MysqlDataSource中的setUrl方法
}
}
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
public class JDBCHello {
public static void main(String[] args) {
MysqlDataSource dataSource=new MysqlDataSource();
dataSource.setUrl();
}
}
DataSource是一个interface不能直接实例化,而MysqlDataSource实现了这个接口
【补充】:
- DataSource这个类来自于我们导入的驱动包
- 上面两段代码虽是等效的,但在实际开发中我们更常使用的是第一种,因为第一种写法里得到的数据源的类型是DataSource类型,后续写其他的代码/方法时,如果使用到数据源,持有的类型也是DataSource类型,DataSource是通用的类型,可以指代任何数据库,未来一旦更换数据库,代码的改动是非常小的
- 对于第二种写法,得到的数据源是MysqlDataSource类型,后续的其他代码,方法/类当中如果用到数据源,持有的类型也是MySqlDataSource类型,只针对MySQL的类型,后面一旦更换数据库,代码改动成本会很大
https://www.baidu.com/ (这就是一个URL)
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
public class Test1 {
public static void main(String[] args) {
//1.使用DataSource描述MySQL服务器的位置
DataSource dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java104?characterEncoding=utf8&useSSL=false");
}
}
【注意】:
这里的URL并不需要背,使用时直接复制就可以了,背下来也没有什么意义
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
public class Test1 {
public static void main(String[] args) {
//1.使用DataSource描述MySQL服务器的位置
DataSource dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java104?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("wp143212");
}
}
数据库里的通信方式采取的是有连接的方式
【有连接方式的优缺点】:
优点:能够在通信之前"投石问路",看看通信环境是否通畅
缺点:需要对连接进行管理,尤其是不用的连接,要记得即使释放.对于用过座机的同学,肯定都知道的一个bug,就是电话没扣严,就会一直扣电话费
public static void main(String[] args) throws SQLException {
//1.使用DataSource描述MySQL服务器的位置
DataSource dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java104?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("wp143212");
//2.和数据库服务器建立连接
Connection connection=dataSource.getConnection();
System.out.println(connection);
}
【注意】:
- getConnection方法的返回值类型是Connection,我们一定要选择的是java.sql包下的Connection
- SQLException属于是受查异常,所以我们需要做处理,上面处理方式是通过throws声明 当建立连接失败的时候会抛出这个异常,例如DataSource中的任何一处填写错误都会连接失败,密码错误等
执行结果:如果是这样说明建立连接成功
但如果我输入的密码是错误的:
首先我们在MySQL中创建了一个学生表
在这里插入图片描述
public static void main(String[] args) throws SQLException {
//1.使用DataSource描述MySQL服务器的位置
DataSource dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java104?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("wp143212");
//2.和数据库服务器建立连接
Connection connection=dataSource.getConnection();
System.out.println(connection);
//3.构造SQL语句
String sql="insert into student values(1,'张三')";
PreparedStatement statement=connection.prepareStatement(sql);
//4.执行SQL语句
int n=statement.executeUpdate();
System.out.println(n);
}
【注意】:
insert,update,delete都是通过executeUpdate方法来执行的
select是通过executeQuery方法来执行的executeUpdate方法的返回值是一个整数表示影响到几行
执行结果:
如果在重复执行一次代码就会报错
public static void main(String[] args) throws SQLException {
//1.使用DataSource描述MySQL服务器的位置
DataSource dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java104?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("wp143212");
//2.和数据库服务器建立连接
Connection connection=dataSource.getConnection();
System.out.println(connection);
//3.构造SQL语句
String sql="insert into student values(1,'张三')";
PreparedStatement statement=connection.prepareStatement(sql);
//4.执行SQL语句
int n=statement.executeUpdate();
System.out.println(n);
//5.断开连接,释放资源
statement.close();
connection.close();
}
后创建的资源先释放
在String sql=“insert into student values(1,‘张三’)”;这个语句中我们直接就把id和name在代码中写死了,如果先让用户输入id和name,需要才需下面这种做法
public static void main(String[] args) throws SQLException {
//1.使用DataSource描述MySQL服务器的位置
DataSource dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java104?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("wp143212");
//2.和数据库服务器建立连接
Connection connection=dataSource.getConnection();
System.out.println(connection);
//3.构造SQL语句
System.out.println("输入学号");
Scanner scanner=new Scanner(System.in);
int num=scanner.nextInt();
System.out.println("输入姓名:");
String name=scanner.next();
String sql="insert into student values("+num+",'"+name+"')";
PreparedStatement statement=connection.prepareStatement(sql);
//4.执行SQL语句
int n=statement.executeUpdate();
System.out.println(n);
//5.断开连接,释放资源
statement.close();
connection.close();
}
上述通过字符串拼接的方式,可完成插入数据的功能,但是这种写法有两个主要的去缺点:
代码比较丑陋,同时存在一定的风险(SQL注入攻击),由于name是用户自己构造的,如果够在的name=’ );drop table…
这就会出现删表的严重后果
推荐写法:
public static void main(String[] args) throws SQLException {
//1.使用DataSource描述MySQL服务器的位置
DataSource dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java104?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("wp143212");
//2.和数据库服务器建立连接
Connection connection=dataSource.getConnection();
System.out.println(connection);
//3.构造SQL语句
System.out.println("输入id:");
Scanner scanner=new Scanner(System.in);
int num=scanner.nextInt();
System.out.println("输入姓名:");
String name=scanner.next();
//使用?作为占位符,后续使用statement对象针对?进行替换
String sql="insert into student values(?,?)";
PreparedStatement statement=connection.prepareStatement(sql);
statement.setInt(1,num);
statement.setString(2,name);
System.out.println(statement);
//4.执行SQL语句
int n=statement.executeUpdate();
System.out.println(n);
//5.断开连接,释放资源
statement.close();
connection.close();
}
执行结果:
【优点】:
这种通过?占位的写法解决了字符串拼接的问题:
能自动补充’ ’
能对SQL注入攻击进行校验
【小结】:
DataSource 描述了数据源(数据的位置,服务器的位置) Connection
表示一个连接,需要先建立连接才能进行通信,一个服务器可以同时处理多个连接 PreparedStatement
描述好了一个SQL语句,需要对象来执行这个SQL语句
(1)update操作示例
public static void main(String[] args) throws SQLException {
//1.使用DataSource描述MySQL服务器的位置
DataSource dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java104?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("wp143212");
//2.和数据库服务器建立连接
Connection connection=dataSource.getConnection();
System.out.println(connection);
//3.构造SQL语句
System.out.println("输入要修改的同学的id:");
//next 读到空白符(空格,制表符,翻页符,换行符,回车符,垂直制表符)
//nextLine读到换行符
Scanner scanner=new Scanner(System.in);
int id=scanner.nextInt();
System.out.println("输入要把该同学的姓名改为:");
String name=scanner.next();
//使用?作为占位符,后续使用statement对象针对?进行替换
String sql="update student set name=? where id=?";
PreparedStatement statement=connection.prepareStatement(sql);
statement.setString(1,name);
statement.setInt(2,id);
System.out.println(statement);
//4.执行SQL语句
int n=statement.executeUpdate();
System.out.println(n);
//5.断开连接,释放资源
statement.close();
connection.close();
}
执行结果:
(2)delete操作示例
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 Test3 {
public static void main(String[] args) throws SQLException {
//1.描述数据源
DataSource dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java104?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("wp143212");
//2.建立连接
Connection connection= dataSource.getConnection();
//3.构造SQL语句
System.out.println("输入要删除的id");
Scanner scanner=new Scanner(System.in);
int id=scanner.nextInt();
String sql="delete from student where id=?";
PreparedStatement preparedStatement= connection.prepareStatement(sql);
preparedStatement.setInt(1,id);
//4.执行SQL语句
int n=preparedStatement.executeUpdate();
System.out.println("n="+n);
//5.关闭资源
preparedStatement.close();
connection.close();
}
}
执行结果:
对于上面的insert,delete,update三个操作示例,我们发现有很多的相似之处,趋于同质化,因此我们可以将他们封装起来变成同一套代码但支持不同的功能,很多的第三方库/框架也就是这么做的,实际开发中很少会直接使用JDBC因为写起来很麻烦,于是就衍生出了
很多框架,能够简化数据库操作,例如MyBatis框架,就是用来操作数据库的
(3)select 操作示例
查找操作和前面的三种操作就不太一样了,这里多了一个步骤,要遍历结果集合
在介绍ResultSet对象的时候会演示
在Java JDBC编程中对数据库的操作均使用JDK自带的API统一处理,通常与特定的数据库驱动类是完全解耦的。所以掌握Java JDBC API(位于java.sql包下),即可掌握Java数据库编程
Connection接口实现类由数据库提供,获取Connetion对象通常有两种方式:
// 加载JDBC驱动程序
Class.forName("com.mysql.jdbc.Driver");
// 创建数据库连接
Connection connection = DriverManager.getConnection(url);
【两种方式的区别】:
概念: 连接池用于创建和管理数据库连接的缓冲池技术,缓冲池中的连接可以被任何需要他们的线程使用。当一个线程需要用JDBC对一个数据库操作时,将从池中请求一个连接。当这个连接使用完毕后,将返回到连接池中,等待为其他的线程服务
工作原理:
(1)建立数据库连接池对象(服务器启动)。
(2)按照事先指定的参数创建初始数量的数据库连接(即:空闲连接数)。
(3)对于一个数据库访问请求,直接从连接池中得到一个连接。如果数据库连接池对象中没有空闲的连接,且连接数没有达到最大(即:最大活跃连接数),创建一个新的数据库连接。
(4)存取数据库。
(5)关闭数据库,释放所有数据库连接(此时的关闭数据库连接,并非真正关闭,而是将其放入空闲队列中。如实际空闲连接数大于初始空闲连接数则释放连接)。
(6)释放数据库连接池对象(服务器停止、维护期间,释放数据库连接池对象,并释放所有连接)。
连接池的作用: 创建和释放数据库连接都是很耗时的操作,频繁这样的操作将占用大量的性能开销,导致性能低效。数据库连接池的解决方案就是在程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,当有请求来时,直接使用已经创建好的连接对数据库进行访问。这样省略了创建连接和销毁连接的过程。性能上得到了提高。
举例:有一位男生,他有一个愿望,每个月都能换一个女朋友,但是他又嫌弃新交一个女朋友需要消耗很多时间培养感情,由于这位男生还是一位优秀的程序猿,于是他就利用了"池"的思想,在与一个女生交往的同时,还在与很多其他女生培养感情(这些女生就处在"备胎池"),一旦他想换女朋友了,就从"备胎池"中选一个就可以了,也不用培养感情了。(以上故事纯属序构)
主要优点和作用:
Statement对象主要是将SQL语句发送到数据库中。JDBC API中主要提供了三种Statement对象
Statement:用于执行不带参数的简单SQL语句
PerparedStatement:用于执行带或不带参数的SQL语句;SQL语句会预编译在数据库系统;执行速度快于Statement对象
CallableStatement:用于执行数据库存储过程的调用
实际开发中最常用的是PreparedStatement对象。
PreparedStatement优点:
【执行SQL的两种常用方法】:
executeQuery() 方法执行后返回单个结果集的,也就是一个ResultSet对象,可以把这个对象视为一个临时表,通常用于select语句
executeUpdate()方法返回值是一个整数,指示受影响的行数,通常用于update、insert、delete语句
ResultSet对象它被称为结果集,它代表符合SQL语句条件的所有行,并且它通过一套get方法提供了对这些行中数据的访问 ResultSet里的数据一行一行排列,每行有多个字段,并且有一个记录指针,指针所指的数据行叫做当前数据行,我们只能来操作当前的数据行。我们如果想要取得某一条记录,就要使用ResultSet的next()方法 ,如果我们想要得到ResultSet里的所有记录,就应该使用while循环。
select操作示例:
package newcoder;
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 Test4 {
public static void main(String[] args) throws SQLException {
//1.描述数据库的位置
DataSource dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java104?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("wp143212");
//2.建立连接
Connection connection=dataSource.getConnection();
//3.构造SQL语句
String sql="select * from student";
PreparedStatement statement=connection.prepareStatement(sql);
//4.执行SQL语句
//executeQuery返回一个ResultSet对象,可以将这个对象视为一个临时表
ResultSet resultSet=statement.executeQuery();
//5.遍历临时表,拿到里面的数据
//resultSet可以简单的视为一个类似于"迭代器"一样的东西
while (resultSet.next()){
//通过ResultSet里的getXXX方法,获取指定的列
int id=resultSet.getInt(1);
String name= resultSet.getString(2);
System.out.println("id:"+id+" name:"+name);
}
//6.关闭连接,释放资源
resultSet.close();
statement.close();
connection.close();
}
}
执行结果:
图书管理系统(后续补充)