0%

前提补充

我们讲过期策略之前,我们先来看几个问题,后面学习的时候带着这几个问题去学习。

我往redis里写的数据怎么没了?

我相信大家在使用redis的时候,都会遇到redis经常丢掉一些数据,写进去了,过一会儿可能就没了。那么这个问题我们需要反思一下:redis是缓存,你给当存储了是吧?

什么是缓存?用内存当缓存。内存是无限的吗,内存是很宝贵而且是有限的,磁盘是廉价而且是大量的。可能一台机器就几十个G的内存,但是可以有几个T的硬盘空间。redis主要是基于内存来进行高性能、高并发的读写操作的。

那既然内存是有限的,比如redis就只能用10个G,你要是往里面写了20个G的数据,会咋办?当然会干掉10个G的数据,然后就保留10个G的数据了。那干掉哪些数据?保留哪些数据?当然是干掉不常用的数据,保留常用的数据了。

所以说,这是缓存的一个最基本的概念,数据是会过期的,要么是你自己设置个过期时间,要么是redis自己给干掉了。

set key value 过期时间(1小时)
set进去的key,1小时之后就没了,就失效了

我的数据明明都过期了,怎么还占用着内存啊?

还有一种就是如果你设置好了一个过期时间,你知道redis是怎么给你弄成过期的吗?什么时候删除掉?如果你不知道,那么你就需要思考,为啥好多数据明明应该过期了,结果发现redis内存占用还是很高?那是因为你不知道redis是怎么删除那些过期key的。

redis内存一共是10g,你现在往里面写了5g的数据,结果这些数据明明你都设置了过期时间,要求这些数据1小时之后都会过期,结果1小时之后,你回来一看,redis机器,怎么内存占用还是50%呢?5g数据过期了,我从redis里查,是查不到了,结果过期的数据还占用着redis的内存。

如果你连这个问题都不知道,上来就懵了,回答不出来,那线上你写代码的时候,想当然的认为写进redis的数据就一定会存在,后面导致系统各种漏洞和bug,谁来负责?

对key设置过期时间

expire key time(以秒为单位)–这是最常用的方式

Redis对存储值的过期处理实际上是针对该值的键(key)处理的,即时间的设置也是设置key的有效时间。
Expires字典保存了所有键的过期时间,Expires也被称为过期字段。

注意:
1、除了字符串自己独有设置过期时间的方法外,其他方法都需要依靠expire方法来设置时间
2、如果没有设置时间,那缓存就是永不过期
3、如果设置了过期时间,之后又想让缓存永不过期,使用persist key

过期策略

三种过期策略机制

1)定时删除(没人用)
含义:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除。

优点:可以保证内存被尽快释放

缺点:若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key

2)惰性删除
含义:key过期的时候不删除,每次从redis获取key的时候去检查是否过期,若过期,则删除,返回null。

优点:删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的key了)

缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)

3)定期删除
含义:所谓定期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。

优点:通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用–处理”定时删除”的缺点

缺点:
在内存友好方面,不如”定时删除”
在CPU时间友好方面,不如”惰性删除”

定期删除过期key可以处理”惰性删除”的缺点

三种过期策略机制总结

看完上面三种策略后可以得出以下结论:
定时删除和定期删除为主动删除:Redis会定期主动淘汰一批已过去的key
惰性删除为被动删除:用到的时候才会去检验key是不是已过期,过期就删除。

定期删除可以通过:
第一、配置redis.conf的hz选项,默认为10 (即1秒执行10次,100ms一次,值越大说明刷新频率越快,最Redis性能损耗也越大) 
第二、配置redis.conf的maxmemory最大值,当已用内存超过maxmemory限定时,就会触发主动清理策略

Redis默认采用的过期策略

Redis服务器默认过期内置策略是:惰性删除+定期删除

惰性删除流程:
在进行get或setnx等操作时,先检查key是否过期;
若过期,删除key,然后执行相应操作;
若没过期,直接执行相应操作。

定期删除流程:
(简单而言,对指定个数个库的每一个库随机删除小于等于指定个数个过期key)
遍历每个数据库(就是redis.conf中配置的”database”数量,默认为16)
检查当前库中的指定个数个key(默认是每个库检查20个key,注意相当于该循环执行20次,循环体时下边的描述)
如果当前库中没有一个key设置了过期时间,直接执行下一个库的遍历。
随机获取一个设置了过期时间的key,检查该key是否过期,如果过期,删除key。
判断定期删除操作是否已经达到指定时长,若已经达到,直接退出定期删除。

提问:为什么不使用定时删除,而使用定期删除呢?
所谓定期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。假设redis里放了10万个key,都设置了过期时间,你每隔几百毫秒,就检查10万个key,那redis基本上就死了,cpu负载会很高的,消耗在你的检查过期key上了。注意,这里可不是每隔100ms就遍历所有的设置过期时间的key,那样就是一场性能上的灾难。实际上redis是每隔100ms随机抽取一些key来检查和删除的。

持久化机制对过期key的处理

RDB对过期key的处理

过期key对RDB没有任何影响
从内存数据库持久化数据到RDB文件
持久化key之前,会检查是否过期,过期的key不进入RDB文件
从RDB文件恢复数据到内存数据库
数据载入数据库之前,会对key先进行过期检查,如果过期,不导入数据库(主库情况)

AOF对过期key的处理

过期key对AOF没有任何影响
从内存数据库持久化数据到AOF文件:
当key过期后,还没有被删除,此时进行执行持久化操作(该key是不会进入aof文件的,因为没有发生修改命令)
当key过期后,在发生删除操作时,程序会向aof文件追加一条del命令(在将来的以aof文件恢复数据的时候该过期的键就会被删掉)
AOF重写
重写时,会先判断key是否过期,已过期的key不会重写到aof文件

内存淘汰机制

内存淘汰引入

想一想,redis服务器默认过期内置策略是:惰性删除+定期删除。

所谓定期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。假设redis里放了10万个key,都设置了过期时间,你每隔几百毫秒,就检查10万个key,那redis基本上就死了,cpu负载会很高的,消耗在你的检查过期key上了。注意,这里可不是每隔100ms就遍历所有的设置过期时间的key,那样就是一场性能上的灾难。实际上redis是每隔100ms随机抽取一些key来检查和删除的。

但是问题是,定期删除可能会导致很多过期key到了时间并没有被删除掉,那咋整呢?所以就是惰性删除了。这就是说,在你获取某个key的时候,redis会检查一下 ,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。
并不是key到时间就被删除掉,而是你查询这个key的时候,redis再懒惰的检查一下。
通过上述两种手段结合起来,保证过期的key一定会被干掉。

很简单,就是说,你的过期key,靠定期删除没有被删除掉,还停留在内存里,占用着你的内存呢,除非你的系统去查一下那个key,才会被redis给删除掉。

但是实际上这还是有问题的,如果定期删除漏掉了很多过期key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?
如果大量过期key堆积在内存里,导致redis内存块耗尽了,咋整?

答案是:走内存淘汰机制。

内存淘汰机制

如果redis的内存占用过多的时候,此时会进行内存淘汰,有如下一些策略:
redis 10个key,现在已经满了,redis需要删除掉5个key
1个key,最近1分钟被查询了100次
1个key,最近10分钟被查询了50次
1个key,最近1个小时倍查询了1次

1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了
2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)
3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的key给干掉啊
4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key(这个一般不太合适)
5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key
6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除

AMQP是什么

AMQP(Advanced Message Queuing Protocol,高级消息队列协议)是一个进程间传递异步消息的网络协议。

在异步通讯中,消息不会立刻到达接收方,而是被存放到一个容器中,当满足一定的条件之后,消息会被容器发送给接收方,这个容器即消息队列,而完成这个功能需要双方和容器以及其中的各个组件遵守统一的约定和规则,AMQP就是这样的一种协议,消息发送与接受的双方遵守这个协议可以实现异步通讯。这个协议约定了消息的格式和工作方式。


阅读全文 »

简介

Feign使得 Java HTTP 客户端编写更方便。Feign 灵感来源于Retrofit、JAXRS-2.0和WebSocket。
Feign最初是为了降低统一绑定Denominator到HTTP API的复杂度,不区分是否支持Restful。
Feign旨在通过最少的资源和代码来实现和HTTP API的连接。通过可定制的解码器和错误处理,可以编写任意的HTTP API。

为什么选择Feign?

你可以使用 Jersey 和 CXF 这些来写一个 Rest 或 SOAP 服务的java客服端。
你也可以直接使用 Apache HttpClient 来实现。
但是 Feign 的目的是尽量的减少资源和代码来实现和 HTTP API 的连接。通过自定义的编码解码器以及错误处理,你可以编写任何基于文本的 HTTP API。

Feign工作机制

Feign通过注解注入一个模板化请求进行工作。只需在发送之前关闭它,参数就可以被直接的运用到模板中。
然而这也限制了Feign,只支持文本形式的API,它在响应请求等方面极大的简化了系统。同时,它也是十分容易进行单元测试的。

版本兼容性

Feign 10.x 及以上版本基于 Java 8 构建,应该适用于 Java 9、10 和 11。
对于需要兼容 JDK 6 的用户,请使用 Feign 9.x

功能概述

这是一张包含 feign 提供的当前关键功能的地图:
feign功能概述

Feign使用简介

Java8新特性简介

1)速度更快
2)代码更少(增加了新的语法Lambda表达式)
3)强大的Stream API
4)便于并行
5)最大化减少空指针异常 Optional

其中 Lambda 表达式与 Stream API 最为核心的。

阅读全文 »

Mysql数据库

MySql基础

数据库的好处

可以持久化到本地
结构化查询


主流的数据库介绍(了解)

Sql server 数据库
是微软,.net程序员最爱,中型和大型项目,性能高

Oracle数据库
是甲骨文的,java程序员(必学),大型项目,特点是适合处理复杂业务逻辑。

Mysql数据库
是sun公司,属于甲骨文公司,中型大型项目,特点:并发性好。对简单的sql处理效率高。

db2数据库
是ibm公司,处理海量数据,大型项目。很强悍。

Informix数据库
是ibm公司。在银行系统,安全性高

Sybase数据库


mysql的优点

开源,免费成本低
性能高,移植性也好
体积小,便于安装

mysql数据库的安装和配置

看MySQL-5.7.17安装与配置.docx

mysql的基本使用

(1)连接到mysql
Cmd>mysql -h 主机名 -u 用户名 -p 密码 回车
举例:

说明:
如果你没有写-h localhost 默认是连接本地

如果你需要远程登录到另一个mysql,则需要修改配置。
一般情况下部让远程登录。

(2)sql服务的退出
exit或ctrl+c

(3)关闭和启动mysql服务
界面操作:

命令模式:
启动:net stop mysql
停止:net start mysql
举例:

说明:这里mysql不是固定的,是安装时取的服务名称。

mysql数据库三层架构介绍

专业术语

Db:
数据库(database)存储数据的”仓库”。它保存了一系列有组织的数据。

Dbms:
数据库管理系统(database management system)。数据库是通过dbms创建和操作的容器。

Sql:
结构化查询语言(Structure Query Langeuage):专门用来与数据库通信的语言。

数据库服务器,数据库和表的关系如图所示:

示意图:

mysql常见命令

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
1.查看当前所有的数据库
show databases;

2.打开指定的库
use 库名;

3.查看所在的数据库名
select database();

4.查看当前库的所有表
show tables;

5.查看其它库的所有表
show tables from 库名;

6.创建表
create table user(id int, name varchar(20));

7.查看表结构
desc 表名;

8.查看服务器的版本
方式一:登录到mysql服务端
Select version();
方式二:没有登录到mysql服务端
Mysql --version或mysql --V

9.查看当前时区
SHOW VARIABLES LIKE 'time_zone'
SET time_zone='+9:00';

Mysql的语法规范

1.不区分大小写,单建议关键字大写,表名,列名小写。
2.每条语句最好用分号结尾。
3.每条命令根据需要,可以进行缩进或换行
4.注释
单行注释:两种
第一种:#注释文字
第二种:– 注释文字
多行注释:/* 注释文字 */

常用数据类型

数值型

整型

特点:
1)如果不设置无符号还是有符号,默认是有符号,如果想设置无符号,需要添加unsigned关键字
2)如果插入的数值超出了整型范围,会报out of range异常,并且插入临界值
3)如果不设置长度,会有默认的长度,长度代表了显示的最大宽度,如果不够用0在左边填充,但必须搭配zerofill使用!

小数

浮点型:
Float(m, d),double(m, d)
4 8

定点型:
Dec(m, d),decimal(m, d)
M+2 m+2

特点:
(1)
M:整数部位+小数部位(总长度)
D:小数部位
如果超出范围插入临界值

(2)
M和D都可以省略
如果是decimal,则m默认为10,d默认为0
如果是double和float,则会随着插入的数值的精度来决定精度

(3)定点型的精确度较高,如果要求插入数值的精度较高,如:货币运算等则考虑使用

字符型

较短的文本:
Char
Varchar

特点:

写法 m的意思 特点 空间的耗费 效率
Char char(m) 最大的字符数 固定长度 比较耗费 高
Varchar varchar(m) 最大的字符数 可变长度的字符 比较节省 低

较长的文本:
Text
Blob(较大的二进制)

Enum类型:
说明:又称为枚举类型哦,要求插入的值必须属于列表中指定的值之一。
如果列表成员为1255,则需要1个字节存储
如果列表成员为255
65535,则需要2个字节存储
最多需要65535个成员

Set类型:
说明:和Enum类型类似,里面可以保存0~64个成员。和Enum类型最大的区
别是:SET类型一次可以选取多个成员,而Enum只能选一个
根据成员个数不同,存储所占的字节也不同

日期型

总结:
Date:只保存日期,并没有保存到时分秒
Time:只保存时间(时分秒)
Year:只保存年

Datetime:保存日期+时间
Timestamp:保存日期+时间

Datetime VS Timestamp

字节 范围 时区等的影响
Datetime 8 1000-9999 不受
Timestamp 4 1970-2038 受

DQL语言(select)

基础查询

语法

select 查询列表 from 表名

特点

查询列表开源是字段,常量表达式,函数,也可以有多个。
查询结构是一个虚拟表。

示例

1.查询单个字段
Select 字段名 from 表名

2.查询多个字段
Select 字段名,字段名 from 表名

3.查询所有的字段
Select * from 表名

4.查询常量
Select 常量值
注意:字符型和日期型常量值必须用单引号用起来,数值类不需要

5.查询函数
Select 函数名(实例参数);

6.查询表达式
Select 100*10;

7.取别名(两种方式)
As
Select last_name as 姓名 from 表名

空格
Select last_name 姓名 from 表名

8.去重
Select distinct 字段名 from 表名

9.+
作用:加法运算
Select 数值+数值;直接运算
Select 字符+数值,先将字符转成数值,如果转换成功,则继续运算,否则转换成0, 再做运算。

10.补充Concat函数
功能:拼接字符
Select concat(字符1,字符2,字符3,….)

11.补充ifnull函数
Select ifnull(name, 0) from user

12.补充isnull函数
Select isnull(name) from user
功能:拍的某个字段是否为null,如果是返回1,如果不是返回0

条件查询

语法

1
2
3
Select 查询列表  
From 表名
Where 筛选条件

筛选条件的分类

(1)简单条件运算符

> < = <> != >= <= <=>

Select * from user where age>15;

Select * from user where age<15;

Select * from user where age<>15;

<=>:安全等于,可以判断普通数值,也可以判断是否为null
Select * from user where age<=> null;

(2)逻辑运算符

and or not

And:
SELECT last_name FROM employees WHERE salary>=10000 AND salary<=20000;

Or:
SELECT * FROM employees WHERE department_id<90 OR department_id>110 OR salary>15000;

Not:
SELECT * FROM employees WHERE NOT(department_id>=90 AND department_id<=110) OR salary>15000;

(3)模糊查询

Like, between and, in, is null, is not null

like:
Select * from user where name like’%a%’;
Select * from user where name like’_a%’;

between and:
作用:在哪两个数之间
Select * from user where age between 10 and 20;

in:
Select * from user where id in(1,2,3);

is null:
作用:判断是否为null
Select * from user where age is null;

is not null:
作用:判断是否不为空
Select * from user where age is not null;

排序查询

语法

Select * from 表名
[Where 筛选条件]
Order by 排序列表[asc desc]

特点

(1)asc代表升序,desc代表降序,如果不写是升序。
(2)order by子句可以支持单个字段,多个字段,表达式,函数,别名
(3)order by子句一般是放查询语句的最后面,limit子句除外

案例

#案例:查询员工信息,要求工资冲高到底排序

1
SELECT * FROM employees ORDER BY salary DESC;

#案例:按年薪的高低显示员工的信息和年薪【按表达式排序】

1
2
3
SELECT *, salary*12*(1+IFNULL(commission_pct, 0)) AS 年薪 
FROM employees
ORDER BY 年薪 DESC;

#案例:查询邮箱中包含e的员工信息,并先按邮箱的字节数降序,再按部门号升序

1
2
3
SELECT * FROM employees 
WHERE email LIKE '%e%'
ORDER BY LENGTH(email) DESC, department_id ASC;

常见函数

功能:类似java中的方法
好处:提高重用性和隐藏实现细节
调用:select 函数名(参数列表)

字符函数

LENGTH:
作用:获取参数的字节个数

1
2
SELECT LENGTH('oyr');  
SELECT LENGTH('欧阳荣');

CONCAT:
作用:拼接字符串

1
2
SELECT CONCAT('abc', 'jkl');  
SELECT CONCAT(first_name, last_name) out_put FROM employees;

UPPER:
作用:字符串变大写

1
SELECT UPPER("asc");

Lower:
作用:字符串变小写

1
SELECT LOWER("ASC");

Substr:
作用:截取字符串,两种使用方式

1
2
3
4
5
6
7
8
9
10
#第一种:截取从指定索引处后面所有字符  
SELECT SUBSTR("欧阳荣真的帅哦", 4);
结果:真的帅哦

#第二种:截取从指定索引处指定字符串的字符
SELECT SUBSTR("欧阳荣不是一般帅哦", 1, 3);
结果:欧阳荣

# 案例1:姓名首字母大写,其他字符小写然后用_拼接,显示出来
SELECT CONCAT(UPPER(SUBSTR(last_name, 1, 1)), '_', LOWER(SUBSTR(last_name, 2))) FROM employees;

Trim:
作用:去除两边空格或去除两边指定字符

1
2
3
4
5
6
7
#去除两边空格:
SELECT LENGTH(TRIM(' 欧阳荣 '));
结果:欧阳荣

#去除两边指定字符:
SELECT TRIM('a' FROM 'aaaaaaaa欧阳aaa荣aaaaaa');
结果:欧阳aaa荣

Lpad:
作用:lpad 用指定的字符实现左填充指定长度

1
SELECT LPAD('欧阳荣', 10, 'a') out_put;

Rpad:
作用:用指定的字符实现右填充指定长度

1
SELECT LPAD('欧阳荣', 12, 'ab') out_put;

Replace:
作用:替换字符串

1
SELECT REPLACE('赵吊彬是zz赵吊彬赵吊彬赵吊彬', '赵吊彬', '李执志');

Instr:
作用:获取子串第一次出现的索引

1
2
SELECT INSTR('欧阳荣多对多', '欧阳');
结果为:1

数学函数

Round:
作用:四舍五入

1
2
3
4
5
6
7
第一种使用:
SELECT ROUND(1.65);
结果:2

第二种使用:
SELECT ROUND(1.657, 2);
结果:1.66

Ceil:
作用:向上取整

1
2
SELECT CEIL(1.52);  
结果:2

Floor:
作用:向下取整

1
2
SELECT FLOOR(9.99);
结果:9

Truncate:
作用:截断

1
2
SELECT TRUNCATE(10.19, 1);  
结果:10.1

Mod:
作用:取余

1
2
SELECT MOD(10, 3);  
结果:1

Rand:
作用:获取随机数,返回0-1之间的小数

1
SELECT RAND();

日期函数

NOW:
作用:返回当前系统日期+时间
SELECT NOW();

Curdate:
作用:返回当前系统日期,不包含时间
SELECT CURDATE();

Curtime:
作用:返回当前时间,不包含日期
SELECT CURTIME();

Year:
作用:获取年
Select year(now());
SELECT YEAR(‘1999-1-1’) 年;

Month:
作用:获取月
SELECT MONTH(NOW()) 月;

Day:
作用:获取日
Select DAY(now()) 日;

日期格式符:

Str_to_date:
作用:将日期格式的字符转换成指定格式的日期
SELECT STR_TO_DATE(‘1999-6-2’, ‘%Y-%c-%d’) out_put;
SELECT * FROM employees WHERE hiredate=STR_TO_DATE(‘4-3 1992’, ‘%c-%d %Y’);

Date_format:
作用:将日期转换成字符
SELECT DATE_FORMAT(NOW(), ‘%y年%m月%d日’) AS out_put;

案例:查询有奖金的员工名和入职日期(xx月/xx日 xx年)
SELECT last_name, DATE_FORMAT(hiredate, ‘%M月/%d日 %y年’)
FROM employees
WHERE commission_pct IS NOT NULL;

Datediff:
作用:返回两个日期相差的天数
SELECT DATEDIFF(NOW(), ‘1999-06-24’);

其他函数

VERSION:
作用:查看当前mysql版本号
SELECT VERSION();

DATABASE:
作用:查看当前打开的数据库
Select DATABASE();

USER:
作用:查看当前登录的用户
Select USER();

流程控制函数

If:
作用:判断真假
SELECT IF(10<5, ‘小’, ‘大’);

Case:
作用:判断
语法:
case 要带的字段或表达式
when 常量1 then 要显示的值1或语句1;
when 常量1 then 要显示的值1或语句1;
…..
else 要显示的值n或语句n
End

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,显示的工资为1.1
部门号=40,显示的工资为1.2
部门号=50,显示的工资为1.3
其他部门,显示的工资为原工资

SELECT last_name, salary 原工资,
CASE department_id
WHEN 30 THEN salary*1.1
WHEN 40 THEN salary*1.2
WHEN 50 THEN salary*1.3
ELSE salary
END AS 新工资
FROM employees;

案例:查询员工的工资的情况
如果工资>20000,显示A级别
如果工资>15000,显示B级别
如果工资>10000,显示C级别
否则,显示D级别

SELECT last_name,salary,
CASE
WHEN salary>20000 THEN 'A'
WHEN salary>15000 THEN 'B'
WHEN salary>10000 THEN 'C'
ELSE 'D'
END AS 级别
FROM employees;

分组

分组函数

sum:
作用:求和
SELECT SUM(salary) FROM employees;

max:
作用:最大值
Select max(salary) from employees;

min:
作用:最小值
SELECT MIN(salary) FROM employees;

avg:
作用:平均数
SELECT AVG(salary) FROM employees;

count:
作用:计算个数
Select count(*) from user;

注意事项:
sum,avg一般用于数值型
max,min,count可以处理任何类型
以上分组函数都忽略null值

分组查询

语法:
select 分组函数,列(要求出现在group by的后面)
from 表
[where 筛选条件]
group by 分组的列表
[having 分组后的筛选]
[order by 子句]

注意:
查询列表必须特殊,要求是分组函数或group by后出现的字段
分组后筛选在group by子句的后面使用 having 关键字
分组函数做条件肯定是放在having子句中
能用分组前筛选,就优先使用分组前筛选
group by 子句支持单个字段分组,多个字段分组(用逗号隔开)

简单分组:

1
2
3
4
5
6
7
8
#案例1:查询每个部门的平均工资
SELECT department_id,AVG(salary) 平均工资 FROM employees GROUP BY department_id;

#案例2:查询每个工种的最高工资
SELECT job_id, MAX(salary) FROM employees GROUP BY job_id;

#案例3:查询每个位置上的部门个数
SELECT location_id, COUNT(*) FROM departments GROUP BY location_id;

添加筛选条件:

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
#案例1:查询邮箱中包含a字符的,每个部门的平均工资
SELECT department_id, AVG(salary)
FROM employees
WHERE email LIKE '%a%'
GROUP BY department_id;

#案例2:查询有奖金的每个领导手下的最高工资
SELECT manager_id, MAX(salary)
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY manager_id;

#案例3:查询哪个部门的员工个数>2
Select department_id, count(*)
from employees
group by department_id having count(*)>2;

#案例4:查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
SELECT job_id, MAX(salary)
FROM employees
GROUP BY job_id HAVING MAX(salary)>12000;

#案例5:查询领导编号>102的每个领导手下的最低工资>5000的领导编号是哪个
SELECT manager_id, MIN(salary)
FROM employees
WHERE manager_id>102
GROUP BY manager_id HAVING MIN(salary)>5000;

#案例6:按员工姓名长度分组,查询每一组的员工个数,筛选员工个数>5的有哪些
SELECT last_name, LENGTH(last_name), COUNT(*) 员工个数
FROM employees
GROUP BY LENGTH(last_name) HAVING COUNT(*)>5;

连接查询

什么是连接查询:
又称多表查询,当查询的字段来自多个表时,就会用到连接查询。

笛卡尔积:
笛卡尔积乘积现象:表1 有m行,表2有n行,结果=m*n行

分类

按年代分类:
sql92标准:仅仅支持内连接
sql99标准【推荐】:支持内连接+外连接(左外和右外)+交叉连接

按功能分类:

  • 内连接:
    • 等值连接
    • 非等值连接
    • 自连接
  • 外连接:
    • 左外连接
    • 右外连接
    • 全外连接(mysql不支持)
  • 交叉连接:

语法

Select 查询列表
From 表1 别名 【连接类型】
Join 表2 别名
On 连接条件
【where 筛选条件】
【group by 分组】
【having 分组后筛选】
【order by 排序列表】

内连接

Sql192语法:

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
(1)等值连接
案例:
查询女神名和对应男生名
SELECT b.name,bs.boyName
FROM beauty b, boys bs
WHERE b.boyfriend_id = bs.id;

查询员工名和对应的部门名
SELECT e.last_name, e.department_id, d.department_name
FROM employees e, departments d
WHERE e.department_id=d.department_id;

查询有奖金的员工名,部门名
SELECT e.`last_name`, d.`department_id`, e.`commission_pct`
FROM employees e,departments d
WHERE e.`department_id`=d.`department_id` AND commission_pct IS NOT NULL;

查询每个工种的工种名和员工的个数,并且按员工个数降序
SELECT j.job_title, COUNT(*)
FROM employees e, jobs j
WHERE e.`job_id`=j.`job_id`
GROUP BY j.`job_title` ORDER BY COUNT(*) DESC;

(2)非等值连接
案例:
查询员工的工资和工资级别
SELECT e.`salary`, j.`grade_level`
FROM employees e, job_grades j
WHERE e.`salary` BETWEEN j.`lowest_sal` AND j.`highest_sal` AND j.`grade_level`='E';

(3)自连接
查询员工名和上级的名称
SELECT e1.last_name, e2.`last_name`
FROM employees e1, employees e2
WHERE e1.`manager_id`=e2.`employee_id`;

三表连接:查询员工名,部门名和所在的城市
SELECT e.last_name, d.`department_name`, l.`city`
FROM employees e, departments d, locations l
WHERE e.`department_id`=d.`department_id` AND d.`location_id`=l.`location_id`;

sql99语法:Inner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
查询员工名,部门名
SELECT e.last_name, d.`department_name` FROM employees e
INNER JOIN departments d ON e.`department_id`=d.`department_id`;

查询名字中只能包含e的员工名和工种名
SELECT e.last_name, j.`job_title` FROM employees e
INNER JOIN jobs j ON e.`job_id`=j.`job_id`
WHERE e.`last_name` LIKE '%e%';

查询部门个数>3的城市名和部门个数
SELECT l.`city`, COUNT(*) FROM locations l
INNER JOIN departments d ON l.`location_id` = d.`location_id`
GROUP BY l.`city` HAVING COUNT(*)>3;

查询工资级别的个数>2的个数,并按工资级别降序
SELECT j.`grade_level`, COUNT(*) 个数 FROM job_grades j
INNER JOIN employees e ON e.`salary` BETWEEN j.`lowest_sal` AND j.`highest_sal`
GROUP BY j.`grade_level` HAVING 个数>2
ORDER BY 个数 DESC;

外连接

应用场景:用于查询一个表中有,另一个表没有

1.外连接的查询结构为主表中的所有记录
如果从表中有和它匹配的,则显示匹配值
如果从表中没有和它匹配的,则显示为null
外连接查询结果=内连接结果+主表中有而从表没有的数据

2.左外连接,left join左边的是主表
右外连接,right join右边的是主表

3.左外和右外交换两个表的顺序,可以实现同样的效果

(1)左连接:
语法:Left 【outer】

1
2
3
4
5
6
7
8
9
10
查询没有男朋友的女神名
SELECT * FROM beauty b
LEFT JOIN boys bo ON b.`boyfriend_id`=bo.`id`
WHERE bo.`id` IS NULL;

查询哪个部门没有员工
SELECT d.`department_id`, d.`department_name` FROM departments d
LEFT JOIN employees e ON d.`department_id`=e.`department_id`
WHERE e.`employee_id` IS NULL
GROUP BY d.`department_id`;

(2)右连接
语法:Right 【outer】

1
2
3
4
5
6
7
8
9
10
#查询没有男朋友的女神名
SELECT * FROM boys bo
RIGHT JOIN beauty b ON b.`boyfriend_id`=bo.`id`
WHERE bo.`id` IS NULL;

查询哪个部门没有员工
SELECT d.`department_id`, d.`department_name` FROM employees e
RIGHT JOIN departments d ON e.`department_id`=d.`department_id`
WHERE e.`employee_id` IS NULL
GROUP BY d.`department_id`;

(3)全外(mysql不支持)
语法:Full 【outer】

交叉连接

语法:Cross

1
2
3
女神男神表交叉连接
SELECT b.*, bo.* FROM beauty b
CROSS JOIN boys bo;

子查询

含义

出现在其他语句的select语句,称为子查询或内出现
内部嵌套其他select语句的查询,称为外查询或主查询

分类:

按子查询出现的位置:
1)select 后面
2)from 后面
3)wehre 或 having后面
4)exists后面(相关子查询)

按结果集的行列数不同:
1)标量子查询(结果集只有一行一列)
2)列子查询(结果集只有一列多行)
3)行子查询(结果集有多行多列)
4)表子查询(结果集一般为多行多列)

用法

> < <> =

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

谁的工资比 Abel 高?
SELECT last_name, salary
FROM employees WHERE salary>(
SELECT salary FROM employees WHERE last_name='Abel'
);

返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资
SELECT last_name,job_id,salary FROM employees
WHERE job_id =(
SELECT job_id FROM employees WHERE employee_id=141
) AND salary>(
SELECT salary FROM employees WHERE employee_id=141
);

案例3:返回公司工资最少的员工的last_name,job_id和salary
SELECT last_name, job_id, salary FROM employees
WHERE salary = (
SELECT MIN(salary) FROM employees
);

In(常用):
作用:可以是子查询中的任一一个
返回location_id是14001700的部们中的所有员工姓名
SELECT last_name
FROM employees
WHERE department_id IN (
SELECT department_id FROM departments WHERE location_id IN(1400, 1700)
);

Any(不常用):
作用:任一一个

返回其他部门中比job_id为‘IT_PROG’部门任一工资低的员工的员工号,姓名,job_id,以及salasy
SELECT employee_id, last_name, job_id,salary FROM employees
WHERE salary<ANY(
SELECT DISTINCT salary FROM employees WHERE job_id='IT_PROG'
) AND department_id<>'IT_PROG';

All(不常用):
作用:所有的

返回其他部门中比job_id为‘IT_PROG’部门所有工资低的员工的员工号,姓名,job_id,以及salasy
SELECT employee_id, last_name, job_id,salary FROM employees
WHERE salary<ALL(
SELECT DISTINCT salary FROM employees WHERE job_id='IT_PROG'
) AND department_id<>'IT_PROG';

分页查询(常用)

应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求。

语法:

select 参数列表
from 表
left join 表名 on 连接条件
where 筛选条件
group by 分组字段 having 分组后筛选
order by 排序字段
limit 【offset,】 size;

offset 要显示条目的起始索引(起始索引从0开始)
size 要显示的条目个数
特点:limit语句放在查询语句的最后

案例:

查询第二页,一页10条记录
Select * from user limit 10, 10;

Union联合查询

union 联合 合并:将多条查询语句的结果合并成一个结果

应用场景:要查询的结果来自于多个表,且多个表没有证据的连接关系,单查询的信息一致时

语法

查询语句1
union
查询语句2
union
….

特点:
1.要求多条查询语句的查询列数是一致的
2.要求多条查询语句的查询的每一列的类型和顺序最好一致
3.union关键字默认去重,如果使用union all 可以包含重复项

案例

1
2
3
4
5
6
7
8
9
--Union
Select id, name from user
Union
Select id,name from emp;

--Union all
Select id, name from user
Union all
Select id,name from emp;

DML语言

数据库操作语言:
插入:insert
修改:update
删除:delete

插入语句

方式1

语法:
Inser into 表名(列名, …) values(值1, …);

使用:
INSERT INTO users VALUES(1, ‘欧阳荣’, ‘男’, 166842.5);
INSERT INTO users(id, NAME, sex, gz) VALUES(2, ‘赵云彬’, ‘欧阳荣’, NULL);

方式2

语法:
insert into 表名 set 列名=值,列名=值,….;

使用:
INSERT INTO boys SET id=6, boyName=’侄子’, userCp=147258;

两种开发方式pk

insert方式一次可以加多条,第二种方式不行
INSERT INTO boys VALUES(7, ‘欧阳荣’, 10000000), (8, ‘欧阳荣’, 10000000);

insert方式可以用子查询
INSERT INTO boys SELECT 10, ‘ii’, 150044;

修改语句

修改单表的记录(常用)

语法:
Update 表名 set 列=新值, 列=新值…. where 筛选条件;

案例:
修改beauty表中姓唐的女神电话为1389988889
UPDATE beauty SET phone=’1389988889’ WHERE NAME LIKE ‘唐%’;

修改boys表中id号为2的名称为张飞,魅力值10
UPDATE boys SET boyName=’张飞’, userCP=10 WHERE id=2;

4.2.2.修改多表的记录(补充)
192语法:
UPDATE 表名 别名,表名 别名
SET 列=值,列=值 …
WHERE 连接添加 AND 筛选条件

199语法:
UPDATE 表名 别名
INNER|LEFT|RIGHT| JOIN 表名 别名 ON 连接条件
SET 列=值,列=值 …
WHERE 筛选条件

案例(以下实现都为199语法):

修改张无忌的女朋友的手机号为114
UPDATE boys bo
INNER JOIN beauty b ON bo.id=b.boyfriend_id
SET phone=’114’
WHERE bo.boyName=’张无忌’;

修改没有男朋友的女神的男朋友的编号都为2号
UPDATE boys bo
RIGHT JOIN beauty b ON bo.id=b.boyfriend_id
SET b.boyfriend_id=2
WHERE bo.id IS NULL;

4.3.删除语句

4.3.1.方式1:delete
语法:
(1)单表删除(重要):
Delete from 表名 where 筛选条件;

(2)多表删除(补充)
192:
Delete 表1别名, 表2别名
from 表一 别名, 表二别名
Where 连接条件 and 筛选条件

199:
Delete 表1别名, 表2别名
From 表一 别名
inner|Left|right| join 表2 别名 on 连接条件
Where 筛选条件

案例:

删除手机号以9结尾的女神信息
DELETE FROM beauty WHERE phone LIKE ‘%9’;

删除张无忌的女朋友的信息
DELETE b FROM beauty b INNER JOIN boys bo ON bo.id=b.boyfriend_id
WHERE bo.boyName=’张无忌’;

删除黄晓明的信息以及他女朋友的信息
DELETE b, bo FROM boys bo
INNER JOIN beauty b ON bo.id=b.boyfriend_id
WHERE bo.boyName=’黄晓明’;

4.3.2.方式2:truncate
语法:
Truncate table 表名;

特点:
不能加筛选条件,也叫做清空。

4.3.3.delete pk truncate(面试题)

1.delete可以加where条件,truncate不可以
2.truncate删除,效率高一丢丢
3.假如删除的表中有自增长,使用delete删除后,再插入数据,自增长列的值从断点开始。
如果使用truncate删除后,再插入值,自增长列的值从1开始。
4.truncate删除没有返回值,delete删除有返回值
5.truncate删除不能回滚,delete删除可以回滚。

5.DDL语言
数据库定义语言,也就是库和表的管理

5.1.库的管理

5.1.1.库的创建

语法:
create database [if not exists] 库名 [character set 字符集]

案例:

创建一个db_book库
CREATE DATABASE IF NOT EXISTS db_oyr CHARACTER SET utf8;

5.1.2.库的修改

修改库名(现在不能用了):
语法:
rename database 库名 to 新库名;

案例
把db_oyr库名改成my_oyr
RENAME DATABASE db_oyr TO my_oyr;

修改字符集:
语法:
alert database 库名 character set 字符集;

案例:

修改db_oyr的字符集为gbk
ALTER DATABASE db_oyr CHARACTER SET gbk;

5.1.3.库的删除

语法:
DROP DATABASE [IF EXISTS] 库名;

案例:
删除db_oyr
DROP DATABASE db_oyr;

5.2.表的管理

5.2.1.表的创建

语法:
create table 表名(
列名 列类型【(类型长度) 约束】,
列名 列类型【(类型长度) 约束】,
列名 列类型【(类型长度) 约束】,

列名 列类型【(类型长度) 约束】
);

案例:
创建book表
CREATE TABLE book(
id INT,#编号
bName VARCHAR(20),#图书名
price DOUBLE,#价格
authorId INT,#作者编号
publishDate DATETIME#出版日期
);

5.2.2.表的修改

语法:
Alter table 表名 add|drop|modify|change column 列名 【列类型 约束】

(1)修改列名

ALTER TABLE book CHANGE COLUMN bName myName VARCHAR(25);

(2)修改列的类型或约束

ALTER TABLE my_book MODIFY COLUMN myName INT;

(3)添加新列

ALTER TABLE book ADD COLUMN oo VARCHAR(20);

(4)删除列

ALTER TABLE book DROP COLUMN oo;

(5)修改表名

ALTER TABLE book RENAME TO my_book;

5.2.3.表的删除

语法:
DROP TABLE 【IF EXISTS】 表名;

案例:

删除my_book表
DROP TABLE IF EXISTS my_book;

5.2.4.表的复制
1)复制表的结构
语法:
Create table 新表名 like 被复制的表名;

复制boys的结构
CREATE TABLE copy LIKE boys;

2)复制表的结构和数据
语法:
Create table 新表名
Select * from 被复制表的表名

复制boys表的结构和数据
CREATE TABLE copy2
SELECT * FROM boys;

复制boys部门数据和部分列
CREATE TABLE copy3
SELECT id,boyName FROM boys WHERE id<5;
SELECT * FROM copy3;

复制几个字段的结构
CREATE TABLE copy4
SELECT id, boyName FROM boys
WHERE 1=2;

5.3.常见的约束

5.3.1.约束介绍

六大约束:
NOT NULL:非空约束,用于保证该字段的值不能为空
UNIQUE:唯一约束,用于保证该字段的值有唯一性,可以为空
PRIMARY KEY:主键约束,用于保证该字段的值有唯一性,并且不能为空
CHEACK:检查约束【MYSQL中不支持,没有效果】
FOREIGN KEY:外检约束,用于限制两个表的关系,用于保证该字段的值必须来自于主表的关联列的值,用于引用主表中某列的值
DEFAULT:默认值,用于保证字段有默认值

添加约束的时机:
创建表时
修改表时

约束的添加分类:
列级约束:
六大约束语法上都支持,单外检约束没有效果
表级约束:
除了非空,默认,其他都支持

列级约束 VS 表级约束
位置 支持的约束类型 是否可以起别名
列级: 列的后面 都支持,但外键没有效果 不可以
表级: 所有列的下面 默认和非空不支持,其他都支持 可以主键(没有效果)

5.3.2.添加列级约束

新建一个库students,在里面测试添加列级约束

新建主修表

create table major(
id bigint(20) primary key, #主键约束
major_name varchar(25) NOT NULL #非空约束
)

新建学生表

create table student(
id bigint(20) primary key, #主键约束
stu_name varchar(25) NOT NULL, #非空约束
sex char(2) CHECK(sex = ‘男’ or sex = ‘女’), #检查约束
age int default 20, #默认约束
card varchar(25) unique,#唯一约束
major_id bigint(20) REFERENCES major(id) #外键约束
)

经过测试后发现,检查约束是无效的,而且外检约束也是无效的。
其他都是ok的。

5.3.3.添加表级约束

语法:
在表字段的最下面
[constraint 约束名] 约束类型(字段名)
表级约束添加外键是有效的。

指定约束名创建约束:
CREATE TABLE student (
id bigint(20) NOT NULL,
stu_name varchar(25) NOT NULL,
sex char(2) DEFAULT NULL,
age int(11) DEFAULT ‘20’,
card varchar(25) DEFAULT NULL,
major_id bigint(20) DEFAULT NULL,
CONSTRAINT pk PRIMARY KEY(id), #主键,约束名不生效
CONSTRAINT un_card UNIQUE KEY(card), #唯一约束
CONSTRAINT ch_sex CHECK(sex = ‘女’ or sex =’男’),
CONSTRAINT pk_student_major FOREIGN KEY (major_id) REFERENCES major(id)
);

不指定约束名创建约束:
CREATE TABLE student (
id bigint(20) NOT NULL,
stu_name varchar(25) NOT NULL,
sex char(2) DEFAULT NULL,
age int(11) DEFAULT ‘20’,
card varchar(25) DEFAULT NULL,
major_id bigint(20) DEFAULT NULL,
PRIMARY KEY(id), #主键,约束名不生效
UNIQUE KEY(card), #唯一约束
CHECK(sex = ‘女’ or sex =’男’),
FOREIGN KEY (major_id) REFERENCES major(id)
);

5.3.4.主键约束 VS 唯一约束

保证唯一性 是否允许为空 一个表中有几个 是否允许出现组合
主键 √ × 最多一个 √,但不推荐
唯一 √ × 可以多个 √,但不推荐

唯一约束可以允许为空,但null只能出现一次。
组合就是组合主键或组合唯一约束。

5.3.5.外键的特点

从表:当前表
主表:被关联的表

1.要求在从表中设置外键关系
2.从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求
3.主表的关联列必须是一个key(主键和唯一,不然会抛出异常)
4.插入数据时,先插入主表,再插入从表,删除数据时,先删除从表,再删除主表

5.3.6.修改表时添加约束

语法:
1.添加列级约束
alter table 表名 modify column 字段名 字段类型 新约束

2.添加表级约束
alter table 表名 add [constraint 约束名] 约束类型(字段名) [外键的引用]

添加非空约束:
列级:
alter table student modify column stu_name varchar(25) not null;

添加默认约束:
列级:
alter table student modify column age int default 20

添加主键约束:
列级:
alter table student modify column id bigint(20) primary key
表级:
alter table student add primary key(id)

添加唯一约束:
列级:
alter table student modify column card varchar(25) unique
表级:
alter table student add constraint un_card unique(card)

添加外键约束:
表级:
alter table student add constraint fk_student_major foreign key(id) references major(id)

5.3.7.修改表时删除约束

1.删除非空约束(直接不写就是删除)
alter table student modify column stu_name varchar(25)

2.删除默认约束(不写即是删除)
alter table student modify column age int default 20

3.删除主键约束
aler table 表名 drop primary key

4.删除唯一约束
alter table 表名 drop index 约束名

5.删除外键约束
alter table 表名 drop foreign key 约束名

5.4.标识列

什么是标识列?
标识列是自增长列
含义:可以不手动的插入值,系统提供默认的序列值

特点:
1.标识列必须和key值搭配(也就是说当前列必须是主键或者唯一)
2.一个表只能有一个标识列
3.标识类的类型只能是数值型
4.标识列可以通过 set auto_increment_increment=3;设置步长。可以手动插入值来设置起始值

创建表时设置标识列
create table tb_user(
id int primary key auto_increment, #主键并且设置标识列
name varchar(25) not null
)

修改表时设置标识列
alter table tb_user modify column id int primary key auto_increment

修改表时删除标识列
alter table tb_user modify column id int primary key

6.TCL 语言
TCL:Transaction Control Language 事物控制语言

事务:事务由单独单元的一个或多个SQL语句组成,在这个单元中,每个MySQL语句是相互依赖的。而整个单独单元作为一个不可分割的整体,如果单元中某条SQL语句一旦执行失败或产生错误,整个单元将会回滚。所有受到影响的数据将返回到事物开始以前的状态;如果单元中的所有SQL语句均执行成功,则事物被顺利执行。

6.1.MySQL 中的存储引擎

1、概念:在mysql中的数据用各种不同的技术存储在文件(或内存)中。
2、通过show engines;来查看mysql支持的存储引擎。
3、 在mysql中用的最多的存储引擎有:innodb,myisam ,memory 等。其中innodb支持事务,而myisam、memory等不支持事务

6.2.事务的ACID属性

  1. 原子性(Atomicity)
    原子性是指事务是一个不可分割的工作单位,事务中的操作要么执行成功,要么都执行失败。

  2. 一致性(Consistency)
    事务必须使数据库从一个一致性状态变换到另外一个一致性状态。拿转账为例,A有500元,B有300元,如果在一个事务里A成功转给B50元,那么不管并发多少,不管发生什么,只要事务执行成功了,那么最后A账户一定是450元,B账户一定是350元。不管转账多少次两个人的钱加起来都是800元。

  3. 隔离性(Isolation)
    并发执行的各个事务之间不能互相干扰。事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的。

  4. 持久性(Durability)
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。

6.3.事物的使用步骤

事物又分两种:隐式事物和显式事物
隐式事物:事物没有明显的开启和结束的标记
列如insert,update,delete

显式事物:事物具有明显的开启和结束的标记
前提:必须设置自动提交功能为禁用

查看当前自动提交功能开关
show variables like ‘autocommit’

关闭自动提交功能,并不是永久的,只是当前回话被关闭
set autocommit=0;

开启事物的语法:
#步骤1:开启事物
set autocommit=0;
start transaction;#开启事物,可选的
#步骤2:编写事物中的sql语句(select insert update delete)
语句1;
语句2;
…….
#步骤3:结束事物
commit;#提交事物
rollback;回滚事物

实战操作:
初始化
create table account(
id bigint(20) primary key auto_increment,
acc_name varchar(25),
money double
)
INSERT INTO account (id, acc_name, money) VALUES (1, ‘欧阳荣’, 1500);
INSERT INTO account (id, acc_name, money) VALUES (2, ‘罗总’, 500);

转账成功提交:执行后可以看到数据库数据已经改变了
set autocommit=0;
start transaction;
update account set money = money-500 where acc_name=’欧阳荣’;
update account set money = money+500 where acc_name=’罗总’;
commit;

转账失败回滚:执行后会发现数据并没有改变
set autocommit=0;
start transaction;
update account set money = money-500 where acc_name=’欧阳荣’;
update account set money = money+500 where acc_name=’罗总’;
rollback;

6.4.数据库的隔离级别

6.4.1.并发问题

对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:

脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段.
之后, 若 T2 回滚, T1读取的内容就是临时且无效的.

不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段.
之后, T1再次读取同一个字段, 值就不同了.

幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插
入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行。

数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题。

一个事务与其他事务隔离的程度称为隔离级别. 数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。

6.4.2.四种事务隔离级别

Oracle支持2种事务隔离级别:READ COMMITED, SERIALIZABLE。Oracle 默认的事务隔离级别为: READ COMMITED。

Mysql支持4种事务隔离级别。Mysql 默认的事务隔离级别为: REPEATABLE READ。

6.5.回滚点使用(savepotion)

set autocommit=0;#设置不自动提交
start transaction;#开启事物
delete form account where id=1;
savepoint a;#保存点
delete from account where id=2;
rollback to a;#回滚到保存点
这上面的sql意思是只回滚到保存点,其实就是删除了id=1的数据,id=2的数据并没有被删除。

7.视图
7.1.什么是视图

含义:虚拟表,和普通表一样使用
MySQL从5.0.1版本开始提供视图功能。一种虚拟存在的表,行和列的数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的,只保存了sql逻辑,不保存查询结果。

应用场景:
1.多个地方用到同样的查询结果
2.该查询结果使用的sql语句较复杂

7.2.视图的创建

语法:
create [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
view 视图名 as
查询语句
[WITH [CASCADED | LOCAL] CHECK OPTION]

OR REPLACE:表示替换已有视图

ALGORITHM:表示视图选择算法,默认算法是UNDEFINED(未定义的):MySQL自动选择要使用的算法 ;merge合并;temptable临时表

[WITH [CASCADED | LOCAL] CHECK OPTION]:
表示视图在更新时保证在视图的权限范围之内
cascade是默认值,表示更新视图的时候,要满足视图和表的相关条件
local表示更新视图的时候,要满足该视图定义的一个条件即可
推荐使用WHIT [CASCADED|LOCAL] CHECK OPTION选项,可以保证数据的安全性 

实际操作:

1.查询邮箱中包含a字符的员工名,部门名,工种信息
– 创建视图
create view view_test1
AS
SELECT
CONCAT(e.first_name, e.last_name) 员工名,
dept.department_name 部门名,
j.job_title 工种信息
FROM
employees e
LEFT JOIN departments dept ON dept.department_id = e.department_id
LEFT JOIN jobs j ON j.job_id = e.job_id
where email LIKE ‘%a%’
– 查询视图
select * from view_test1

2.查询各部门的平均工资级别
create view view_test2
as
SELECT
dept.department_name dept_name,
AVG(e.salary) ag
FROM
departments dept
LEFT JOIN employees e ON dept.department_id = e.department_id
GROUP BY dept.department_name

SELECT
vt.dept_name,
jb.grade_level
FROM
view_test2 vt
LEFT JOIN job_grades jb ON vt.ag BETWEEN jb.lowest_sal
AND jb.highest_sal

3.查询平均工资最低的部门
create view view_test3
as
SELECT
dept.department_name dept_name,
AVG(e.salary) ag
FROM
departments dept
LEFT JOIN employees e ON dept.department_id = e.department_id
GROUP BY dept.department_name having ag is not null
order by ag asc limit 0, 1

4.查询平均工资最低的部门名和工资
create view view_test4
as
SELECT
dept.department_name dept_name,
AVG(e.salary) ag
FROM
departments dept
LEFT JOIN employees e ON dept.department_id = e.department_id
GROUP BY dept.department_name having ag is not null
order by ag asc limit 0, 1

视图的好处:
重用sql语句
简化复杂的sql操作,不必知道它的查询细节
保护数据,提高安全性

7.3.视图的修改

方式一:如果存在,则覆盖
create [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
view 视图名 as
查询语句
[WITH [CASCADED | LOCAL] CHECK OPTION]

方式二:指定修改视图
alter
[ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
[DEFINER = { user | CURRENT_USER }]
[SQL SECURITY { DEFINER | INVOKER }]
view 视图名
as
查询语句
[WITH [CASCADED | LOCAL] CHECK OPTION];

方式一实际操作:
create or replace view view_test4
as
select * from employees where employee_id > 105

方式二实际操作:
alter view view_test4
as
select * from employees where employee_id > 120

7.4.视图的删除

语法:
drop view 视图名,视图名,视图名……..

实际操作:
drop view view_test4, view_test3

7.5.查看视图
方式1:desc 视图名

方式2:show create view 视图名

方式1实际操作:
desc view_test2

可以看到实际上能看到的是当前视图可以查询出的字段信息

方式2实际操作:
show create view view_test2

可以看到的是当前拿到的是创建视图的逻辑sql。

7.6.视图VS表

创建语法的关键字 是否实际占用物理空间 使用
视图 create view 只是保存了逻辑sql 增删改查,一般不能增删改查

表 create table 保存了数据 增删改查

8.变量

8.1.系统变量

说明:变量由系统提供的,不是银行定义,属于服务器层面
系统变量又可以细分为全局变量和局部变量

使用的语法:
1.查看所有的系统变量
show global|[session] variables

2.查看满足条件的部分系统变量
show global | [session] variables like ‘%char%’;

3.查看指定的某个系统变量的值
select @@global | [session] .系统变量名

4.为某个系统变量赋值
方式一:
set global | [session] 系统变量名 = 值;

方式二:
Set @@global | [session] .系统变量名 = 值;

注意:如果是全局级别,则需要加global,如果是会话级别,则需要加session,如果不写,则默认是session级别

8.1.1.全局变量实际操作

作用域:服务器每次启动将为所有的全局变量赋初始值,针对所有会话(连接)

(1)查看所有的全局变量
show global variables;

(2)查看部分的全局变量
show global variables like ‘%char%’;

(3)查看指定的全局变量的值
select @@global.autocommit;

(4)为某个指定的全局变量赋值
set @@global.autocommit = 0;

8.1.2.局部变量实际操作

作用域:仅仅针对于当前会话(连接)有效

(1)查看所有的会话变量
show variables
show session variables

(2)查看部分的会话变量
show variables like ‘%char%’
show session variables like ‘%char%’

(3)指定查看某个会话变量
select @@character_set_client
select @@session.character_set_client

(4)为某个会话变量赋值
set autocommit=0
set @@session.autocommit=1

8.2.自定义变量

说明:变量是用户自定义的,不是由系统自动生成的。
自定义变量又可以细分成用户变量(当前会话有效),局部变量
使用步骤:
声明
赋值
使用(查看,比较,运算等)

8.2.1.用户变量

作用域:针对于电器干会话(连接)有效,同于会话变量的作用域
应用在任何地方,也就是begin end里面或begin end外面

使用语法:

(1)声明并初始化:
set @用户变量名=值 或
set @用户变量名:=值 或
select @用户变量名:=值

(2)赋值(更新用户变量的值)
方式一:通过set和select
set @变量名=值 或
set @变量名:=值 或
select @变量名:=值

方式二:通过select into
select 字段 INTO @变量名
from 表

(3)使用(查看用户变量的值)
select @用户变量名

实际操作:

声明并且初始化:

声明并且初始化

set @name=’欧阳荣’;
set @name:=10;
select @name:=’罗总’;

赋值:
#赋值
set @count = 10;
set @count := 15;
select @count := 20;
select count(*) into @count from account;

使用:
select @count;

8.2.2.局部变量

作用域:仅仅在定义它的begin end中有效,应用在begin end的第一句

(1)声明
declare 变量名 类型;
declare 变量名 类型 default 值;

(2)赋值
方式一:通过set和select
set 局部变量名=值 或
set 局部变量名:=值 或
select @局部变量名:=值

方式二:通过select into
select 字段 INTO 局部变量名
from 表

(3)使用
select 局部变量名

8.2.3.用户变量 VS 局部变量

作用域 定义和使用的位置 语法
用户变量 当前会话 会话中的任何地方 必须加@符,不限定类型
局部变量 begin end中 只能在begin end中,且为第一句 不用加@符,需要限定类型

9.存储过程和函数
存储过程和函数:类似于java中的方法
好处:
1、提高代码的重用性
2、简化操作

9.1.存储过程

含义:一组预先编译好的SQL语句的集合,理解成批处理语句
1、提高代码的重用性
2、简化操作
3、减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率

9.1.1.创建&调用&查看&删除语法

创建语法:
create procedure 存储过程名(参数列表)
begin
存储过程体(一组合法的SQL语句)
end

注意:
1.参数列表包含三部分
参数模式 参数名 参数类型
in student varchar(20)

参数模式:
in: 该参数可以作为输入,也就是该参数需要调用方法传入值
out: 该参数可以作为输出,也就是该参数可以作为返回值
inout: 该参数既可以作为输入也可以作为输出,也就是该参数即需要传入值,又可以返回值

2.如果存储过程体仅仅只有一句话,begin end可以省略。
存储过程的每条SQL语句的结尾要求必须加上分号。
存储过程的结尾可以使用delimiter重新设置
语法:
DELIMITER 结束标记
DELIMITER $

调用语法:
CALL 存储过程名(实参列表)

查看语法:
show create procedure 存储过程名;

删除语法:
drop procedure 存储过程名;

9.1.2.存储过程实战

(1)空参的存储过程:

创建存储过程

create procedure myp1()
begin
insert into admin(name, money) values(‘z1’, 11), (‘z2’, 22), (‘z3’, 33), (‘z4’, 44), (‘z5’, 55);
end;

#调用存储过程
CALL myp1();

带in模式参数的存储过程:
根据女神名获取男神信息

创建存储过程

create procedure myp1(in beauty_name varchar(25))
begin
select bs.* from beauty b right join boys bs on b.boyfriend_id = bs.id where b.name = beauty_name;
end;

调用

call myp1(‘赵敏’)

根据用户名和密码判断是否能登录成功
– 创建存储过程
create procedure myp2(in username varchar(25), in password varchar(25))
begin
declare result int default ‘0’;
select count(*) INTO result from admin a where a.username = username and a.password = password;
select if(result > 0, ‘成功’, ‘失败’);
end;
– 调用
call myp2(‘john’, ‘8888’)

(2)带out模式的存储过程:

根据女神名,返回对应的男神名
#创建
create procedure myp3(in beauty_name varchar(25), out boy_name varchar(25))
begin
select bs.boyName into boy_name from beauty b right join boys bs on b.boyfriend_id = bs.id where b.name = beauty_name;
end;
– 调用
set @boy_name=’’;
call myp3(‘赵敏’, @boy_name);
select @boy_name;

根据女神名,返回对应的男神名和男神魅力值
创建
create procedure myp4(in beauty_name varchar(25), out boy_name varchar(25), out user_cp int)
begin
select bs.boyName, bs.userCP into boy_name,user_cp from beauty b right join boys bs on b.boyfriend_id = bs.id where b.name = beauty_name;
end;

– 调用
set @boy_name=’’;
set @user_cp=0;
call myp4(‘赵敏’, @boy_name, @user_cp);
select @boy_name, @user_cp;

(3)带inout模式参数的存储过程

传入a和b两个值,最终a和b都翻倍并返回
#创建
create procedure myp5(inout x int, inout y int)
begin
set x = x2;
set y = y
2;
end;
#调用
set @x = 10;
set @y = 20;
call myp5(@x, @y);
select @x, @y;

9.2.函数

含义:一组预先编译好的SQL语句的集合,理解成批处理语句
1、提高代码的重用性
2、简化操作
3、减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率

存储过程与函数的区别:
存储过程:可以有0个返回,也可以与多个返回,适合做批量插入,批量更新
函数:有且仅有一个返回,适合做处理数据后返回一个结果,适合做处理数据后返回一个结果

9.2.1.创建&调用&查看&删除语法

创建语法:
create function 函数名(参数列表) returns 返回类型
begin
函数体
end

注意:
1.参数列表包含两部分:参数名 参数类型
2.函数体:肯定会有return语句,如果没有会报错。
如果return语句没有放在函数体的最后也不报错,但不建议。
3.函数体重仅有一句话,则可以省略begin end
4.使用delimiter语句设置结束标记

调用语法:
select 函数名(参数列表)

查看语法:
show create function 函数名

删除语法:
dorp function 函数名

9.2.2.函数实战

(1)无参有返回

返回公司的员工个数:
#创建
create function myf1() returns int
begin
declare count int default 0;
select COUNT(*) into count from employees;
return count;
end;
#调用
select myf1();

(2)有参返回

根据员工名返回工资:
#创建
create function myf2(username varchar(25)) returns double
begin
declare money double default 0;
select salary into money from employees where last_name = username;
return money;
end;
#调用
select myf2(‘Kochhar’);

根据部门名,发挥该部门的平均工资
#创建
create function myf3(dept_name varchar(25)) returns double
begin
declare ave_salary double default 0;
select avg(e.salary) into ave_salary from departments dept left join employees e on dept.department_id = e.department_id
where dept.department_name = dept_name group by dept.department_id;
return ave_salary;
end;
#调用
select myf3(‘Adm’);

实现传入两个float,返回两者之和:

#创建
create function myf1(x float, y float) returns float
begin
declare sum float default 0;
set sum = x + y;
return sum;
end;
#调用
select myf1(1, 5.1);

(3)查看函数
show create function myf3;

(4)删除函数
drop function myf3

10.流程控制结构
顺序结构:程序从上往下依次执行
分支结构:程序从两条或多条路径中选择一条去执行
循环结构:程序在满足一定条件的基础上重复执行一段代码

10.1.分支结构

(1)if函数
功能:实现简单的双分支
语法:
If(表达式1, 表达式2, 表达式3)
执行顺序:
如果表达式1成立,则if函数返回表达式2的值,否则返回表达式3的值
应用:任何地方
实战操作:
select if(1 > 2, 1, 2)
当前sql执行后会返回2

(2)case结构

在begin end 外面:

情况1:类似于java中的switch语句,一般用于实现等值的判断
case 变量|表达式|字段
when 要判断的值 then 返回的值1
when 要判断的值 then 返回的值2
….
else 返回的值n
end

情况2:类似于java的多重IF语句,一般用于视区间判断
case
when 要判断的条件1 then 返回的值1
when 要判断的条件2 then 返回的值2
….
else 返回的值n
end

在begin end里面:

情况1:类似于java中的switch语句,一般用于实现等值的判断
case 变量|表达式|字段
when 要判断的值 then 要执行的语句1
when 要判断的值 then 要执行的语句2
….
else 要执行的语句n
end case;

情况2:类似于java的多重IF语句,一般用于视区间判断
case
when 要判断的条件1 then 要执行的语句1
when 要判断的条件2 then 要执行的语句2
….
else 要执行的语句n
end case;

实战操作:

创建存储过程,根据传入的成绩,来显示登记,比如传入的成绩:90-100显示A,80-90显示B,60-80,显示C,否则显示D

create procedure show_grade(in grade int)
begin
declare result varchar(2);
CASE
WHEN grade>=90 and grade<=100 THEN set result = ‘A’;
WHEN grade>=80 THEN set result = ‘B’;
WHEN grade>=60 THEN set result = ‘C’;
ELSE set result = ‘D’;
END CASE;
select result;
end;

CALL show_grade(99);

#根据传递的数据库类型,显示对应的数据库名
create procedure show_database_type(in val int)
begin
declare result varchar(20);
CASE val
WHEN 1 THEN set result = ‘mysql’;
WHEN 2 THEN set result = ‘oracle’;
WHEN 3 THEN set result = ‘sql server’;
ELSE set result = ‘不认识的类型,滚啊。。。’;
END CASE;
select result;
end;

call show_database_type(1);

(3)if结构
功能:实现多重分类
语法:
if 条件1 then 语法1;
esleif 条件2 then 语法2;

[else 语句n;]
end if;

应用场景:应用在begin end中

实战操作:

创建存储过程,根据传入的成绩,来显示登记,比如传入的成绩:90-100显示A,80-90显示B,60-80,显示C,否则显示D

create function show_grade(grade int) returns char(1)
begin
declare result char(1);
if grade>=90 and grade<=100 THEN set result = ‘A’;
elseif grade>=80 THEN set result = ‘B’;
elseif grade>=60 THEN set result = ‘C’;
else set result = ‘D’;
end if;
return result;
end;

select show_grade(55);

#批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止
create procedure pro_while_insert2(in count int)
begin
declare i int default 1;
a:while i<=count do
insert into admin(username, password) values(CONCAT(‘user’, i), CONCAT(‘psw’, i));
if i>=20 then leave a;
end if;
set i=i+1;
end while a;
end;

call pro_while_insert2(30);

10.2.循环结构

循环分类:
while、 loop、repeat

循环控制:
iterate类似于continue,结束本次循环,继续下一次循环
leave类似于break,结束当前所在循环。

三种循环语法:

(1)while
[标签:] while 循环条件 do
循环体
end while [标签]

(2)loop
[标签:] loop
循环体
end loop [标签]

(3)repeat
[标签:] repeat
循环体
until 结束循环的条件
end repeat [标签]

循环实战操作:

#批量插入,根据次数插入到amdin表中多条数据
create procedure pro_while_insert(in count int)
begin
declare i int default 1;
while i<=count do
insert into admin(username, password) values(CONCAT(‘user’, i), CONCAT(‘psw’, i));
set i=i+1;
end while;
end;

call pro_while_insert(10);

while VS repeat VS loop
while:先判断后执行
repeat:先执行后判断
loop:没有条件的死循环

博客园所学

索引

索引分为单列索引(主键索引,唯一索引,普通索引)和组合索引.
单列索引:一个索引只包含一个列,一个表可以有多个单列索引.
组合索引:一个组合索引包含两个或两个以上的列。

索引的创建

1)单列索引

普通索引:
第一种方式 :
CREATE INDEX 索引名ON 表名(字段名(length))

第二种方式: 
ALTER TABLE award ADD INDEX account_Index(account)

唯一索引:
CREATE UNIQUE INDEX IndexName ON TableName(字段名(length));
ALTER TABLE TableName ADD UNIQUE (column_list)

主键索引:不允许有空值

2)组合索引:

语法:
CREATE INDEX IndexName On TableName(字段名(length),字段名(length),…);

索引的删除

语法:
DORP INDEX IndexName ON TableName

触发器

触发器:监视某种情况,并触发某种操作。

创建语法

触发器创建语法四要素:1.监视地点(table)
           2.监视事件(insert/update/delete)
           3.触发时间(after/before)
           4.触发事件(insert/update/delete)

语法:
create trigger triggerName after/before insert/update/delete
on 表名 for each row #这句话是固定的
 begin
     #需要执行的sql语句
 end
注意1:after/before: 只能选一个 ,afte表示后置触发, before表示前置触发
注意2:insert/update/delete:只能选一个

创建一个视图:
create trigger tag1 after insert on order_table
for each row
begin
update goods set num=num-3 where id=1;
end;

我们如何在触发器引用行的值,也就是说我们要得到我们新插入的订单记录中的gid或much的值。
对于insert而言,新插入的行用new来表示,行中的每一列的值用new.列名来表示。
所以现在我们可以这样来改我们的触发器:
create trigger tg1 after insert on order_table
for each row
BEGIN
update goods set num = num-new.much where id=new.gid;
END;

当用户撤销一个订单的时候,我们这边直接删除一个订单,我们是不是需要把对应的商品数量再加回去呢?
 对于delete而言:原本有一行,后来被删除,想引用被删除的这一行,用old来表示旧表中的值,old.列名可以引用原(旧)表中的值。
create trigger tg2 after delete on order_table
for each ROW
BEGIN
update goods set num = num+old.much where id=old.gid;
END;

删除触发器

语法:drop trigger 触发器名称;
使用:drop trigger dg1;

ActiveMQ基础

简介

ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。
ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现。
尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。

特点

1)支持来自Java,C,C ++,C#,Ruby,Perl,Python,PHP的各种跨语言客户端和协议
2)完全支持JMS客户端和Message Broker中的企业集成模式
3)支持许多高级功能,如消息组,虚拟目标,通配符和复合目标
4)完全支持JMS 1.1和J2EE 1.4,支持瞬态,持久,事务和XA消息
5)Spring支持,以便ActiveMQ可以轻松嵌入到Spring应用程序中,并使用Spring的XML配置机制进行配置
6)专为高性能集群,客户端 - 服务器,基于对等的通信而设计
7)CXF和Axis支持,以便ActiveMQ可以轻松地放入这些Web服务堆栈中以提供可靠的消息传递
8)可以用作内存JMS提供程序,非常适合单元测试JMS
9)支持可插拔传输协议,例如in-VM,TCP,SSL,NIO,UDP,多播,JGroups和JXTA传输
10)使用JDBC和高性能日志支持非常快速的持久性

ActiveMQ的安装

ActiveMQ的消息形式

对于消息的传递有两种类型:
1)点对点(p2p),即一个生产者和一个消费者一一对应;
2)发布/订阅,即一个生产者产生消息并进行发送后,可以由多个消费者进行接收;
如图所示:
ActiveMQ的消息类型

JMS定义了五种不同的消息正文格式,以及调用的消息类型,允许你发送并接收以一些不同形式的数据,提供现有消息格式的一些级别的兼容性。

  • StreamMessage – Java原始值的数据流
  • MapMessage – 一套名称-值对
  • TextMessage – 一个字符串对象
  • ObjectMessage – 一个序列化的 Java对象
  • BytesMessage – 一个字节的数据流

ActiveMQ的使用

导入maven依赖:

1
2
3
4
5
6
<!-- activemq依赖的jar -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.11.2</version>
</dependency>

Queue

Producer

生产者:发布消息。

使用步骤:
第一步:创建ConnectionFactory对象,需要指定服务端ip及端口号。
第二步:使用ConnectionFactory对象创建一个Connection对象。
第三步:开启连接,调用Connection对象的start方法。
第四步:使用Connection对象创建一个Session对象。
第五步:使用Session对象创建一个Destination对象(topic、queue),此处创建一个Queue对象。
第六步:使用Session对象创建一个Producer对象。
第七步:创建一个Message对象,创建一个TextMessage对象。
第八步:使用Producer对象发送消息。
第九步:关闭资源。

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
   @Test
public void testQueueProducer() throws Exception {
// 第一步:创建ConnectionFactory对象,需要指定服务端ip及端口号。
//brokerURL服务器的ip及端口号
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.168:61616");
// 第二步:使用ConnectionFactory对象创建一个Connection对象。
Connection connection = connectionFactory.createConnection();
// 第三步:开启连接,调用Connection对象的start方法。
connection.start();
// 第四步:使用Connection对象创建一个Session对象。
//第一个参数:是否开启事务。true:开启事务,第二个参数忽略。
//第二个参数:当第一个参数为false时,才有意义。消息的应答模式。1、自动应答2、手动应答。一般是自动应答。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 第五步:使用Session对象创建一个Destination对象(topic、queue),此处创建一个Queue对象。
//参数:队列的名称。
Queue queue = session.createQueue("test-queue");
// 第六步:使用Session对象创建一个Producer对象。
MessageProducer producer = session.createProducer(queue);
// 第七步:创建一个Message对象,创建一个TextMessage对象。
/*TextMessage message = new ActiveMQTextMessage();
message.setText("hello activeMq,this is my first test.");*/
TextMessage textMessage = session.createTextMessage("hello activeMq,this is my first test.");
// 第八步:使用Producer对象发送消息。
producer.send(textMessage);
// 第九步:关闭资源。
producer.close();
session.close();
connection.close();
}

Consumer

Topic

Producer

生产者:发布消息。

使用步骤:
第一步:创建ConnectionFactory对象,需要指定服务端ip及端口号。
第二步:使用ConnectionFactory对象创建一个Connection对象。
第三步:开启连接,调用Connection对象的start方法。
第四步:使用Connection对象创建一个Session对象。
第五步:使用Session对象创建一个Destination对象(topic、queue),此处创建一个Topic对象。
第六步:使用Session对象创建一个Producer对象。
第七步:创建一个Message对象,创建一个TextMessage对象。
第八步:使用Producer对象发送消息。
第九步:关闭资源。

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
   @Test
public void testTopicProducer() throws Exception {
// 第一步:创建ConnectionFactory对象,需要指定服务端ip及端口号。
// brokerURL服务器的ip及端口号
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.168:61616");
// 第二步:使用ConnectionFactory对象创建一个Connection对象。
Connection connection = connectionFactory.createConnection();
// 第三步:开启连接,调用Connection对象的start方法。
connection.start();
// 第四步:使用Connection对象创建一个Session对象。
// 第一个参数:是否开启事务。true:开启事务,第二个参数忽略。
// 第二个参数:当第一个参数为false时,才有意义。消息的应答模式。1、自动应答2、手动应答。一般是自动应答。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 第五步:使用Session对象创建一个Destination对象(topic、queue),此处创建一个topic对象。
// 参数:话题的名称。
Topic topic = session.createTopic("test-topic");
// 第六步:使用Session对象创建一个Producer对象。
MessageProducer producer = session.createProducer(topic);
// 第七步:创建一个Message对象,创建一个TextMessage对象。
/*
* TextMessage message = new ActiveMQTextMessage(); message.setText(
* "hello activeMq,this is my first test.");
*/
TextMessage textMessage = session.createTextMessage("hello activeMq,this is my topic test");
// 第八步:使用Producer对象发送消息。
producer.send(textMessage);
// 第九步:关闭资源。
producer.close();
session.close();
connection.close();
}

Consumer

消费者:接收消息。

使用步骤:
第一步:创建一个ConnectionFactory对象。
第二步:从ConnectionFactory对象中获得一个Connection对象。
第三步:开启连接。调用Connection对象的start方法。
第四步:使用Connection对象创建一个Session对象。
第五步:使用Session对象创建一个Destination对象。和发送端保持一致topic,并且话题的名称一致。
第六步:使用Session对象创建一个Consumer对象。
第七步:接收消息。
第八步:打印消息。
第九步:关闭资源

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
   @Test
public void testTopicConsumer() throws Exception {
// 第一步:创建一个ConnectionFactory对象。
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.168:61616");
// 第二步:从ConnectionFactory对象中获得一个Connection对象。
Connection connection = connectionFactory.createConnection();
// 第三步:开启连接。调用Connection对象的start方法。
connection.start();
// 第四步:使用Connection对象创建一个Session对象。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 第五步:使用Session对象创建一个Destination对象。和发送端保持一致topic,并且话题的名称一致。
Topic topic = session.createTopic("test-topic");
// 第六步:使用Session对象创建一个Consumer对象。
MessageConsumer consumer = session.createConsumer(topic);
// 第七步:接收消息。
consumer.setMessageListener(new MessageListener() {

@Override
public void onMessage(Message message) {
try {
TextMessage textMessage = (TextMessage) message;
String text = null;
// 取消息的内容
text = textMessage.getText();
// 第八步:打印消息。
System.out.println(text);
} catch (JMSException e) {
e.printStackTrace();
}
}
});
System.out.println("topic的消费端03。。。。。");
// 等待键盘输入
System.in.read();
// 第九步:关闭资源
consumer.close();
session.close();
connection.close();
}

ActiveMQ消息数据持久化

场景问题:服务器断电重启,未被消费的消息是否会在重启之后被继续消费?

两种选择:非持久性模式/持久性模式
1)非持久性模式:服务器断电(关闭)之后,使用非持久性模式时,没有被消费的消息不会继续消费全部丢失;
程序会报一个连接关闭异常停止运行,继续启动服务器运行程序,不会接收任何消息。
2)持久性模式:服务器断电(关闭)后,使用持久性模式时,没有被消费的消息会继续消费;
程序也会报连接关闭异常,但再次启动服务器和程序后,接收方还能继续原来的消息再次接收。

PERSISTENT:
指示JMS provider持久保存消息,以保证消息不会因为JMS provider的失败而丢失

NON_PERSISTENT:
不要求JMS provider持久保存消息

在消息提供者设置消息持久化:producer.setDeliveryMode(DeliveryMode.PERSISTENT);

JMS可靠消息机制

JMS消息只有在被确认之后,才认为已经被成功的消费了。
消息的成功消费通常包含三个阶段:客户接收消息,客户处理消息和消息被确认。

在事务性会话中,当一个事务被提交的时候,确认自动发生。在非事务性会话中,消息何时被确认取决于创建会话时的应答模式。
改参数有三个可选值:

  1. Session.AUTO_ACKNOWLEDGE:自动签收
    当客户成功的从receive方法返回的时候,或者从MessageListener.onMessage方法成功返回的时候,会话自动确认客户收到的消息。
  2. Session.CLIENT_ACKNOWLEDGE:手动签收
    客户通过调用消息的acknowledge方法确认消息。需要注意的是,在这种模式中,确认是在会话层上进行,确认一个被消费的消息,将自动确认所有已被会话消费的消息。例如,如果一个消息消费者消费了10个消息,然后确认第5个消息,那么所有10个消息都被确认。
  3. Session.DUPS_ACKNOWLEDGE:
    该选择只是会话迟钝的确认消息的提交。如果JMS Provider失败,那么可能会导致一些重复的消息。如果是重复的消息,那么JMS provider必须把消息头的JMSRedelivered字段设置为true
  4. 带事物的session:
    完成操作后需要手动的commit

JMS简介

JMS是什么

JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM,Message oriented Middleware)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。

JMS允许应用程序组件基于JavaEE平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。

阅读全文 »

什么是中间件

什么是中间件

我国企业从20世纪80年代开始就逐渐进行信息化建设,由于方法和体系的不成熟,以及企业业务的市场需求的不断变化,一个企业可能同时运行着多个不同的业务系统,这些系统可能基于不同的操作系统、不同的数据库、异构的网络环境。现在的问题是,如何把这些信息系统结合成一个有机地协同工作的整体,真正实现企业跨平台、分布式应用。中间件便是解决之道,它用自己的复杂换取了企业应用的简单。

中间件(Middleware)是处于操作系统和应用程序之间的软件,也有人认为它应该属于操作系统中的一部分。人们在使用中间件时,往往是一组中间件集成在一起,构成一个平台(包括开发平台和运行平台),但在这组中间件中必须要有一个通信中间件,即中间件+平台+通信,这个定义也限定了只有用于分布式系统中才能称为中间件,同时还可以把它与支撑软件和使用软件区分开来


阅读全文 »