JDBC初步学习笔记
Java养成计划77天
jdbc连接数据库
现在先将表面的知识学习完成之后,才会进一步来深入学习,比如计组,JDK源码,计网,数据库原理;现在的初期目标是能够熟练操作java相关,初步完成一个完整简单的项目;包括数据库表,java编程;涉猎一下web编程;全栈操作,重在后端
昨天已经完成数据库表的学习,包括索引和视图,当然非常浅显,但是目的是为了拓宽知识面,先能够熟练的运用,运用熟练之后学习底层应该会容易一些
学习数据库的目的就是为了更好的管理数据,管理数据的目的是为了让应用得到更好的实现,那么既已经初步学习了数据库,接下来就应该是考虑数据库如何在程序中使用,java中要使用数据库,就离不开JDBC,那么什么是JDBC呢?
想到jdbc,就会想到很多缩略词比如CRUD,ACID,这些都是对应的英语单词取首字母形成的,这里的JDBC就是java dababase connectivity java数据库连接工具 ;
面向接口编程的目的 :解耦合,提高程序的可扩展性
主要是理解什么是耦合,耦合就是模块间的关联程度,关联程度越高,那么耦合度越高;比如new 对象的时候,对象不存在,编译出错,无法运行,这个时候就是耦合度提高的;还有比如实现图书的格式多种html,xml之类;设计的时候不能放在一个类中,这样修改的时候会很麻烦,耦合度太高,实际上各种格式之间的耦合度应该很低,这个时候就使用strategy pattern,面向接口编程来降低耦合,俗称解耦
面向具体编程,会导致程序过僵,耦合度太高,扩展性很低 【所以面向接口编程】
SUN指定了一套JDBC接口,这时因为每一个数据库的地城实现原理都不一样,Oracle,MySQL等各个数据库的底层原理都不一样,每一个数据库产品都有自己独特的实现原理 -------- 各个数据库厂家都去实现JDBC接口的实现类,就是一套.class文件;
而Programmer就是面向JDBC一套接口,我们可以进入JAVA 的帮助文档中,可以发现一java.sql包中的接口就有一个connection接口,这个接口就是连接数据库的超级接口,各个数据库都对其进行了实现
而MySQL对其的实现就是可以看到几个.class文件,包括Driver.class;SocketFactory.class,SocketFactorWrapper.class等Java文件; 各个数据库对于jdbc的实现称为驱动driver,比如我下载的就是MySQL驱动
驱动不是SUN公司提供,而是各个数据库厂家去下载jar包才可以
关于数据库的模拟可以看一下之前的博客,之前分享反射的时候就是以数据库迁移来分享的,这里使用了配置文件properties文件,创建文件,利用class类的forname方法获取类的对象,之后创建实例,利用newInstance方法创建对象,这里使用上转型就可以了,上转型可以强转或者不转;下转型使用instanceof判断之后再强制类型转换;
当然除了Properties类,也可以使用ResourceBundle 绑定类来获取文件中的数据
jdbc的.class文件都是从网上下载的,这些文件没有在java bin中,所以需要配置classpath变量
classpath没有配置文件的情况下,默认从当前路径下加载class,calsspath如果配置死了,比如 classpath = D:\abc ,则表示只从abc下找class;但是我们自己写的class也要进行类加载,之前运行java程序的时候就是在当前路径下,因为指明了.java文件,路径明了;
那么路径就要配置为.:\…… .代表的当前路径下 ;代表的是该变量的另外一种值
jar包不需要解压,类加载器可以自动找到文件
驱动在数据库方面就是Driver,那么注册驱动就是告诉Java程序需要连接的是哪个品牌的数据库;
上面提到SUN公司提供了一套接口JDBC,当然就有接口和相关的类负责驱动注册了
在java程序中,分管sql的是java.sql包,该包中有一个类为Driver
需要注意的是加载驱动不是在com.mysql.jdbc.Driver下了,而是在com.mysql.cj.jdbc.Driver,还要注意修改时间
java中因为各个数据库公司实现了SUN公司提供的接口,但是名称是一样的,这个时候就不能两边都省略了,因为JVM是识别不了到底谁是谁,这个时候一个导入import,另外一个直接加上包名就可以不用导入了
一般注册驱动就两步
Driver driver = new com.mysql.jdbc.Driver();
//Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
DriverManager.registerDriver(driver);
上面第一步注册驱动只是引入Mysql驱动程序 ---- 可以允许mysql数据库了,但是还没有获取到mysql中的数据库,那么接下来还是要使用DriverManager中的类方法getConnections ---- 都会抛SQLException,这种方法重载了,因为可以使用配置文件来书写密码和用户,还可以直接写密码和用户【String】 String – URL
建立到给定数据库URL(uniform resource locator统一资源定位符)的连接,DriverManager则从已经注册的驱动程序中选择一个合适的【因为是一组不是一个】
这里的user和password就是用户和密码,和mysql中的-u -p 相同,都是String类型的就可
要注意数据库的url的写法:
url之前在计网中介绍过,就不再介绍了,url包括 协议 ip 端口 路径 用户etc protocol分层,垂直service
Oracle : oracle:jdbc:thin:@localhost:3306:cfengtest
String user = "cfeng";
String password = "*********";
String url = "jdbc:mysql://localhost:3306/cfengtest?serverTimezone=GMT%2B8";
Connection conn = DriverManager.getConnection(url, user, password);//取得一个数据库连接对象
System.out.println(conn);
//com.mysql.cj.jdbc.ConnectionImpl@f0c8a99 这里其实可以看出来就是创建一个mysql的连接对象;当然不同的数据库是不同的对象 都是实现的sun的接口Connection
上面第二步已经获取到了数据库连接对象了,那么要想操作数据库中的数据,必须要有数据库操作对象,数据库操作对象的获取可以使用==Connection接口中的createStatement()方法
该方法可以创建一个statement对象来讲SQL语句发送到数据库
连接对象只是管连接的,可以操作连接状态;Statement【也是sun的一个总接口】对象是在连接的基础上传送语句的
通过一个连接对象Connection是可以创建多个Statement对象的
Statement state = conn.createStatement(); //这里就简单获取一个对象就可以了
System.out.println(state);
//com.mysql.cj.jdbc.StatementImpl@7674f035
【execute 执行】
这里要使用Statement中的方法就可以了,比如executeUpdate 语句就是DML语句都可,可以返回int型的修改了多少rows的记录
String sql = "INSERT INTO emp (empno,ename) VALUES (15,'ROBIT')";
int count = state.executeUpdate(sql);
System.out.println(count);
//1 代表影响了数据库中的一行记录,可以去数据库中看一下效果
mysql> SELECT * FROM emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 15 | ROBIT | NULL | NULL | NULL | NULL | NULL | NULL |
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLANK | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILIER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
可以看到是成功插入了数据的
再来多实验几次,DELETE语句和UPDATE语句
String sql = "UPDATE emp SET ename = 'Cfeng' WHERE empno = 15";
int count = state.executeUpdate(sql);
System.out.println(count);
//1
mysql> SELECT * FROM emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 15 | Cfeng | NULL | NULL | NULL | NULL | NULL | NULL
再来看一下DELETE
String sql = "DELETE FROM emp WHERE empno = 15";
int count = state.executeUpdate(sql);
System.out.println(count);
//1
mysql> SELECT * FROM emp;
| 7934 | MILIER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
14 rows in set (0.01 sec)
可以看到和之前单独使用dos窗口操作时一样的效果,只是要注意SQL语句的正确性
当上面的SQL语句为DQL的时候,这个时候就要调用数据库操作对象的executeQuery()方法,这个方法返回的是一个ResultSet对象
JDBC中所有的下标都是从1开始的
,通过getString(columindex)方法取得第index位置的数据【不管是什么类型都是以String类型返回rset = state.executeQuery(sql);
while(rset.next()) {
String emp = rset.getString(1) + " " + rset.getString(2) + " " + rset.getString(3);//上面的SQL语句查询的每一行的记录是3col,所以从1到3
System.out.println(emp);
}
字段下标不健壮,因为一旦修改之后,位置可能变化,但是字段名称很少变化
因为维护进程通信连接是需要资源的,还有statement也需要维护,所以要释放掉资源不用的时候
这里的释放因为上面的try catch,所以直接在finally中释放,关于finally的考点比如return,exit之类的就不再赘述,因为try形成了语句块,又变量的作用域和生命周期,所以这里变量声明放在try的外面,方便在finnally中释放
这个问题主要是应对一些其他的情况,finally除了强制关闭jvm都是可以执行的,所以放在finally中或许更好一些
//先关闭ResultSet,关闭state,再关闭connection
if(state != null) {
state.close();
}
if(conn != null) {
conn.close();
}
//放在finally中要分别try catch
这里已经知道编程6步,那么就要实践一下,看一下如何运用
package test;
import java.sql.*;
public class JdbcTest {
public static void main(String[] args) {
//注册驱动
Connection conn = null;
Statement state = null;
ResultSet rset = null;
try {
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
//获取数据库连接对象
String url = "jdbc:mysql://localhost:3306/cfengtest?servertimezone=GMT%2B8";
String user = "cfeng";
String password = "************";
conn = DriverManager.getConnection(url, user, password);
//获取数据库操作对象
state = conn.createStatement();//直接再连接的基础上来创建对象
//执行SQL语句
String sql = "SELECT empno,ename,sal FROM emp LIMIT 5";
rset = state.executeQuery(sql);
while(rset.next()) {
String emp = rset.getString(1) + " " + rset.getString(2) + " " + rset.getString(3);//上面的SQL语句查询的每一行的记录是3col,所以从1到3
System.out.println(emp);
}
//获取数据库查询结果集
} catch (SQLException e) {
e.printStackTrace();
}finally {
//释放资源
if(rset != null) {
try {
rset.close();
} catch (SQLException e) {
e.printStackTrace();
}
if(state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
}
}
//建议获取数据使用名称,比下标健壮
/*
7369 SMITH 800.0
7499 ALLEN 1600.0
7521 WARD 1250.0
7566 JONES 2975.0
7654 MARTIN 1250.0
*/