头歌Educoder——Java高级特性 - JDBC(上)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: Java高级特性 - JDBC(上)

第1关:JDBC连接数据库

任务描述

本关任务:使用jdbc连接数据库并完成创建数据库和创建表的操作。

相关知识

JDBC API提供以下接口和类:

DriverManager:此类管理数据库驱动程序列表。可在JDBC下识别某个子协议的第一个驱动程序,用于建立数据库连接。

Driver:此接口处理与数据库服务器的通信。我们很少会直接与Driver对象进行交互。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序。

Connection:此接口具有用于联系数据库的所有方法。 Connection对象表示通信上下文,即与数据库的所有通信仅通过连接对象。

Statement:用于执行静态SQL语句并返回它所生成结果的对象。一些派生接口还可接受参数,如PrepareStatement

ResultSet:提供检索不同类型字段的方法。(操作对象为Statement执行SQL查询后的结果)

SQLException:此类处理数据库应用程序中发生的任何错误。

使用JDBC的步骤如下:

加载数据库驱动 → 建立数据库连接(Connection) → 创建执行SQL语句的Statement对象 → 处理执行结果(ResultSet) → 释放资源

为了完成本关任务,你需要掌握:1.如何加载数据库驱动;2.如何建立数据库连接;3.如何执行编写的SQL语句;4.释放资源。

加载数据库驱动

驱动加载是为了打开与数据库的通信通道。

在注册驱动前我们需要装载特定厂商的数据库驱动程序,导入mysq-connector-javajar包,方法是在项目中建立lib目录,在其下放入jar包。

image.png

然后右键jarBuild PathAdd to Build Path完成jar包导入。将jar包导入项目之后我们就开始注册驱动:

Java加载数据库驱动通常是使用Class类的静态方法forName(),语法格式如下:

Class.forName(StringdriverManager)

示例:

try {
Class.forName("com.mysql.jdbc.Driver" );
} catch (ClassNotFoundExceptione) {
e.printStackT\frace();
}

如果加载成功,会将加载的驱动类注册给DriverManager;加载失败,会抛出ClassNotFoundException异常。

建立连接

成功加载完数据库驱动后,就可以建立数据库的连接了,使用DriverManager的静态方法getConnection()来实现。如下:

Connectionconn=DriverManager.getConnection(url, user, password);

URL用于标识数据库的位置,通过URL地址告诉JDBC程序连接信息。

若不存在数据库,只建立连接,URL的写法为:

image.png

若存在数据库testURL的写法为:

image.png

其中localhost可以换成IP地址127.0.0.1,3306MySQL数据库的默认端口号,userpassword对应数据库的用户名和密码。

执行编写的SQL语句

连接建立完毕后,就可以使用Connection接口的createStatement()方法来获取Statement对象;并通过executeUpdate()方法来执行SQL语句。

  • 创建statement对象
try {
Statementstatement=conn.createStatement();
} catch (SQLExceptione) {
e.printStackT\frace();
}
  • 创建数据库
try {
Stringsql1="drop database if exists test";
Stringsql2="create database test";
statement.executeUpdate(sql1);//执行sql语句statement.executeUpdate(sql2);
} catch (SQLExceptione) {
e.printStackT\frace();
}
  • 创建表
try {
statement.executeUpdate("use test");//选择在哪个数据库中操作Stringsql="create table table1("+"column1 int not null, "+"column2 varchar(255)"+")";
statement.executeUpdate(sql);
} catch (SQLExceptione) {
e.printStackT\frace();
}

释放资源

Jdbc程序运行完后,切记要释放程序在运行过程中创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, StatementConnection对象。

特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。

Connection的使用原则是尽量晚创建,尽量早的释放。

为确保资源释放代码能运行,资源释放代码一定要放在finally语句中。

finally {
try {
if(statement!=null)
statement.close();
if(conn!=null)
conn.close();
    } catch (SQLExceptione) {
e.printStackT\frace();
    }
}

编程要求

在右侧编辑器补充代码,完成下列相应任务:

  1. 加载数据库驱动;【平台数据库连接的用户(user)为root,密码(password)为123123
  2. 创建数据库mysql_db
  3. 创建表student

student表结构为:

字段名 类型 备注 约束
id int 学生id 非空
name varchar(20) 学生姓名
sex varchar(4) 学生性别
age int 学生年龄

测试说明

平台会对你编写的代码进行测试:

测试输入:无

预期输出:

id INT(11)

name VARCHAR(20)

sex VARCHAR(4)

age INT(11)


开始你的任务吧,祝你成功!

实现代码

packagejdbc;
importjava.sql.*;
publicclassjdbcConn {
publicstaticvoidgetConn() {
try {
// 1.注册驱动Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundExceptione) {
e.printStackTrace();
        }
/********** End **********//********** Begin **********/Connectionconn=null;
Statementstatement=null;
try {
// 2.建立连接并创建数据库和表conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/", "root", "123123");
Stringsql1="drop database if exists mysql_db;";
Stringsql2="create database mysql_db;";
statement=conn.createStatement();
statement.execute(sql1);
statement.execute(sql2);
statement.execute("use mysql_db");
Stringsql3="create table student(id int not null,name varchar(20),sex varchar(4),age int)";
statement.execute(sql3);
        } catch (Exceptione) {
// TODO 自动生成的 catch 块e.printStackTrace();
        } finally {
try {
if (statement!=null)
statement.close();
if (conn!=null)
conn.close();
            } catch (SQLExceptione) {
e.printStackTrace();
            }
        }
    }
}

第2关:JDBC对表中数据的操作

任务描述

本关任务:使用JDBC完成数据插入和查询。

相关知识

为了完成本关任务,你需要掌握:1.在连接时如何指定数据库;2.向指定表中插入数据;3.遍历表中数据。

指定数据库连接

当我们已经有数据库时,可以直接在连接时指定数据库,如下指定与test_db数据库建立连接:

Stringurl="jdbc:mysql://localhost:3306/test_db";
Connectionconn=DriverManager.getConnection (url,"root","123123" );

当在连接时指定数据库后,我们就不用编写SQL语句进行选择数据库了。

向指定表中插入数据

建立连接之后,编写向表中插入数据的sql语句,使用Statement对象的executeUpdate()方法来执行该sql语句就可向表中修改数据(该方法适用于insertupdatedeletesql语句),当sql语句为查询语句时,则使用executeQuery()方法:

try {
Statementstatement=conn.createStatement();
statement.executeUpdate("insert into table1(column1,column2) values(101,'xxx')");
} catch (SQLExceptione) {
e.printStackT\frace();
}

PreparedStatement

上述直接使用Statement向表中插入数据,操作中存在SQL注入危险,脱离上述表,如下例:

Stringid="5";
Stringsql="delete from tablename where id="+id;
Statementst=conn.createStatement();
st.executeQuery(sql);//查询到表中将无数据//如果用户传入的id为“5 or 1=1”,那么将删除表中的所有记录

为预防这种情况的SQL注入,PreparedStatement 有效的防止sql注入(SQL语句在程序运行前已经进行了预编译,当运行时动态的把参数传给PreprareStatement,即使参数里有敏感字符如or '1=1'数据库也会作为参数的一个字段属性值来处理而不会作为一个SQL指令)

PreparedStatement使用如下:

PreparedStatementstatement=conn.prepareStatement("insert into table1(column1,column2) values(?,?)");//使用占位符来先占个位置statement.setInt(1,101);//占位符顺序从1开始,根据数据库中字段相应的类型存入数据statement.setString(2, "XXX");//也可以使用setObjectstatement.executeUpdate();//每执行一个sql语句就需要执行该方法

查询表中数据

Jdbc程序中的ResultSet用于代表Sql语句的执行结果。

Resultset封装执行结果时,采用的类似于表格的方式,ResultSet对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next()方法,可以使游标指向具体的数据行,然后调用方法获取该行的数据。

//编写查询sql语句PreparedStatementstatement=conn.prepareStatement("select * from table1");
ResultSetresultSet=statement.executeQuery();//将执行结果给ResultSetwhile (resultSet.next()) {//循环判断表中是否还有数据intid=resultSet.getInt(1);//通过列的索引查询Stringname=resultSet.getString("column2");//通过列名查询}

编程要求

在右侧编辑器补充代码,向上一章节中已创建好的数据库mysql_db中的表student中插入数据,并将插入的数据进行输出:

id name sex age
1 张三 19
2 李四 18
3 王五 20

提示:已为你封装好student类,可在右侧文件夹中查看,此类可直接使用。

测试说明

平台会对你编写的代码进行测试:

测试输入:无

预期输出:

1 张三 男 19

2 李四 女 18

3 王五 男 20


开始你的任务吧,祝你成功!

实现代码

packagejdbc;
importjava.sql.*;
importjava.util.ArrayList;
importjava.util.List;
publicclassjdbcInsert {
publicstaticvoidinsert() {
/********** Begin **********/try {
// 加载驱动Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundExceptione) {
e.printStackTrace();
        }
/********** End **********/Connectionconn=null;
PreparedStatementstatement=null;
/********** Begin **********/// 连接并插入数据try {
Stringurl="jdbc:mysql://localhost:3306/mysql_db?useUnicode=true&characterEncoding=utf8";
Stringuser="root";
Stringpassword="123123";
conn=DriverManager.getConnection(url, user, password);
Stringsql="insert into student(id,name,sex,age) values (1,'张三','男',19),(2,'李四','女',18),(3,'王五','男',20)";
statement=conn.prepareStatement(sql);
statement.executeUpdate();
Stringsql1="select * from student";
ResultSetrs=statement.executeQuery(sql1);
Studentstudent=null;
while (rs.next()) {
intid=rs.getInt(1);
Stringname=rs.getString(2);
Stringsex=rs.getString(3);
intage=rs.getInt(4);
student=newStudent(id, name, sex, age);
System.out.println(
student.getId() +" "+student.getName() +" "+student.getSex() +" "+student.getAge());
            }
        } catch (SQLExceptione) {
e.printStackTrace();
        }
/********** End **********/finally {
try {
if (statement!=null)
statement.close();
if (conn!=null)
conn.close();
            } catch (SQLExceptione) {
e.printStackTrace();
            }
        }
    }
}

第3关:JDBC事务

任务描述

本关任务:按照具体要求编写程序。

相关知识

为了完成本关任务,你需要掌握:1. 什么是事务;2. 事务的基本要素;3.如何开启事务;4.事务的提交和回滚

事务

假设场景,我们有一个人员管理系统,你要删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就可以构成一个事务!

事务能够控制何时更改提交并应用于数据库。 它将单个SQL语句或一组SQL语句视为一个逻辑单元,如果任何语句失败,整个事务将失败。

事务的基本要素(ACID

  • 原子性(Atomicity):一组事务,要么成功;要么撤回;
  • 一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到;
  • 隔离性(Isolation):事务独立运行。一个事务处理后的结果,影响了其他事务,那么其他事务会撤回。事务的100%隔离,需要牺牲速度;
  • 持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

开启事务

开启事物需要启用手动事务支持,而不是使用JDBC驱动程序默认使用的自动提交模式,可调用Connection对象的setAutoCommit()方法。 如果将布尔的false传递给setAutoCommit(),则关闭自动提交,也就相当于开启了事物。 也可以传递一个布尔值true来重新打开它。

Connectionconn=DriverManager.getConnection (url,"root","123123" );
conn.setAutoCommit(false);//关闭自动提交开启事务

提交和回滚

mysql数据库中,默认为将每一句sql都自动提交,当我们将它设置为手动事务支持(也就是已经手动开启事务)时,我们就可以在需要提交的时候进行手动提交:

conn.commit();//提交事务

当有多条sql语句一次提交时,我们需要考虑到其中sql是否合法,若其中某一条不合法,其他的sql是否仍需更改等一系列问题;确保事务的基本要素,我们需要手动调用事务回滚来控制sql的执行:

try{
Connectionconn=DriverManager.getConnection (url,"root","123123" );
conn.setAutoCommit(false);//开启事务PreparedStatementps=conn.prepareStatement("insert into table1(column1,column2) values(1,'xx1')");
ps.executeUpdate();
ps=conn.prepareStatement("insert in table1(column1,column2) values(1,'xx1')");
ps.executeUpdate();
conn.commit();//提交事务} catch (SQLExceptione) {
try {
conn.rollback();//回滚事务  回滚到你开始事务之前    } catch (SQLExceptione1) {
e1.printStackT\frace();
    }
}

上述代码执行完毕后,数据库中并不会有数据更新。由于第二条insert语句语法错误,所以事务回滚,之前的insert也会失效。通常事务回滚都会放在catch中来捕获。

开启事务后,一定要跟上 commitrollback,及时释放可能锁住的数据。

不用rollback()表面和用了rollback()效果一样,但是不用rollback()可能导致被锁住的数据不能及时的释放(需要等事物超时释放),会影响下一次的事物操作。

编程要求

根据提示,在右侧编辑器补充代码,编写一条新增SQL语句和任意一条错误SQL语句,提交事务;要求第一条新增语句在数据库中被修改,其后错误SQL语句不执行。

新增插入语句具体要求如下: 在mysql_db数据库student表中新增一条id4name为赵六,sex为女,age21的数据。

提示:每一条SQL语句之后都可提交事务。

测试说明

平台会对你编写的代码进行测试:

测试输入:无

预期输出:

1 张三 男 19

2 李四 女 18

3 王五 男 20

4 赵六 女 21


开始你的任务吧,祝你成功!

实现代码

packagejdbc;
importjava.sql.*;
publicclassjdbcTransaction {
publicstaticvoidtransaction() {
try {
Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundExceptione) {
e.printStackTrace();
        }
Connectionconn=null;
PreparedStatementps=null;
/********** Begin **********/// 连接数据库并开启事务try {
Stringurl="jdbc:mysql://localhost:3306/mysql_db?useUnicode=true&characterEncoding=utf8";
conn=DriverManager.getConnection(url, "root", "123123");
conn.setAutoCommit(false);
Stringsql="insert into student(id,name,sex,age) values(4,'赵六','女',21)";
ps=conn.prepareStatement(sql);
ps.executeUpdate();
conn.commit();
Stringsql1="daj;ljd";
ps.executeUpdate();
conn.commit();
Stringsql2="select * from student";
ResultSetrs=ps.executeQuery(sql2);
Studentstudent=null;
while (rs.next()) {
intid=rs.getInt(1);
Stringname=rs.getString(2);
Stringsex=rs.getString(3);
intage=rs.getInt(4);
student=newStudent(id, name, sex, age);
System.out.println(
student.getId() +" "+student.getName() +" "+student.getSex() +" "+student.getAge());
            }
        } catch (SQLExceptione) {
try {
// 事务回滚conn.rollback();
            } catch (SQLExceptione1) {
e1.printStackTrace();
            }
        }
/********** End **********/finally {
try {
if (ps!=null)
ps.close();
if (conn!=null)
conn.close();
            } catch (SQLExceptione1) {
e1.printStackTrace();
            }
        }
    }
}
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
63 2
|
2月前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
40 3
|
2月前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
33 2
|
2月前
|
存储 算法 Java
Java Set因其“无重复”特性在集合框架中独树一帜
【10月更文挑战第14天】Java Set因其“无重复”特性在集合框架中独树一帜。本文深入解析Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定的数据结构(哈希表、红黑树)确保元素唯一性,并提供最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的`hashCode()`与`equals()`方法。
31 3
|
2月前
|
安全 Java API
Java 17新特性让你的代码起飞!
【10月更文挑战第4天】自Java 8发布以来,Java语言经历了多次重大更新,每一次都引入了令人兴奋的新特性,极大地提升了开发效率和代码质量。本文将带你从Java 8一路走到Java 17,探索那些能让你的代码起飞的关键特性。
83 1
|
19天前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
28 4
|
2月前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
61 3
|
2月前
|
存储 安全 Java
Java Map新玩法:深入探讨HashMap和TreeMap的高级特性
【10月更文挑战第19天】Java Map新玩法:深入探讨HashMap和TreeMap的高级特性,包括初始容量与加载因子的优化、高效的遍历方法、线程安全性处理以及TreeMap的自然排序、自定义排序、范围查询等功能,助你提升代码性能与灵活性。
25 2
|
2月前
|
Java 开发者
在Java的集合世界里,Set以其独特的特性脱颖而出,它通过“哈希魔法”和“红黑树防御”两大绝技
【10月更文挑战第13天】在Java的集合世界里,Set以其独特的特性脱颖而出。它通过“哈希魔法”和“红黑树防御”两大绝技,有效抵御重复元素的侵扰,确保集合的纯洁性和有序性。无论是“人海战术”还是“偷梁换柱”,Set都能从容应对,成为开发者手中不可或缺的利器。
31 6
|
2月前
|
Java 开发者
在Java集合世界中,Set以其独特的特性脱颖而出,专门应对重复元素
在Java集合世界中,Set以其独特的特性脱颖而出,专门应对重复元素。通过哈希表和红黑树两种模式,Set能够高效地识别并拒绝重复元素的入侵,确保集合的纯净。无论是HashSet还是TreeSet,都能在不同的场景下发挥出色的表现,成为开发者手中的利器。
27 2