一、什么是JDBC
JDBC全称为Java数据库连接(Java Database Connectivity),是一套用于执行SQL语句的Java API。应用程序可以通过这套API连接到关系型数据库,并使用SQL语句完成对数据中数据的查询、增加、更新和删除等操作。
JDBC在应用程序与数据库之间起到了一个桥梁作用,当应用程序使用JDBC访问特定的数据库时,需要通过不同数据库驱动(所谓数据库驱动就是数据库厂商提供链接数据库的jar包)与不同数据库进行连接,连接后即可对该数据库进行相应的操作。
由于不同的数据库厂商提供的数据库驱动各不相同,在使用不同的数据库的时候需要学习对应的数据库驱动的api,对于开发人员来说学习成本会非常高.
于是sun公司提供了jdbc规范.本质上是一大推的接口,所有的数据库驱动都实现这一套接口,这样如果换数据库只需要换驱动包即可.
驱动包和jdbc规则的关系是父接口和实现类的关系.
JDBC常用API主要位于Java.sql包中,该包定义了一系列访问数据库的接口和类:
1.Driver 接口:代表驱动程序;
2.DriverManager 类:驱动程序管理员;
3.Connection 接口:代表数据库连接;
4.Statement 、PreparedStatement、CallableStatement 接口:代表数据库操作对象;
5.ResultSet 接口:代表结果集;
6.DatabaseMetadata、ResultSetMetadata接口:代表元数据;
7.Types 类:代表JDBC类型。
二、JDBC入门代码
1 、在IDEA下新建工程并在工程目录下新建lib文件夹
2、 将数据库驱动拷贝到lib文件夹下
这是Java与数据库建立链接的前提基础。
然后现在来准备数据,构建一个数据库表:
前提条件准备完毕,开始在IDEA中写代码与数据库建立链接。
导入包。
public class Demo1 { //抛异常
public static void main(String[] args) throws Exception {
//1 注册驱动 //报错1、抛异常2、或者try/catch捕获异常
DriverManager.registerDriver(new Driver());
//2 获取链接 //协议:子协议://IP:端口号3306/数据库名称
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb_21","root","*******");
//3 获取执行sql语句的对象
Statement stat = conn.createStatement();
//4 执行sql语句并拿到结果
//查询操作使用的方法只能是executeQuery
ResultSet rs=stat.executeQuery("select * from user"); //ResultSet结果集/虚拟表
//5 处理执行结果
while (rs.next() /*游标,下一行有数据返回true,否则false*/){//每一行
System.out.println(rs.getObject(1));//每一列
System.out.println(rs.getObject(2));
}
//6 释放资源
conn.close();
stat.close();
rs.close();
}
}
异常的处理方法:1、直接抛异常,throws Exception 2、用try / catch捕获处理异常
第一步(1)注册驱动 DriverManager.registerDriver(new Driver() );
用驱动包中的驱动程序管理员创建注册驱动Driver。
第二步(2)获取链接 Connection conn = DriverManager.getConnection(url:" jdbc:mysql://localhost:3306/mydb_21", user:" root ",password:" ******* ");
用驱动管理程序创建获取链接,getConnection方法有3个重载方法,上文的这个是包含3个string类型的方法,url:数据库地址,后面是 "协议 : 子协议 :// IP:端口号 / 数据库名称 "
user: 数据库用户名 ,password:密码。
重载方法2:
我们知道要链接到数据库需要数据库地址、数据库用户名和密码3样东西,无论如何都绕不开这些,这里2个参数保留了数据库地址参数,而将用户名密码封装在Properties中。
//2 获取链接 //协议:子协议://IP:端口号3306/数据库名称
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","*********");
Connection conn=DriverManager.getConnection("",info);
创建一个Properties对象info,将用户名密码封装其中。
重载方法3:
如上所述,连接数据库绕不开三要素,这里只需要一个参数url,实际上是用? 和&符号将用户名密码连到数据库地址后面:
//2 获取链接 //协议:子协议://IP:端口号3306/数据库名称
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb-21?user=root&password=*********");
类似网址中的:
CSDNhttps://mp.csdn.net/mp_blog/creation/editor?spm=1011.2124.3001.6192
第三步(3) 获取执行sql语句的对象
Statement stat = conn.createStatement();
用上一步的conn对象创建stat对象,用于执行sql语句。
第四步(4) 查询sql语句并拿到结果
ResultSet rs = stat.executeQuery("select* from user);
stat是用来执行sql语句select* from user的对象,将结果赋给rs变量,executeQuery方法是只能查询操作使用,返回值是ResultSet,还有一个方法是executeUpdate,返回值是int。
ResultSet是结果集/虚拟表。
第五步(5) 处理执行结果
while (rs.next() /*游标,下一行有数据返回true,否则false*/){//每一行
System.out.println(rs.getObject(1));//每一列
System.out.println(rs.getObject(2));
System.out.println(rs.getObject(3));
System.out.println(rs.getObject(4));
}
将执行结果(数据库数据)打印在终端上。
rs.next() 是游标,数据库下一行有数据时返回true,否则返回false。
getObject()是获取数据,因为不知道数据库表中存的是什么类型的数据,Object是所有类的子类。
第六步(6) 释放资源
conn.close();
stat.close();
rs.close(); 顺序无所谓,哪个关闭都行。
一定要记得关闭释放资源,否则内存会不够。
上述流程可以形象地抽象为下面一张图:
左边是IDEA,可抽象为城市A,右边是与之建立链接的数据库,可抽象为城市B。
中间要想建立连接需要搭建桥梁Connection,也需要建桥的人DriverManager,开往B的车辆Statement返回时以ResultSet的形式返回。
三、各部分详解
(1)DriverManager.registerDriver(new Driver() );
进入registerDriver方法的定义中,我们发现驱动被注册了2次,并且高度依赖驱动包。这是该语句的2个问题。
解决方法:使用驱动的加载。
Class.forName("com.mysql.jdbc.Driver"); 用该语句代替上面的注册驱动。
(2)stat执行sql语句
有executeQuery方法和executeUpdate方法,前者用于查询操作,后者用于增删改操作。
executeUpdate方法的返回值是int,以下是示例:
public class Demo2 {
@Test //抛异常
public void test01() throws Exception {
//1、加载驱动
Class.forName("com.mysql.jdbc.Driver");
Properties info = new Properties();
info.getProperty("user", "root");
info.getProperty("password", "********");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb-21");
Statement stat = conn.createStatement();
int i = stat.executeUpdate("insert into user values(5,'eee',29,'男')");
if (i > 0) {
System.out.println("更新成功");
} else {
System.out.println("更新失败");
}
conn.close();
stat.close();
}
}
PS:这里的@Test是: 在Java中,@Test
是JUnit测试框架中的一个注解,用于标记一个测试方法。使用 @Test
注解可以告诉JUnit测试运行器(test runner)该方法是一个测试方法,并且应该被执行。(不同@Test直接创建其它类也可以)
插入一条数据,如果插入成功返回1,失败返回0.
删除语句
public void test02() throws Exception{
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection
("jdbc:mysql://localhost:3306/mydb_21?user=root&password=*******");
Statement stat = conn.createStatement();
int i = stat.executeUpdate("delete from user where id = 4");
if(i > 0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
conn.close();
stat.close();
}
}
(3) 结果集的处理
像上面
//5 处理执行结果
while (rs.next() /*游标,下一行有数据返回true,否则false*/){//每一行
System.out.println(rs.getObject(1));//每一列
System.out.println(rs.getObject(2));
System.out.println(rs.getObject(3));
System.out.println(rs.getObject(4));
}
这就是结果集的处理的第一种方式;
又比如:
while (rs.next()){
System.out.println(rs.getInt("id"));
System.out.println(rs.getString("name"));
System.out.println(rs.getInt("age"));
}
这就是结果集的处理的第二种方式,但是这2种方法都存在问题。因此我们最好将结果集封装起来。
创建一个User类,用于创建对象将数据添加到结果集中。
User类:
public class User {
private int id;
private String name;
private int age;
private char sex;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
@Override
public String toString() {
return "User{" +
" id= " + id +
" ,name = " + name+
" ,age = " + age+
" ,sex = " + sex+
" }";
}
这里的User类实际上就是一个JavaBean(avaBean 是一种JAVA语言写成的可重用组件。为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,set和get方法获取。)
封装用集合,不用数组,因为数组大小固定不方便操作,用泛型ArrayList<>实现。
ResultSet rs = stat.executeQuery("select* from user");
ArrayList<User> list= new ArrayList<>();
while(rs.next()){
User u = new User();
u.setId(rs.getInt("id"));
u.setName(rs.getString("name"));
u.setAge(rs.getInt("age"));
u.setSex(rs.getString("sex"));
list.add(u);
}
conn.close();
stat.close();
rs.close();
(4)处理可能出现的异常
如上一段代码,如果在ResultSet rs = stat.executeQuery(...);执行完之后出现异常,那么后续代码不会执行,也就是资源不会关闭释放,那么会造成系统负担。因此我们需要在前面加上try /catch /finally的异常捕获机制来避免这种情况的发生。
public class Demo3 {
@Test
public void practice01() throws Exception {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection
("jdbc:mysql://localhost:3306/mydb_21?user=root&password=1194006164qwer");
stat = conn.createStatement();
rs = stat.executeQuery("select* from user");
while (rs.next() ){
System.out.println(rs.getObject(1));
System.out.println(rs.getObject(2));
System.out.println(rs.getObject(3));
System.out.println(rs.getObject(4));
}
}catch(Exception e) {
e.printStackTrace();
} finally {
conn.close();
stat.close();
rs.close();
}
}
}
注意:被try包裹后,finally中的3个变量就没有定义了,需要在开头声明成全局变量。
在关闭conn,stat,rs的时候也要注意可能会出现异常,就像关机的时候进程太多出现关机错误,所以也需要加上捕获异常的措施try-catch-finally。当conn,stat,rs不为null时,尝试正常close,如果失败,就手动置空。
完整格式:
public class Demo3 {
@Test
public void practice01() throws Exception {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection
("jdbc:mysql://localhost:3306/mydb_21?user=root&password=1194006164qwer");
stat = conn.createStatement();
rs = stat.executeQuery("select* from user");
while (rs.next()) {
System.out.println(rs.getObject(1));
System.out.println(rs.getObject(2));
System.out.println(rs.getObject(3));
System.out.println(rs.getObject(4));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(conn != null){
try {
conn.close();
}catch(Exception e){
e.printStackTrace();
}finally {
conn = null;
}
}
if(stat != null){
try{
stat.close();
}catch(Exception e){
e.printStackTrace();
}finally {
stat.close();
}
}
if(rs != null){
try{
rs.close();
}catch(Exception e){
e.printStackTrace();
}finally {
rs.close();
}
}
}
}
}