数据库
数据库是专门用来存储一系列集合数据的地方。所有的文件都可以被称之为库,当应用场景没那么复杂的时候,简单的应用程序用文本就可以了。数据库的意义是为了设计更好的保障数据安全(如多线程操作)、数据一致、索引(如何在庞大的数据中快速查找)等等一系列能满足更庞大的场景需求的功能。
基础概念
如果你想设计一个你自己的数据库,必须要满足以下特征:
- 原子性(Atomicity):如果同时往库里写入或修改多条数据(也可以是一批次操作),必须满足要么都成功要么都失败。在数据库里这个也叫事务(Transaction)
- 一致性(Consistency):多节点之间保持数据的一致。
- 场景一:你从微信中提现到你得银行卡,需要同时确保微信扣款成功,银行卡入款成功。反之往微信充钱也一个道理。
- 场景二:集群化的数据库之间(目前了解一下即可)需要保证数据同时写入修改或删除。
- 隔离性(Isolation)也叫独立性:就是咱们上一课中讲的多线程安全。
- 持久性(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
。如果是:
- 在程序偏好设置中找到mysql,并stop掉它。
- 在命令行执行:
sudo chmod -R a+rwx /usr/local/mysql/data/
输入你电脑的密码即可。- 再启动
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
在最初的操作界面中
- 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:运行性能大屏
- Management
- Schemas:数据库目录
- 目前只有sys一个系统库
大概了解之后选择红色箭头指向按钮来,创建一个新的SQL编写TAB页。可以同时新建多个来区分不同的SQL编写窗口。
建库
在SQL编辑窗口中,输入
create database Lesson4 character set utf8;
,在1处执行该语句。在2处查看执行的结果。在3处刷新查看。
create database
字面意思 创建数据库。character set utf8;
设置该库使用的字符编码,utf8是能支持中文的编码格式。其他编码如GB2312
支持简体中⽂编码,utf8能支持繁体。
字符集
:字符的集合,类似中英文字典集合了中英文所有的字。中文是象形而英文是字母。所以它们表达的是不同的字形、符号等。例如,GB2312字符集就收录了简化汉字和中文特有的符号。
字符编码
:计算机是二进制的世界010101010011,字符编码是将字符集中的字符转换为计算机内部可以识别的过程。你输入时字符“周星驰”需要通过编码来转换为二进制进行存储,你读取时需要通过编码来解读二进制为中文。
建表
建表语句说明:
- 要知道当前指定的库,一个数据库实例可以有多个库。未指定会执行失败。
- 表名字通过小写与
_
组成,比如Pop Singer需要词之间通过_
来衔接pop_singer
。- 每个列由
变量名
类型(长度)
COMMENT '描述'
等等元素组成。在JAVA中不需要指定变量类型的长度因为是默认与固定的。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`;
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
删除表、删除数据、清空数据
表和数据都删除:
DROP TABLE `pop_singer`;
删除全部数据,但是有些值不会复原,比如建表时的主键当前自增的结果(可以自行测试:插入一些数据然后删除然后再插入数据):
DELETE FROM `pop_singer`; DELETE FROM `pop_singer` where `uid` = 1;--条件与查询一样
删除全部数据并且将隐藏数据清零,比如自增的结果恢复到0,测试办法一样。
truncate FROM `pop_singer`;--该语句无条件
插入数据
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");
修改数据
update `pop_singer` --要从哪里修改
set `user_name` = "Beyond" --设置新的值
where `user_name` = "黄家驹";--条件
执行时可能会遇到这样的问题:
意思是:在安全模式下,没有主键的条件不允许修改数据。所以需要修改为:update `pop_singer` --要从哪里修改 set `user_name` = "Beyond" --设置新的值 where `user_name` = "黄家驹" and `uid` >0 ;--条件 前面指定了uid为主键
取消安全模式(WINDOWS的同学在左上角的EDIT选项里):
继续深入Java中的面向对象(类、抽象类、接口)
这一节的意义也是相当重要的,是否能理解将伴随着你未来阅读理解源码的能力
。因为所有的架构和框架都是基于这三种语言能力
从而体现的面向对象
与设计模式
。你需要确信的是,所有开发开源框架的人都深刻理解它们三者之间所强调的奥义,并同时遵循着它们的奥义。所以如果你无法理解它们的奥义,你将不会入的了源码的门
。
这也是本篇教程后面需要讲解框架的意义
所必须提到的设计模式
的关键所在。
在Java继承中通过
extends
的办法让类与类之间产生上下之间的联系,可以让我们实现一层一层的抽象从而实现多态。extends继承抽象
是一个脉络的象征,是能力和特征的继承(可以试想你在面对王思聪时不是在面对他,而是在面对他爸爸的家产与实力)。到java中就是具体指继承属性和方法,属性好理解,方法继承的意义就是托底。就是儿子没有老子有。再不济也有默认的实现。
- 避免重复实现,父类实现了调用即可。
- 老子有的锤子钳子这些基础的东西就不用重复再去买了。
- 多态与泛型等特性的结合使用
抽象类
抽象类具备普通类的一切能力。抽象类通过public abstract class
来标识。但是它通常使用在,限定调用过程与调用顺序之上。在下面的例子中,定义变量和方法的默认实现和普通类一模一样。它具备的加强能力是:
- 在
setClass(int i)
函数中定义了默认实现,并且定义了必须调用的过程与它们的调用顺序。 -
setClass1()
与setClass2()
交给子类去实现,而子类并不需要实现setClass(int i)
。这个意义体现在定义编程模型
。在封装时我们强调了函数功能的单一化,一是为了简化单个函数的实现、二是为了区分不同的能力。所以这个地方在场景不那么复杂的时候是多余的,因为单个函数就可以搞定了。 - 所以它的具体使用意义是,需要定义先后顺序执行的场景,比如一个餐厅对厨房的规定(张三凌晨5点该把菜买回来了->李四早上7点该把食材进行初步处理了->王五8点需要梳理今天的食材能做什么菜到菜单上了)。
- 并且这一系列操作在这个体系下,是坚决不能更改的。所有后来的人必须遵循。(因为在厨房的问题上很有可能菜单上的菜今天买不到相应的食材。比如下大雪,灾害等等导致物流不通畅的因素。)
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;
}
}
抽象类中的抽象方法是必须实现的,调用时和集成中的多态同样,可以自行测试:
接口
类和抽象类是为了限定特征
和行为方式
而存在。在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();
}
接口与接口之间也可以继承,加强对能力的要求(实现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; } }
定义的抽象类或接口是不能直接被使用(实例化)的:
也可以通过这样的方式来给它
实现
,这种方式方法和你新建一个类文件没有什么区别(暂时这么理解吧)。但是正如前面所说,这样的写法更符合面相过程
,而不是面相对象
。当然我也不是强调处处都要面向对象
(比如这里完全可以新建一个类去实现它,没必要让一个函数很臃肿
),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);//请注意这里,它仍然可以正常调用
}
}
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的处理。能确定的是:
- JAVA必须提供相应的方式让对方去实现。
- 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的实现 } }
第二
,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 "处理完后的结果"; } }
强调
- 父类的意义是为子类提供更基础的功能。从而避免重复造轮子。
设计模式的意义与目标之一
。- 当老板把任务交给员工时,往往希望员工自己去识别其中的含义。所以为了多态(归类),参考
new MysqlClient(new MysqlVendor())的MysqlClient构造函数
。如果这里没有这个JavaDriverManagement
父类。那么我在传参的时候就必须要明确MysqlClient
,OracleClient
。这就意味着它的上限只能处理它,下限是它的子类。- 加上
MysqlClient
重写了父类JavaDriverManagement
中的getImpl()
方法,满足了多态调用。它的意义是通过JavaDriverManagement
定义了对象,但是具体是怎么样的实现我并不关心。- 如果
MysqlClient
没有重写则不能调用getImpl
,如果它没有实现,JavaDriverManagement
中的getImpl()
方法是protected
标识的,protected标识着该函数只能被子类调用,你可以尝试一下如果子类没有重写去调用。- 所以
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(); } }
设计模式的组成有非常多的关键元素,需要慢慢理解
要学会设计模式必须要明白它要干什么满足什么场景,它实际起到了什么作用,结合实际思考。不用想的很复杂。
比如桥接模式的意义就是在双方都有联系的情况下,双方都有各自的实现,又要满足双方修改或扩展的可能。
Java JDBC的实际图示
- Connection对应上面代码的VendorInterface
- ConnectionImpl对应MysqlVendor
- DriverManager对应JavaDriverManagement以及其子类
- 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自带的数据库操作封装。
- JAVA连接数据库(
java.sql.Connection
类完成)。 - 操作数据库(
java.sql.Statement
) - 获取操作的结果(
java.sql.ResultSet
)。
下例代码演示了必要的准备工作,以及这些必要工作的解释:
Connection
Statement
ResultSet
,它们必须要定义在try
代码块之外,因为操作数据库的连接需要在后续的finally
代码块中进行释放和关闭。如果只是执行一两次的程序是不需要关心的,但如果有更多的执行次数将造成程序崩溃(内存泄漏,原理篇会讲)。try
,catch
,finally
代码块的执行顺序是:
- 情况一:无异常则try->finally
- 情况二:有异常则try->catch->finally
- 也就是说无论如何try代码块中无论如何执行都会执行finally
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();
}
}
}
}
刚才我们提到,Class.forName会加载外部类,并且动态的实例化,也就是会执行构造函数。那么它是怎么能让java
识别到
的呢。我们来看看Mysql的源码做了什么:
- 它调用了java中的
DriverManager
- 并且向registeredDrivers中注册了它的数据库驱动类。这和上面代码示例中使用到的
DriverManager.getConnection
就对上了。
查询数据库
上面的代码中,为了不让更多的代码迷惑你,去掉了部分代码,这里是上面代码中的完整的内容:
- Statement 中文翻译就是语句,就是执行sql的操作类。
- 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();
}
}
}
}
我们可以通过把断点调试放在while循环那一行,从而来观察rs返回的内容:
- 如果你点进去源码看过应该能发现ResultSet是一个接口
- 调试时我们则可以看到它具体的对象实例是哪个类:
ResultSetImpl
- 找到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();
}
}
}
}
修改数据
此时重新示范一下通过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();
}
}
}
}
删除数据
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();
}
}
}
}
强调
在java中操作数据库,重中之中是要确保真正执行的SQL是能够在Workbench中直接执行的。所以在出现错误时反复的调试非常关键。文章来源:https://www.toymoban.com/news/detail-833848.html
接下来要思考的场景(下一课内容)
- 上面的基础操作非常繁琐,如果有成千上万个需要操作数据库的功能你将重复这些代码。
- 每一次都需要新建关闭连接,在计算机的世界中上千万亿次的执行中,是极大的浪费。
- 在查询时,如果有很多个属性将需要一个一个的通过这样的方式来获取。
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这一列的数据
}
目前为止你的能力
学到这里,如论如何你可以在公司中承担处理数据库中的数据的任务了。文章来源地址https://www.toymoban.com/news/detail-833848.html
到了这里,关于完全从零Java自学系列【入门篇】(第四课:Mysql服务端安装&使用客户端操作数据库&初识SQL基础操作&Java中使用第三方包&Java数据库操作&初步理解面相对象真正的意义之桥接设计模式)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!