当前位置:   article > 正文

完全从零Java自学系列【入门篇】(第四课:Mysql服务端安装&使用客户端操作数据库&初识SQL基础操作&Java中使用第三方包&Java数据库操作&初步理解面相对象真正的意义之桥接设计模式)

完全从零Java自学系列【入门篇】(第四课:Mysql服务端安装&使用客户端操作数据库&初识SQL基础操作&Java中使用第三方包&Java数据库操作&初步理解面相对象真正的意义之桥接设计模式)


数据库

  数据库是专门用来存储一系列集合数据的地方。所有的文件都可以被称之为库,当应用场景没那么复杂的时候,简单的应用程序用文本就可以了。数据库的意义是为了设计更好的保障数据安全(如多线程操作)、数据一致、索引(如何在庞大的数据中快速查找)等等一系列能满足更庞大的场景需求的功能。

基础概念

  如果你想设计一个你自己的数据库,必须要满足以下特征:

  1. 原子性(Atomicity):如果同时往库里写入或修改多条数据(也可以是一批次操作),必须满足要么都成功要么都失败。在数据库里这个也叫事务(Transaction)
  2. 一致性(Consistency):多节点之间保持数据的一致。
    1. 场景一:你从微信中提现到你得银行卡,需要同时确保微信扣款成功,银行卡入款成功。反之往微信充钱也一个道理。
    2. 场景二:集群化的数据库之间(目前了解一下即可)需要保证数据同时写入修改或删除。
  3. 隔离性(Isolation)也叫独立性:就是咱们上一课中讲的多线程安全。
  4. 持久性(Durability):要保证数据的用不丢失。

mysql数据库

  它是一个开源的关系型数据库系统,关系型就是可以通过建立关系约束来达成不同数据集之间的沟通。通俗点就是说它主打这个特点功能。
  数据库又由多个基础元素组成:

  • 数据列:和JAVA对象中的属性对应,一个对象有多个属性,一个数据行有多个列。比如:“周杰伦”,“男”,“歌手”。周杰伦就是一行数据的其中一个数据列。
  • 数据行:通常是多条数据组成,比如一个集合中有多个对象。比如:1. “周杰伦”,“男”,“歌手”。2. “周星驰”,“男”,“演员”周杰伦。这个数据集就是2行数据3个数据列组成。
  • 数据表:就是多行数据列多行数据行组成的数据集整体。单个数据表不能超过4GB,超过则崩溃。
  • 数据库:它由多个数据表、单独的权限访问机制、单独的资源(线程等)使用,单独的日志文件等组成。单个库不能超过256TB,超过则崩溃。
  • 数据库实例:一个实例就是一个进程。可以单台机器多mysql实例,也可以多台机器多mysql实例。 在这里插入图片描述
Mysql 安装
MYSQL服务端安装

  前期安装JAVA需要解释这个语言环境所以自己示范了,未来涉及到的安装内容将会为大家精心挑选引用其它文章(本人使用MAC电脑很难处处为大家演示双系统)。理论上安装什么版本的都可以,并不一定按作者的版本来。
  需要注意的是这里安装的只是mysql服务端程序也就是后台程序。安装完成后还需要通过客户端(界面)工具来连接才能使用它。
  MAC安装:CSDN教程链接,WINDOWS安装:CSDN教程链接

MAC安装可能出现的问题:如果出现了.Logging to '/usr/local/mysql/data/apples-MacBook-Pro.local.err',使用cat /usr/local/mysql/data/apples-MacBook-Pro.local.err命令查看里面的内容是否是:Permission denied。如果是:

  1. 在程序偏好设置中找到mysql,并stop掉它。
  2. 在命令行执行:sudo chmod -R a+rwx /usr/local/mysql/data/输入你电脑的密码即可。
  3. 再启动
    在这里插入图片描述
mysql客户端安装

这玩意儿没啥花头,就是装个软件,这里我MAC用的是8.0.27。
MYSQL WorkBench客户端官网下载链接
装好之后就可以当前你启动的MysqlServer服务了在这里插入图片描述

连接Mysql数据库后端服务

连接数据库需要知道该数据库的IP地址以及端口、用户名密码

  • 这里我们连接本机IP就是127.0.0.1
  • 端口默认的都是3306,除非安装时修改了。
  • 目前直接用你安装mysql时用的那个root账户密码即可。
  • 可以点击ok了。
    在这里插入图片描述
  • 连接成功后是这个样子的
    在这里插入图片描述
MYSQL操作

  操作MYSQL有两种方式,命令行(所有后端服务都可以用命令行来操作),客户端工具,这里我们使用的是官方的Workbench。
  两者都可以通过SQL (Structured Query Language)来进行,Workbench这样的客户端工具则可以通过界面操作。但SQL是我们必须要学习的,因为你不可能让JAVA学会如何界面化操作Workbench。所以程序与数据库的沟通只能是SQL。所以必须学。
  接下来我们需要用SQL来完成以下事情:在这里插入图片描述

START

  在最初的操作界面中

  1. Administration板块:是管理员操作空间,
    • Management
      • Server Status:查看服务器状态
      • Client Connections:查看客户端链接
      • Users and Privileges:管理用户与权限
      • Status and System Variables:管理系统变量
      • Data Export:数据导出
      • Data Import/Restore:数据导入与恢复
      • Instance
    • Instance
      • Startup / shutdown:启动关闭实例
      • Server Logs:查看系统日志
    • Performance
      • Dashboard:运行性能大屏
  2. Schemas:数据库目录
    • 目前只有sys一个系统库

大概了解之后选择红色箭头指向按钮来,创建一个新的SQL编写TAB页。可以同时新建多个来区分不同的SQL编写窗口。

在这里插入图片描述

建库

  在SQL编辑窗口中,输入create database Lesson4 character set utf8;,在1处执行该语句。在2处查看执行的结果。在3处刷新查看。

  1. create database字面意思 创建数据库。
  2. character set utf8;设置该库使用的字符编码,utf8是能支持中文的编码格式。其他编码如GB2312支持简体中⽂编码,utf8能支持繁体。

  字符集:字符的集合,类似中英文字典集合了中英文所有的字。中文是象形而英文是字母。所以它们表达的是不同的字形、符号等。例如,GB2312字符集就收录了简化汉字和中文特有的符号。
  字符编码:计算机是二进制的世界010101010011,字符编码是将字符集中的字符转换为计算机内部可以识别的过程。你输入时字符“周星驰”需要通过编码来转换为二进制进行存储,你读取时需要通过编码来解读二进制为中文。
在这里插入图片描述

建表

建表语句说明:

  1. 要知道当前指定的库,一个数据库实例可以有多个库。未指定会执行失败。
  2. 表名字通过小写与_组成,比如Pop Singer需要词之间通过_来衔接pop_singer
  3. 每个列由变量名 类型(长度) COMMENT '描述'等等元素组成。在JAVA中不需要指定变量类型的长度因为是默认与固定的。
  4. uid中的
  • bigint(20) 设置了一个长整型并且长度为20字节
  • UNSIGNED标识这个整数只有正整数
  • AUTO_INCREMENT标识了这个整型将由系统自增不需要指定值。
  • PRIMARY KEY表示了它是主键。有此标识的MYSQL底层将用此列来组织索引。并且它必须也一定是唯一的。
  • COMMENT '' 是该列的描述,和注释一样,不产生任何实际效果。
  • 建表SQL中通过,来标识一个属性的定义结束。在,之前换行都是可以的。
  • ENGINE=InnoDB表示着使用什么样的索引、查询机制和其它处理细节。比如InnoDB 支持事务MyISAM 不支持。innodb是聚集索引,而myisam是非聚集索引(深入篇会讲)。Innodb不支持全文索引,而myisam支持全文索引(ES篇会讲)。innodb支持表(多线程冲锁一行数据)、行级锁,而myisam支持表级锁(每次冲突都锁表);
use Lesson4;--这里是选择当前SQL窗口(当前会话),在什么库中执行SQL。
CREATE TABLE `pop_singer` (
 `uid` bigint(20) --这里可以换行主要以,标识结束
 UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '用户 id',
 `user_name` varchar(64) COMMENT '用户名称',
 `user_email` varchar(128) COMMENT '用户 email 地址'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';
--执行后可以通过来查看表与建表时的语句,也可以通过界面
SHOW CREATE TABLE `pop_singer`;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

PS:在JAVA中通过//来添加注释描述,在SQL中通过--来描述。MYSQL中变量名字通过双`来防止与MYSQL关键字的冲突。

  • 如果当前会话的窗口会对选中的数据库粗体化,注意观察左边。
  • 如果只想执行部分SQL应选中要执行的SQL内容
    在这里插入图片描述
查表

SQL语句中,关键字是不区分大小写的。查询语句由select标识查询语句 *全部列或指定数据列 from从哪里查 表名称 where在哪里(条件的开始) and(同时满足的条件)

一条整完整的SQL语句通过;来结尾:

select * from `pop_singer`;--查询全部列数据
--指定查询部分列
select `uid`,`user_name`,`user_email` from `pop_singer`;
--指定条件查询
select `uid`,`user_name`,`user_email` from `pop_singer` 
where `user_name` = "周星驰" 
and `uid` = 1 and `user_email` = "123@qq.com"; --可以有多个and
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
删除表、删除数据、清空数据

表和数据都删除:

DROP TABLE `pop_singer`;
  • 1

删除全部数据,但是有些值不会复原,比如建表时的主键当前自增的结果(可以自行测试:插入一些数据然后删除然后再插入数据):

DELETE FROM `pop_singer`;
DELETE FROM `pop_singer` where `uid` = 1;--条件与查询一样
  • 1
  • 2

删除全部数据并且将隐藏数据清零,比如自增的结果恢复到0,测试办法一样。

truncate FROM `pop_singer`;--该语句无条件
  • 1
插入数据
insert into -- 标识是插入语句
`pop_singer` -- 往哪张表里插入数据
-- 插入的数据内容,前面提到过`uid`设置了自增所以可以不指定
		--`uid`,`user_name`,`user_email`下方要注意键表时的字段先后顺序
values (null,"周星驰","123@qq.com");	
--需要注意的是uid位置的值,null代表下一个自增的值如果不想指定则需要这样写
insert into `pop_singer`(`user_name`,`user_email`) values("周星驰","123@qq.com");
--也可以指定uid,但是如果uid存在则会报错
insert into `pop_singer` values (2,"黄家驹","234@qq.com");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
修改数据
update `pop_singer` 		--要从哪里修改
set `user_name` = "Beyond" 	--设置新的值
where `user_name` = "黄家驹";--条件
  • 1
  • 2
  • 3

执行时可能会遇到这样的问题:
在这里插入图片描述
意思是:在安全模式下,没有主键的条件不允许修改数据。所以需要修改为:

update `pop_singer` 		--要从哪里修改
set `user_name` = "Beyond" 	--设置新的值
where `user_name` = "黄家驹" and `uid` >0 ;--条件 前面指定了uid为主键
  • 1
  • 2
  • 3

取消安全模式(WINDOWS的同学在左上角的EDIT选项里):在这里插入图片描述

继续深入Java中的面向对象(类、抽象类、接口)

  这一节的意义也是相当重要的,是否能理解将伴随着你未来阅读理解源码的能力。因为所有的架构和框架都是基于这三种语言能力从而体现的面向对象设计模式。你需要确信的是,所有开发开源框架的人都深刻理解它们三者之间所强调的奥义,并同时遵循着它们的奥义。所以如果你无法理解它们的奥义,你将不会入的了源码的门
  这也是本篇教程后面需要讲解框架的意义所必须提到的设计模式的关键所在。

  在Java继承中通过extends的办法让类与类之间产生上下之间的联系,可以让我们实现一层一层的抽象从而实现多态。extends继承抽象是一个脉络的象征,是能力和特征的继承(可以试想你在面对王思聪时不是在面对他,而是在面对他爸爸的家产与实力)。到java中就是具体指继承属性和方法,属性好理解,方法继承的意义就是托底。就是儿子没有老子有。再不济也有默认的实现。

  1. 避免重复实现,父类实现了调用即可。
  2. 老子有的锤子钳子这些基础的东西就不用重复再去买了。
  3. 多态与泛型等特性的结合使用
    在这里插入图片描述

抽象类

  抽象类具备普通类的一切能力。抽象类通过public abstract class来标识。但是它通常使用在,限定调用过程与调用顺序之上。在下面的例子中,定义变量和方法的默认实现和普通类一模一样。它具备的加强能力是:

  1. setClass(int i)函数中定义了默认实现,并且定义了必须调用的过程与它们的调用顺序。
  2. setClass1()setClass2()交给子类去实现,而子类并不需要实现setClass(int i) 。这个意义体现在定义编程模型。在封装时我们强调了函数功能的单一化,一是为了简化单个函数的实现、二是为了区分不同的能力。所以这个地方在场景不那么复杂的时候是多余的,因为单个函数就可以搞定了。
  3. 所以它的具体使用意义是,需要定义先后顺序执行的场景,比如一个餐厅对厨房的规定(张三凌晨5点该把菜买回来了->李四早上7点该把食材进行初步处理了->王五8点需要梳理今天的食材能做什么菜到菜单上了)。
  4. 并且这一系列操作在这个体系下,是坚决不能更改的。所有后来的人必须遵循。(因为在厨房的问题上很有可能菜单上的菜今天买不到相应的食材。比如下大雪,灾害等等导致物流不通畅的因素。)
package Lesson4;
import java.util.ArrayList;
public abstract class AbstractClass {
    int i = 0;
    public void setClass(int i) {
        String step1 = setClass1();
        ArrayList list = setClass2();
    }
    public abstract String setClass1();
    public abstract ArrayList setClass2();
}

package Lesson4;
import java.util.ArrayList;
public class AbstractClassTest extends AbstractClass{
    @Override
    public String setClass1() {
        return null;
    }
    @Override
    public ArrayList setClass2() {
        return null;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

抽象类中的抽象方法是必须实现的,调用时和集成中的多态同样,可以自行测试:
在这里插入图片描述

接口

  类和抽象类是为了限定特征行为方式而存在。在Java中接口也具备多态的特性(使用时和父子类语法一样),但它更多的是去强调必须拥有此能力,接口本身没有任何基础的实现。语义(语法)上也不允许你有默认的实现。
  单个类或抽象类都只能有一个父类,但是接口可以有多重实现。它的意义是在你需要使用某一种能力的时候,它一定有相应能力的实现。而继承更加体现的特征的共同,父类中的默认实现并不要求子类一定要去实现,如果子类没有调用父类就ok了。
  所以接口更加强调实现,实际中也是叫做实现某个接口。先不管你实现的函数具体有没有内容,但是它要求你必须提供这样的函数(能力)。因为它本身没有任何实现。

package Lesson4;
public interface InterfaceExample2 {//通过interface来标识这不是class,没有任何实现,定义必须具备的能力即可
    void setExample2(int i);//参数是可以定义的
    String getExample2();//没有`{}`方法体,直接以`;`结束。
}
package Lesson4;
public interface InterfaceExample1 extends InterfaceExample2 {
    void setExample1(int i);
    String getExample1();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  接口与接口之间也可以继承,加强对能力的要求(实现A接口也必须实现B接口),因为InterfaceExample1继承了InterfaceExample2,所以两个接口的定义都要被实现。在这里插入图片描述
  用一个普通类(也必须是普通类)InterfaceClas通过implements关键字来实现它:

package Lesson4;
public class InterfaceClas implements InterfaceExample1 {//通过implements语法来标识实现一个函数
   @Override//这里不是必须的
   public void setExample1(int i) {
   	//在方法块中做具体的实现
   }
   @Override
   public String getExample1() {
       return null;//返回值和参数必须和接口定义中的一样
   }
   @Override
   public void setExample2(int i) {
   }
   @Override
   public String getExample2() {
       return null;
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

定义的抽象类或接口是不能直接被使用(实例化)的:
在这里插入图片描述

  也可以通过这样的方式来给它实现,这种方式方法和你新建一个类文件没有什么区别(暂时这么理解吧)。但是正如前面所说,这样的写法更符合面相过程,而不是面相对象。当然我也不是强调处处都要面向对象(比如这里完全可以新建一个类去实现它,没必要让一个函数很臃肿),java中每一个具体的函数的具体实现都必然是面相过程的编码的

package Lesson4;
import java.util.ArrayList;
public class InterfaceClasTest {
    public static void main(String[] args) {
    	//普通类实例化时只需要InterfaceExample1 interfaceExample1 = new InterfaceExample1();结束即可
    	//注意{}就行
        InterfaceExample1 interfaceExample1 = new InterfaceExample1() {
            @Override
            public void setExample2(int i) {
            }
            @Override
            public String getExample2() {
                return null;
            }
            @Override
            public void setExample1(int i) {
            }
            @Override
            public String getExample1() {
                return null;
            }
        };
        AbstractClass abstractClass = new AbstractClass() {
            @Override
            public String setClass1() {
                return null;
            }
            @Override
            public ArrayList setClass2() {
                return null;
            }
        };
        abstractClass.setClass(1);//请注意这里,它仍然可以正常调用
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

Java数据库操作

  到了这里必须要强调的内容是,Java其本身只是语言,语言的功能是描述或组织逻辑的过程。真正要实际干活的,就要依赖数据处理mysql、全文搜索es、KV存储mongo、KV缓存存储redis、等等等等等。这也就是为什么Java的范畴内往往伴随着其它诸多架构。
  Java的强大之处在,它本身只是语言,但它的生态环境与它作伴时令它无所不能。这就好像让一个只会bb叨叨讲话的人的每一句话都能有具体效益起到了实际效果。Mysql就是它能起到效果的其中之一。反过来,JAVA这类语言的存在也让Mysql有了存在的价值。因为Mysql本身这是一个存储数据的地方,它需要被发光发热。这是相辅相成的。

JDBC(设计模式初理解!)

  Java本身只专注于语言,它想要和外界(各种集群框架)接触,它必须要提供能让对方接入进来的门路。这个门路,就是接口(在JAVA中并不单指interface)。
  Java DataBase Connectivity(Java连接数据库),JAVA通过抽象出JDBC的接口,提供给各大数据库厂商(MYSQL、ORACLE、DB2)各自领域对接JAVA的实现。
  但它也不可能这么随意的去处理如此重要的环节,它必须严谨的去设计它,在面向对象中设计的真正奥义就是在设计模式上。

设计模式

  设计模式是在实践的过程中总结出来的结晶。它通过某一个专业领域普遍存在反复出现的问题总结而成。它有前人发明后人总结的,也可以是未来你设计的。如果是你来设计,那么你的设计模式必须满足:

  • 可读(你写的代码三年后再去看。或者懂得设计模式的人去看,能很清晰的找到脉络)。
  • 重用(避免重复造轮子,也就是重复代码)。
  • 扩展(最初我们的支付方式很单一,但是随着时间和互联网变化,支付方式也越来越多)。
  • 可靠(不影响以前的实现)。

      至于什它的什么各种原则,我想没有必要去了解。所有的内容(代码)都必须符合最实际达成的效果而已。也就是达成上面所说的四点,你就设计出了适合解决这一专业问题的设计模式,并且你可以为它起名。
      但是要想发明一个新的东西并不是那么容易得。必须要学习前人所总结出来的那些设计模式。知古所能知古所不能则有创新的可能。
JDBC中的桥接模式

  JAVA进程执行时,它本身并不清楚你需要使用什么样的数据库(MYSQL、ORACLE)。所以必须要清楚的是,JAVA和MYSQL都会进行版本的迭代。双方都有可能做出更改。JAVA会改变它处理MYSQL的方式,MYSQL会改变链接JAVA的处理。能确定的是:

  1. JAVA必须提供相应的方式让对方去实现。
  2. JAVA必须要在调用它时有自己的处理。

第一,提供相应的接口让数据库厂商实现。

package Lesson4;
public interface VendorInterface {//这里强调的是JAVA必须要求对方提供的内容
   String impl();//数据库厂商必须要实现的能力
}
package Lesson4;
public class MysqlVendor implements VendorInterface {//Mysql厂商
   @Override
   public String impl() {
       return "Mysql";//模拟Mysql的实现
   }
}
package Lesson4;
public class OracleVendor implements VendorInterface{//Oracle厂商
   @Override
   public String impl() {
       return "Oracle";//模拟Oracle的实现
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

第二,JAVA调用厂商的实现后,仍然需要进一步的处理:

package Lesson4;
public abstract class JavaDriverManagement {
   private VendorInterface vendorInterface;
   public JavaDriverManagement(VendorInterface vendorInterface){
       this.vendorInterface = vendorInterface;
   }
   //protected标识着该函数只能被子类调用,通常用来意味它并不是一个完整的功能
   protected String getImpl(){
       return vendorInterface.impl();//在这里默认调用了厂商所提供的具体实现
   }
}
package Lesson4;
public class MysqlClient extends JavaDriverManagement {
   public MysqlClient(VendorInterface vendorInterface) {
       super(vendorInterface);
   }
   @Override
   public String getImpl(){
       String vendor = super.getImpl();
       System.out.println("JAVA添加本身的处理"+vendor);
       return "处理完后的结果";
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

强调

  1. 父类的意义是为子类提供更基础的功能。从而避免重复造轮子。设计模式的意义与目标之一
  2. 当老板把任务交给员工时,往往希望员工自己去识别其中的含义。所以为了多态(归类),参考new MysqlClient(new MysqlVendor())的MysqlClient构造函数。如果这里没有这个JavaDriverManagement父类。那么我在传参的时候就必须要明确MysqlClientOracleClient。这就意味着它的上限只能处理它,下限是它的子类。
  3. 加上MysqlClient重写了父类JavaDriverManagement中的getImpl()方法,满足了多态调用。它的意义是通过JavaDriverManagement定义了对象,但是具体是怎么样的实现我并不关心。
  4. 如果MysqlClient没有重写则不能调用getImpl,如果它没有实现,JavaDriverManagement中的getImpl()方法是protected标识的,protected标识着该函数只能被子类调用,你可以尝试一下如果子类没有重写去调用。
  5. 所以protected为设计模式提供了基础的限制能力,当然还有其它。
package Lesson4;
public class JavaDriverManagementTest {
   public static void main(String[] args) {
			//1.把具体的厂商实现传入进去
			//2.通过MYSQL的方式来处理该厂商的实现
       JavaDriverManagement jdbc = new MysqlClient(new MysqlVendor());
       	//3.这里是MysqlClient重写getImpl()的真正意义
       String result = jdbc.getImpl();
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

设计模式的组成有非常多的关键元素,需要慢慢理解

  要学会设计模式必须要明白它要干什么满足什么场景,它实际起到了什么作用,结合实际思考。不用想的很复杂。
  比如桥接模式的意义就是在双方都有联系的情况下,双方都有各自的实现,又要满足双方修改或扩展的可能。

Java JDBC的实际图示
  1. Connection对应上面代码的VendorInterface
  2. ConnectionImpl对应MysqlVendor
  3. DriverManager对应JavaDriverManagement以及其子类
  4. client对应JavaDriverManagementTest
    在这里插入图片描述

Java数据库操作:

  铺垫了这么多,目的就是为了让大家知道JDBC是个什么东西。在JAVA操作数据库时的一些操作你就不会迷惑。从而更好的理解关键内容。

引入Mysql提供的Java实现

  在Java中引入第三方实现,是通过jar包的方式。jar包就是java程序编译后的完整集合,导入到你的项目即可通过import关键字在你自己的java类中使用。

下载jar包

  mvnrepository仓库地址,选择jar包即可下载。在这里插入图片描述

引入到idea工程内

按图片示例步骤操作后点击OK即可:
在这里插入图片描述
  通过上面的引入jar包后,我们展开External Libraries,可以看到<1.8>这是java本身自带的全部类,新建工程的时候是默认给你引入的。而mysql-connector你不操作引入是无法在这里看到的。
在这里插入图片描述

开始操作数据库前的认识

  在开始之前,需要了解三个必要的操作。它们都属于java.sql包下,是java自带的数据库操作封装。

  1. JAVA连接数据库(java.sql.Connection类完成)。
  2. 操作数据库(java.sql.Statement
  3. 获取操作的结果(java.sql.ResultSet)。

下例代码演示了必要的准备工作,以及这些必要工作的解释:

  1. Connection Statement ResultSet ,它们必须要定义在try代码块之外,因为操作数据库的连接需要在后续的finally代码块中进行释放和关闭。如果只是执行一两次的程序是不需要关心的,但如果有更多的执行次数将造成程序崩溃(内存泄漏,原理篇会讲)。
  2. try,catch,finally代码块的执行顺序是:
  • 情况一:无异常则try->finally
  • 情况二:有异常则try->catch->finally
  • 也就是说无论如何try代码块中无论如何执行都会执行finally
  1. Class.forName("com.mysql.jdbc.Driver");
  • java中类分三种,1)java本身自带的,2)你编写的,3)通过引入External Libraries后,Class.forName第三方引入的
  • 在jar包引入External Libraries后,仅仅只是说我们的工程内有了这个内容。但是具体运行时还没有引用。Class.forName则是在程序运行时动态的将此类加载,并且自动的帮你实例化

先看代码:

package Lesson4;
import java.sql.*;
public class MysqlSearchTest {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            // 加载MySQL驱动程序(反射,后面会讲,暂时理解为通过这样的方式能引入三方jar包到java运行时即可。)
            Class.forName("com.mysql.jdbc.Driver");
            /**
             * 此处使用 conn stmt rs完整数据库操作
             */
            //数据库连接变量定义
            String url = "jdbc:mysql://localhost:3306/Lesson4?useSSL=false";
            String user = "root";
            String password = "123456789";
            conn = DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 关闭ResultSet、Statement和Connection
            try {
                if (rs != null) {
                    rs.close();
                }
                if (stmt != null) {
                    stmt.close();
                }
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

  刚才我们提到,Class.forName会加载外部类,并且动态的实例化,也就是会执行构造函数。那么它是怎么能让java识别到的呢。我们来看看Mysql的源码做了什么:

  1. 它调用了java中的DriverManager在这里插入图片描述
  2. 并且向registeredDrivers中注册了它的数据库驱动类。这和上面代码示例中使用到的DriverManager.getConnection就对上了。在这里插入图片描述
查询数据库

  上面的代码中,为了不让更多的代码迷惑你,去掉了部分代码,这里是上面代码中的完整的内容:

  1. Statement 中文翻译就是语句,就是执行sql的操作类。
  2. ResultSet 就是结果集,不同的操作类型(增删改查)会返回不同的结果集。
package Lesson4;
import java.sql.*;
public class MysqlSearchTest {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            // 加载MySQL驱动程序(反射,后面会讲,暂时理解为通过这样的方式能引入三方jar包到java运行时即可。)
            Class.forName("com.mysql.jdbc.Driver");
            //数据库连接变量定义
            String url = "jdbc:mysql://localhost:3306/Lesson4?useSSL=false";
            String user = "root";
            String password = "123456789";
            conn = DriverManager.getConnection(url, user, password);
            stmt = conn.createStatement();
            rs = stmt.executeQuery("SELECT * FROM pop_singer");
            // 处理查询结果
            //while(ture or false){}循环体中()处需要的是一个boolean值,boolean基础类型只有两个true真,false假。
            //如果while(true)时,{}代码块中的代码将被无限执行。
            //所以此时rs.next()返回的是否有下一个值的 true false。
            //如果有,则获取"SELECT * FROM pop_singer"查询中返回的每一行数据的uid列值。
            while (rs.next()) {
                System.out.println(rs.getString("uid")); //获取uid这一列的数据
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 关闭ResultSet、Statement和Connection
            try {
                if (rs != null) {
                    rs.close();
                }
                if (stmt != null) {
                    stmt.close();
                }
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

我们可以通过把断点调试放在while循环那一行,从而来观察rs返回的内容:

  1. 如果你点进去源码看过应该能发现ResultSet是一个接口
  2. 调试时我们则可以看到它具体的对象实例是哪个类:ResultSetImpl
    在这里插入图片描述
  3. 找到columnDefinition可以看到真实的返回结果。所以通过rs.getString("uid")调用的是被完整处理过后的列结果:在这里插入图片描述
新增数据

  新增数据时需要将Statement定义为PreparedStatement,该类可以帮助你通过?占位符预处理sql。当然使用Statement也是可以的。只不过sql变量就要修改为/** */注释处的sql2。此时将不再拥有PreparedStatement.setString的功能。

package Lesson4;
import java.sql.*;
public class MysqlInsertTest {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            // 加载MySQL驱动程序(反射,后面会讲,暂时理解为通过这样的方式能引入三方jar包到java运行时即可。)
            Class.forName("com.mysql.jdbc.Driver");
            //数据库连接变量定义
            String url = "jdbc:mysql://localhost:3306/Lesson4?useSSL=false";
            String user = "root";
            String password = "123456789";
            conn = DriverManager.getConnection(url, user, password);
            /**
             * String sql2 = "INSERT INTO pop_singer (user_name, user_email) VALUES ('周雅芬', '111@qq.com')";
             */
            String sql = "INSERT INTO pop_singer (user_name, user_email) VALUES (?, ?)";
            stmt = conn.prepareStatement(sql);
            //setString setInt 等等
            stmt.setString(1, "周雅芬"); // 第一个参数为索引位置,第一个?需要插入的值
            stmt.setString(2, "111@qq.com"); // 第二个参数为索引位置,value2为需要插入的值
            int affected = stmt.executeUpdate(); //我们写入了一条sql,如果成功则是返回1
            //java中""为字符串,一切皆可拼接为字符串
            System.out.println("写入了"+affected+"条数据");//这里"写入了"是一个字符串通过+和affected拼接,然后通过+和"条数据"拼接
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 关闭ResultSet、Statement和Connection
            try {
                if (rs != null) {
                    rs.close();
                }
                if (stmt != null) {
                    stmt.close();
                }
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
修改数据

  此时重新示范一下通过Statement来执行,而不是PreparedStatement就没有占位符。

package Lesson4;
import java.sql.*;

public class MysqlUpdateTest {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            // 加载MySQL驱动程序(反射,后面会讲,暂时理解为通过这样的方式能引入三方jar包到java运行时即可。)
            Class.forName("com.mysql.jdbc.Driver");
            //数据库连接变量定义
            String url = "jdbc:mysql://localhost:3306/Lesson4?useSSL=false";
            String user = "root";
            String password = "123456789";
            conn = DriverManager.getConnection(url, user, password);
            stmt = conn.createStatement();
            /**
			 * 运行时调试sql拼接后的内容为:
			 * update pop_singer set user_name = '周雅芬' , user_email = '999@qq.com' where user_name = '周雅芬'
			 * 如果我们在Workbench操作数据库 则 单引号'' 双引号""都可以,但在java中"本身就代表了字符串",所以如果需要拼接时,需要通过'来区分
			 * +"周雅芬"+,+"999@qq.com"+,+"周雅芬"+本身可以理解为它们是三个变量,上面新增的注释中我强调了拼接字符串的使用
			 * 我们拼接出来的东西必须是能拿到Workbench验证能执行的,update pop_singer set user_name = 周雅芬 , user_email = 999@qq.com where user_name = 周雅芬,如果是这条语句将无法执行报错
			 */
            String sql = "update pop_singer set user_name = '"+"周雅芬"+"' , user_email = '"+"999@qq.com"+"' where user_name = '"+"周雅芬"+"'";
            int affected = stmt.executeUpdate(sql); //如果修改成功返回修改的行数
            System.out.println("修改了"+affected+"条数据");
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 关闭ResultSet、Statement和Connection
            try {
                if (rs != null) {
                    rs.close();
                }
                if (stmt != null) {
                    stmt.close();
                }
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
删除数据
package Lesson4;
import java.sql.*;
public class MysqlRemoveTest {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            // 加载MySQL驱动程序(反射,后面会讲,暂时理解为通过这样的方式能引入三方jar包到java运行时即可。)
            Class.forName("com.mysql.jdbc.Driver");
            //数据库连接变量定义
            String url = "jdbc:mysql://localhost:3306/Lesson4?useSSL=false";
            String user = "root";
            String password = "123456789";
            conn = DriverManager.getConnection(url, user, password);
            stmt = conn.createStatement();
            String sql = "delete from pop_singer where user_name = '"+"周雅芬"+"'";
            int affected = stmt.executeUpdate(sql); //如果修改成功返回修改的行数
            System.out.println("删除了"+affected+"条数据");
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 关闭ResultSet、Statement和Connection
            try {
                if (rs != null) {
                    rs.close();
                }
                if (stmt != null) {
                    stmt.close();
                }
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
强调

在java中操作数据库,重中之中是要确保真正执行的SQL是能够在Workbench中直接执行的。所以在出现错误时反复的调试非常关键。

接下来要思考的场景(下一课内容)

  1. 上面的基础操作非常繁琐,如果有成千上万个需要操作数据库的功能你将重复这些代码。
  2. 每一次都需要新建关闭连接,在计算机的世界中上千万亿次的执行中,是极大的浪费。
  3. 在查询时,如果有很多个属性将需要一个一个的通过这样的方式来获取。
	while (rs.next()) {
                System.out.println(rs.getString("uid")); //获取Studentname这一列的数据
                System.out.println(rs.getString("uid")); //获取Studentname这一列的数据
                System.out.println(rs.getString("uid")); //获取Studentname这一列的数据
                System.out.println(rs.getString("uid")); //获取Studentname这一列的数据
                System.out.println(rs.getString("uid")); //获取Studentname这一列的数据
            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

目前为止你的能力

学到这里,如论如何你可以在公司中承担处理数据库中的数据的任务了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/677240
推荐阅读
相关标签
  

闽ICP备14008679号