JDBC基础
如果没有JDBC,会带来哪些问题
我们访问数据库操作就没有规范.
因为数据库厂商是不一样的,就会执行自己的访问数据库规则.
比如小明,之前在A公司,使用了的是AB公司的MySQL,就得学习MySQL访问数据库的方式.
后来,小明去了B公司,B公司使用的Oracle的Oracle数据库,小明只能重新学习Oracle访问数据库的方式.
小明又跳槽了去了C公司,C公司使用的IBM的DB2,继续学习.
加重了小明的学习成本。
造成上述问题的根本原因
数据库厂商不一样,没有指定访问数据库的规则,所以操作不同的数据库就得学习不同的技术.
这时,SUN出来,说:我来统一指定所有数据库的访问规则,你们(各大数据库厂商)必须按照我的方式来实现,否则不支持你的数据库服务器.
有了规范之后,针对于不同的数据库服务器,就只需要一套技术即可.这个规范称之为JDBC.
什么是持久化(persistence)
持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。
大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大多通过各种关系数据库来完成。
持久化的主要应用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。
JDBC(Java DataBase Connectivity)
是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序.
JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
总结: JDBC本身是java连接数据库的一个标准,是进行数据库连接的抽象层,由java编写的一组类和接口组成,接口的实现由各个数据库厂商来完成
JDBC的版本
JDBC隶属于JavaSE的范畴,伴随着JavaSE的版本升级.
Java6开始:JDBC4.0: (了解),JDBC4.0有一个新特性-无需加载注册驱动.
Java7开始:JDBC4.1: Connection,ResultSet 和 Statement 都实现了Closeable 接口,所有在 try-with-resources 语句中调用,就可以自动关闭相关资源了
Java8: JDBC4.2
增加了对REF Cursor的支持
修改返回值大小范围(update count)
增加了java.sql.DriverAction接口
增加了java.sql.SQLType接口
增加了java.sql.JDBCtype枚举
对java.time包时间类型的支持
JDBC的API在哪里?
java.sql包装的就是JDBC的API.
各大数据库厂商就会对JDBC的API提供实现类.--->驱动包
如图:
7.png
注意:在开发中(编写的Java代码),使用到的关于JDBC的类/接口全部引入的是java.sql包中的.
千万不要引入com.mysql..Xxx类.如果引入了具体驱动包中的API,那么在切换数据库的时候存在问题
JDBC访问数据库
在Java中,数据库存取技术只能通过JDBC访问数据库:
JDBC访问数据库的形式主要有两种:
-
1).直接使用JDBC的API去访问数据库服务器(MySQL/Oracle).
-
2).间接使用JDBC的API去访问数据库服务器.
第三方O/R Mapping工具,如Hibernate, MyBatis等.(底层依然是JDBC) JDBC是java访问数据库的基石,其他技术都是对jdbc的封装.
我们通过一张图来看一下JDBC规范在访问数据库的流程中起到了什么作用。如图:
1.pngJDBC完成CRUD
JDBC操作数据库:
口诀(思路): 贾琏欲执事.
步骤:
1.加载注册驱动.
2.获取连接对象:
3.获取语句对象.
4.执行SQL语句.
5.释放资源.
获取数据库连接
获取数据库连接Connection对象
一、准备工作:
1.拷贝MySQL的驱动包到项目中去:mysql-connector-java-5.1.x-bin.jar,注意,不要拷贝zip包.
2.build path,告诉项目去哪里去找字节码文件.
二、具体操作:
操作JDBC的第一步,获取JDBC的连接对象:Connection. 贾琏.
-
1):加载注册驱动:
Class.forName("com.mysql.jdbc.Driver");--快买京东
为什么上述这一行代码,就能完成加载和注册驱动操作呢?
1):会把com.mysql.jdbc.Drvier这份字节码加载进JVM.
2):当一份字节码被加载进JVM时,就会执行该字节码中的静态代码块.
我们可以通过查看源码来理解这一点,那么如果要看源码,首先要关联源码,如果关联呢?如图:
6.png
关联源码成功以后,然后进入Driver类中,可以看到一个静态代码,我们都知道当类加载到虚拟机的时候,首先执行的是静态代码块。如图:
8.png- 2):获取连接:
通过DriverManager类的getConnection方法来获取连接对象.
Connection conn = DriverManager.getConnection(url,username,password);
参数:
url: 连接数据库的URL: jdbc:mysql://localhost:3306/jdbcdemo 如果连接的是本机的MySQL,并且端口是默认的3306,则可以简写: jdbc:mysql:///jdbcdemo
username: 数据库用户名:root
password: 数据库密码 :admin
三、注意:同学们容易犯的错误:
1):忘记拷贝驱动包或者拷包错误.
2):导包错误!应该导入java.sql.xxx
3):起名错误!使用了JDK中的类名或者Jar包中的类名.
public class ConnectionTest {
@Test
public void testGetConnection() throws Exception {
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取连接对象
Connection connection = DriverManager.getConnection(// 连接数据库的要素
"jdbc:mysql:///javaweb", // url
"root", // 用户名
"admin"); // 密码
System.out.println(connection);
/*
注意:对于url的内容jdbc:mysql://localhost:3306/javaweb
如果是本机上的MySQL数据库并且端口号是3306
那么 url的内容 可以写成 jdbc:mysql:///javaweb
*/
}
}
创建表和常见的异常处理
创建一张t_student表:
CREATE TABLE `t_student` (`id` bigint(20) PRIMARY KEY AUTO_INCREMENT,`name` varchar(20),`age` int(11));
常见的异常错误以及处理异常的思路。
如图:
4.png处理异常的思路:
看到异常以后,我们应该重点看异常的内容。比如上图中异常提示说Unknown database 'jdbcdem',意思很明确的告诉我们不知道名字叫‘jdbcdem’的数据库。所以我们应该立刻想到去检查连接数据库的参数中的第一个参数,检查数据库的名称
如图:
5.png
处理异常的思路:
看到异常以后,我们应该重点看异常的内容。比如上图中异常提示说Access denied for user 'root@localhost'(using password:YES),意思很明确的告诉我们访问数据库被拒绝,为何拒绝在括号里面告诉我们要访问数据库要使用密码,那么我们在设置连接参数的时候也设置了密码,为何还错误呢,很明显应该去检查一下设置的参数里面的账号和密码是不是设置正确了,如果不正确,就会报这个错误。
DDL和DML操作
Connection接口的常用方法:
Statement createStatement() :创建一个语句对象,该对象可以把SQL发送到数据库服务器中.
Statement接口: 语句对象,目的:发送静态SQL到数据库服务器,并返回结果.
int executeUpdate(String sql):该方法可以执行DML和DDL语句. 若执行的是DML,则返回受影响的行数.若执行DDL将0.
ResultSet executeQuery(String sql):该方法执行DQL语句. 并返回一个结果集对象.
案例:创建学生信息表(t_student),包括id/name/age三个列.
SQL:
create table t_student ( id bigint primary key auto_increment, name varchar(30) , age int );
代码如下:
public class DDLTest {
@Test
public void testCreateTable() throws Exception {
String sql = "create table t_student (id bigint primary key auto_increment ,name varchar(30) ,age int)";
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取Connection连接对象
Connection connection = DriverManager.getConnection(// 连接数据库的要素
"jdbc:mysql:///javaweb", // url
"root", // 用户名
"admin"); // 密码
// 获取Statement对象
Statement statement = connection.createStatement();
// 调用executeUpdate(sql)方法 执行sql
statement.executeUpdate(sql);
// 释放资源
statement.close();
connection.close();
}
上面的写法上有点不妥,操作数据库的代码可能导致的异常,不能直接抛出去,应该自己处理。把整体代码放到一个try中,对于释放资源为了保证一定能释放,应该放到finally代码快中。代码改造如下:
@Test
public void testBundleException() {
String sql = "create table t_student (id bigint primary key auto_increment ,name varchar(30) ,age int)";
Connection connection = null;
Statement statement = null;
try {
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取Connection连接对象
connection = DriverManager.getConnection(// 连接数据库的要素
"jdbc:mysql:///javaweb", // url
"root", // 用户名
"admin"); // 密码
// 获取Statement对象
statement = connection.createStatement();
// 调用executeUpdate(sql)方法 执行sql
statement.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放资源
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
对于上面的代码,我们发现释放资源那里的代码很长,如果每次完成一个操作,都要写那么长的代码来释放资源,简直是噩梦。
在java7中,也就是JDBC4.1: Connection,ResultSet 和 Statement 都实现了Closeable 接口,所有在 try-with-resources 语句中调用,就可以自动关闭相关资源了。改造如下:
// java7 开始支持这种方式
@Test
public void testBundleException2() {
String sql = "create table t_student (id bigint primary key auto_increment ,name varchar(30) ,age int)";
try (
// 获取Connection连接对象
Connection connection = DriverManager.getConnection(// 连接数据库的要素
"jdbc:mysql:///javaweb", // url
"root", // 用户名
"admin"); // 密码
// 获取Statement对象
Statement statement = connection.createStatement();) {
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 调用executeUpdate(sql)方法 执行sql
statement.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面我对于表的创建功能实现了,那么接下来就是对表中的数据进行增删改操作。
需求:1.往学生表中插入信息杰克 , 18
@Test
public void testInsert() throws Exception {
String sql = "insert into t_student(name,age) values (?,?)";
// 1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取Connection连接对象
Connection connection = DriverManager.getConnection(// 连接数据库的要素
"jdbc:mysql:///javaweb", // url
"root", // 用户名
"admin"); // 密码
// 3.获取预编译语句对象
PreparedStatement ps = connection.prepareStatement(sql);
// 4.执行sql语句
ps.executeUpdate();
// 5.释放资源
ps.close();
connection.close();
}
需求:2.修改id为1的学生的姓名为杰克马 , 年龄为20。
@Test
public void testName() throws Exception {
String sql = "update t_student set name=? ,age=? where id =?";
// 1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取Connection连接对象
Connection connection = DriverManager.getConnection(// 连接数据库的要素
"jdbc:mysql:///javaweb", // url
"root", // 用户名
"admin"); // 密码
// 3.获取预编译语句对象
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, "杰克马");
ps.setInt(2, 20);
ps.setLong(3, 1L);
// 4.执行sql语句
ps.executeUpdate();
// 5.释放资源
ps.close();
connection.close();
}
需求:3.把id为1的学生删除.
@Test
public void testDelete() throws Exception {
String sql = "delete from t_student where id=?";
// 1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取Connection连接对象
Connection connection = DriverManager.getConnection(// 连接数据库的要素
"jdbc:mysql:///javaweb", // url
"root", // 用户名
"admin"); // 密码
// 3.获取预编译语句对象
PreparedStatement ps = connection.prepareStatement(sql);
ps.setLong(1, 1L);
// 4.执行sql语句
ps.executeUpdate();
// 5.释放资源
ps.close();
connection.close();
}
}
DQL操作
通过查看API,调用executeQuery方法获取查询的结果。返回值为ResultSet,那么ResultSet是什么呢?
ResultSet :表示使用JDBC规范操作DQL返回的结果集, 用来封装查询的每一条数据信息,该api中有一个next方法,当调用next方法,返回true时,表示把光标指向的该行的数据封装到ResultSet对象当中。
通过该api提供的 getXxx方法 来获取封装的数据 ,getXxx 方法 返回 Xxx类型 如果传入的是int类型,参数表示列的索引(索引从1开始的). 如果传入的String类型,参数表示列的名称(推荐使用),根据列的名称 获取对应的值。
为了更好的理解ResultSet,通过画图的方式来进行描述,如图:
2.png明白了ResultSet的含义,接下来我们完成一些查询的需求。
需求: 1.查询数据库的总记录数
@Test
public void testQueryTotalCount() throws Exception {
String sql = "select count(*) from t_student";
// 1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取Connection连接对象
Connection connection = DriverManager.getConnection(// 连接数据库的要素
"jdbc:mysql:///javaweb", // url
"root", // 用户名
"admin"); // 密码
// 3.获取预编译语句对象
PreparedStatement ps = connection.prepareStatement(sql);
// 4.执行sql语句
ResultSet resultSet = ps.executeQuery();
if (resultSet.next()) {
System.out.println("获取数据的总数:" + resultSet.getLong(1));
System.out.println("获取数据的总数:" + resultSet.getLong("count(*)"));
}
// 5.释放资源
ps.close();
connection.close();
}
需求: 2.查询id为1的学生信息
@Test
public void testQueryOne() throws Exception {
String sql = "select * from t_student where id=?";
// 1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取Connection连接对象
Connection connection = DriverManager.getConnection(// 连接数据库的要素
"jdbc:mysql:///javaweb", // url
"root", // 用户名
"admin"); // 密码
// 3.获取预编译语句对象
PreparedStatement ps = connection.prepareStatement(sql);
ps.setLong(1, 1L);
// 4.执行sql语句
ResultSet resultSet = ps.executeQuery();
if (resultSet.next()) {
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
System.out.println("姓名:" + name + "/年龄:" + age);
}
// 5.释放资源
ps.close();
connection.close();
}
需求:3.查询所有的学生信息
@Test
public void testQueryAll() throws Exception {
String sql = "select * from t_student";
// 1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取Connection连接对象
Connection connection = DriverManager.getConnection(// 连接数据库的要素
"jdbc:mysql:///javaweb", // url
"root", // 用户名
"admin"); // 密码
// 3.获取预编译语句对象
PreparedStatement ps = connection.prepareStatement(sql);
// 4.执行sql语句
ResultSet resultSet = ps.executeQuery();
while (resultSet.next()) {
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
System.out.println("姓名:" + name + "/年龄:" + age);
}
// 5.释放资源
ps.close();
connection.close();
}
}
那么以后我们处理查询功能的时候,如果不知道应该定义什么类型的变量接收参数,是很困惑的。我们如果知道数据库中的类型对应到java代码中是什么类型。那么以后处理查询功能,就很简单了。下面我们通过一个表来对应一下类型。
网友评论