WenLiangBaoBlog

个人博客


  • 首页

  • 标签7

  • 归档62

  • 搜索

AJAX

发表于 2021-12-06 | 更新于 2019-05-30

AJAX 简介

AJAX 全称 Asynchronous JavaScript and XML

AJAX = 异步 JavaScript 和 XML。

AJAX 是一种用于创建快速动态网页的技术。

AJAX 工作流程图

ajax基于Internet

AJAX是基于现有的Internet标准,并且联合使用它们:

XMLHttpRequest 对象 (异步的与服务器交换数据)
JavaScript/DOM (信息显示/交互)
CSS (给数据定义样式)
XML (作为转换数据的格式)
lamp AJAX应用程序与浏览器和平台无关的!

ajax实例

基础实例

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
function loadXMLDoc(){

var xmlhttp;
if (window.XMLHttpRequest){
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp=new XMLHttpRequest();
}
else{
// IE6, IE5 浏览器执行代码
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","https://www.runoob.com/try/ajax/ajax_info.txt",true);
xmlhttp.send();
}
</script>
</head>
<body>

<div id="myDiv"><h2>使用 AJAX 修改该文本内容</h2></div>
<button type="button" onclick="loadXMLDoc()">修改内容</button>

</body>
</html>

内容解析:

xmlHttpRequest 请求运行流程

1.创建xmlHttpRequest对象

2.使用xmlHttpRequest对象开启一个请求方式

3.发送请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1.创建xmlHttpRequest对象
xmlhttp=new XMLHttpRequest(); //创建xmlHttpRequest对象,IE7+, Firefox, Chrome, Opera, Safari 浏览器可执行代码

xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); //同样也是创建XMLHttpRequest对象,针对IE 6,5老版本的创建方式

2.使用xmlHttpRequest对象开启一个请求

//open(method,url,async)
// 规定请求的类型、URL 以及是否异步处理请求。(开启一个请求)
// method:请求的类型;GET 或 POST
// url:文件在服务器上的位置,服务器请求路径
// async:true(异步)或 false(同步),ajax中此选项一般都是 true
xmlhttp.open("GET","https://www.runoob.com/try/ajax/ajax_info.txt",true);

//send(string)
// 将请求发送到服务器。
// string:仅用于 POST 请求,用于添加post请求中的数据
xmlhttp.send();

GET 于 POST 的对比

与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。

然而,在以下情况中,请使用 POST 请求:

1.无法使用缓存文件(更新服务器上的文件或数据库)
2.向服务器发送大量数据(POST 没有数据量限制)
3.发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠

xmlHttpRequest.setRequestHeader(header,value)

此方法用于,向请求添加 HTTP 头。

header: 规定头的名称
value: 规定头的值

例如

1
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");

responseText and responseXML

responseText 用于获取服务器中的文本信息资源

responseXML 用于获取服务器中的XML格式资源

使用实例

1
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;

onreadystatechange 事件

当请求被发送到服务器时,我们需要执行一些基于响应的任务。

每当 readyState 改变时,就会触发 onreadystatechange 事件。

readyState 属性存有 XMLHttpRequest 的状态信息。

下面是 XMLHttpRequest 对象的三个重要的属性:

onreadystatechange :存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。

readyState :

存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。

0: 请求未初始化

1: 服务器连接已建立

2: 请求已接收

3: 请求处理中

4: 请求已完成,且响应已就绪

status :200: OK HTTP 响应状态码。404 :未找到页面

使用实例:

1
2
3
4
5
6
7
8
9
function myFunction()
{
loadXMLDoc("/try/ajax/ajax_info.txt",function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
//要做的事情 todo
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
});
}

参考网址:https://www.runoob.com/ajax/ajax-tutorial.html

Anaconda 工具

发表于 2021-12-06 | 更新于 2019-09-27

Python

version 环境切换

列出当前Anaconda python拥有的环境列表

1
2
3
4
5
(python36) spiderbao@spiderbao-CW65S:~$ conda info -e
# conda environments:
#
base /home/spiderbao/anaconda3
python36 * /home/spiderbao/anaconda3/envs/python36

其中base 为安装Anaconda 所默认安装的

查看当前Python的版本

1
2
(python36) spiderbao@spiderbao-CW65S:~$ python -V
Python 3.6.9 :: Anaconda, Inc.

安装新的Python 版本

1
conda create --name python36 python=3.6

其中 python36 为别名

新版本安装完后一些常用的库和包需要手动安装

切换Python 版本

1
conda activate python36

其中Python36 为别名

切换会之前的环境

1
conda deactivate

删除指定的版本环境

1
conda remove --name python34 --all

其中python34 为别名

手动安装常用的库和包

1
2
3
4
5
conda install numpy

conda install scipy

conda install matplotlib

webgl中文网学习笔记

发表于 2021-12-06 | 更新于 2019-07-03

chapter1:

what is Three.js?

什么是threejs,很简单,你将它理解成three + js就可以了。three表示3D的意思,js表示javascript的意思。那么合起来,three.js就是使用javascript 来写3D程序的意思。

Javascript是运行在网页端的脚本语言,那么毫无疑问Three.js也是运行在浏览器上的。

看到这里,也许你就开始有很多疑问了,那么让我来猜猜你的疑问吧。

1、javascript能写高效率的3D程序吗?
能。技术在进步,几年前也许这是不行,写3D程序,最好是用c++,这样才能保证效率,但是现在,世界改变了。javascript的计算能力因为google的V8引 擎得到了迅猛的增强,做3D程序,做服务器都没有问题。如果你对服务器感兴趣,你可以看看nodejs,当然,不是现在。现在,你最好保持热情,将Three.js学精深,在以后的工作学习中做出 更大的成绩。

Three.js where come from?

它源自github的一个开源项目.

先去下载代码,它的地址是: https://github.com/mrdoob/three.js。 如图所示

analysis of source directory structur?

要了解事物的真相,就要解析到它的深处去,我们来看看Three.js的源目录结果。用解压软件解开刚才的源码包,各个目录如下所示:

Build目录:包含两个文件,three.js 和three.min.js 。这是three.js最终被引用的文件。一个已经压缩,一个没有压缩的js文件。

Docs目录:这里是three.js的帮助文档,里面是各个函数的api,可惜并没有详细的解释。试图用这些文档来学会three.js是不可能的。

Editor目录:一个类似3D-max的简单编辑程序,它能创建一些三维物体。

Examples目录:一些很有趣的例子demo,可惜没有文档介绍。对图像学理解不深入的同学,学习成本非常高。

Src目录:源代码目录,里面是所有源代码。

Test目录:一些测试代码,基本没用。

Utils目录:存放一些脚本,python文件的工具目录。例如将3D-Max格式的模型转换为three.js特有的json模型。

.gitignore文件:git工具的过滤规则文件,没有用。

CONTRIBUTING.md文件:一个怎么报bug,怎么获得帮助的说明文档。

LICENSE文件:版权信息。

README.md文件:介绍three.js的一个文件,里面还包含了各个版本的更新内容列表。

examples one

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>canvas { width: 100%; height: 100% }</style>
</head>
<body>
<script src="js/three.js"></script>

</body>
</html>

在chrome浏览器中 开发者工具中的console中输入 THREE.REVISION 控制台上会输出相应的版本信息。

chapter2:

three component

在Three.js中,要渲染物体到网页中,我们需要3个组建:场景(scene)、相机(camera)和渲染器(renderer)。有了这三样东西,才能将物体渲染到网页中去。

记住关建语句:有了这三样东西,我们才能够使用相机将场景渲染到网页上去。

创建这三要素的代码如下:

1
2
3
4
5
var scene = new THREE.Scene();  // 场景
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);// 透视相机
var renderer = new THREE.WebGLRenderer(); // 渲染器
renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器的大小为窗口的内宽度,也就是内容区的宽度
document.body.appendChild(renderer.domElement);

在Threejs中场景就只有一种,用THREE.Scene来表示,要构件一个场景也很简单,只要new一个对象就可以了,代码如下:

var scene = new THREE.Scene();

场景是所有物体的容器,如果要显示一个苹果,就需要将苹果对象加入场景中。

camera

另一个组建是相机,相机决定了场景中那个角度的景色会显示出来。相机就像人的眼睛一样,人站在不同位置,抬头或者低头都能够看到不同的景色。

场景只有一种,但是相机却又很多种。和现实中一样,不同的相机确定了呈相的各个方面。比如有的相机适合人像,有的相机适合风景,专业的摄影师根据实际用途不一样,选择不同的相机。对程序员来说,只要设置不同的相机参数,就能够让相机产生不一样的效果。

在Threejs中有多种相机,这里介绍两种,它们是:

透视相机(THREE.PerspectiveCamera)、这里我们使用一个透视相机,透视相机的参数很多,这里先不详细讲解。后面关于相机的那一章,我们会花大力气来讲。定义一个相机的代码如下所示:(已经迫不及待想看看相机的参数了,点这里)

1
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);

renderer

最后一步就是设置渲染器,渲染器决定了渲染的结果应该画在页面的什么元素上面,并且以怎样的方式来绘制。这里我们定义了一个WebRenderer渲染器,代码如下所示:

1
2
3
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

注意,渲染器renderer的domElement元素,表示渲染器中的画布,所有的渲染都是画在domElement上的,所以这里的appendChild表示将这个domElement挂接在body下面,这样渲染的结果就能够在页面中显示了。

scene add object

那现在,我们将一个物体添加到场景中:

1
2
3
4
var geometry = new THREE.CubeGeometry(1,1,1); 
var material = new THREE.MeshBasicMaterial({color: 0x00ff00});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);

代码中出现了THREE.CubeGeometry,THREE.CubeGeometry是什么东东,他是一个几何体,几何体由什么组成,已经不是本课的主要内容了,后面的课程我们会详细的学到。不过这里你只需要知道CubeGeometry是一个正方体或者长方体,究竟是什么,由它的3个参数所决定,cubeGeometry的原型如下代码所示:

1
CubeGeometry(width, height, depth, segmentsWidth, segmentsHeight, segmentsDepth, materials, sides);

width:立方体x轴的长度

height:立方体y轴的长度

depth:立方体z轴的深度,也就是长度

想一想大家就明白,以上3个参数就能够确定一个立方体。

剩下的几个参数就要费解和复杂一些了,不过后面我们会自己来写一个立方体,到时候,你会更明白这些参数的意义,这里你可以将这些参数省略。

render

终于到了最后一步,这一步做完后,就可以该干嘛干嘛去了。

渲染应该使用渲染器,结合相机和场景来得到结果画面。实现这个功能的函数是

renderer.render(scene, camera);

渲染函数的原型如下:

render( scene, camera, renderTarget, forceClear )

各个参数的意义是:

scene:前面定义的场景

camera:前面定义的相机

renderTarget:渲染的目标,默认是渲染到前面定义的render变量中

forceClear:每次绘制之前都将画布的内容给清除,即使自动清除标志autoClear为false,也会清除。

loop render

渲染有两种方式:实时渲染和离线渲染 。

先看看离线渲染,想想《西游降魔篇》中最后的佛主,他肯定不是真的,是电脑渲染出来的,其画面质量是很高的,它是事先渲染好一帧一帧的图片,然后再把图片拼接成电影的。这就是离线渲染。如果不事先处理好一帧一帧的图片,那么电影播放得会很卡。CPU和GPU根本没有能力在播放的时候渲染出这种高质量的图片。

实时渲染:就是需要不停的对画面进行渲染,即使画面中什么也没有改变,也需要重新渲染。下面就是一个渲染循环:

1
2
3
4
5
6
function render() {
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
requestAnimationFrame(render);
}

其中一个重要的函数是requestAnimationFrame,这个函数就是让浏览器去执行一次参数中的函数,这样通过上面render中调用requestAnimationFrame()函数,requestAnimationFrame()函数又让rander()再执行一次,就形成了我们通常所说的游戏循环了。

the relationship between scene 、 camera 、render

Three.js中的场景是一个物体的容器,开发者可以将需要的角色放入场景中,例如苹果,葡萄。同时,角色自身也管理着其在场景中的位置。

相机的作用就是面对场景,在场景中取一个合适的景,把它拍下来。

渲染器的作用就是将相机拍摄下来的图片,放到浏览器中去显示。他们三者的关系如下图所示:

chapter3.1

composition of the 3d World

在计算机世界里,3D世界是由点组成,两个点能够组成一条直线,三个不在一条直线上的点就能够组成一个三角形面,无数三角形面就能够组成各种形状的物体,如下图:

我们通常把这种网格模型叫做Mesh模型。给物体贴上皮肤,或者专业点就叫做纹理,那么这个物体就活灵活现了。最后无数的物体就组成了我们的3D世界。

那么3D世界的组成,是否真的这样简单?是的,从编程的角度,目前为此,你只需要知道这些。下一节,我们从点说起。

dot

在三维空间中的某一个点可以用一个坐标点来表示。一个坐标点由x,y,z三个分量构成。在three.js中,点可以在右手坐标系中表示:

空间几何中,点可以用一个向量来表示,在Three.js中也是用一个向量来表示的,代码如下所示:

1
2
3
4
5
6
7
THREE.Vector3 = function ( x, y, z ) {

this.x = x || 0;
this.y = y || 0;
this.z = z || 0;

};

我们来分析这段代码:前面我们已经知道了THREE是Three.js引擎的一个全局变量。只要你想用它,就可以在任何地方用它。有点充气娃娃的意思,不需要你同意,你想用就用吧。

那么THREE.Vector3呢,就是表示Vector3是定义在THREE下面的一个类。以后要用Vector3,就必须要加THREE前缀。当然Three.js的设计者,也可以不加THREE这个前缀,但是他们预见到,Three.js引擎中会有很多类型,最好给这些类型加一个前缀,以免与开发者的代码产生冲突。

THREE.Vector3被赋值为一个函数。这个函数有3个参数,分别代表x坐标,y坐标和z坐标的分量。函数体内的代码将他们分别赋值给成员变量x,y,z。看看上面的代码,中间使用了一个“||”(或)运算符,就是当x=null或者undefine时,this.x的值应该取0。

dot operaction

在3D世界中点可以用THREE.Vector3D来表示。对应源码为/src/math/Vector3.js(注意:源码所在的位置,可能不同版本不一样,请自己搜索Vector3关键词来确定)。在您继续学习之前,你可以打开该文件浏览一下,推荐使用WebStorm,如果没有,你也可以用NotePad++。

现在来看看怎么定义个点,假设有一个点x=4,y=8,z=9。你可以这样定义它:

1
var point1 = new THREE.Vecotr3(4,8,9);

另外你也可以使用set方法,代码如下:

1
2
var point1 = new THREE.Vector3();
point1.set(4,8,9);

chapter3.2

coordinate

我们下面会学习使用直线画一个网格出来,为了更好的理解这个网格在空间中的位置,我们是时候,讲一讲空间坐标系了。

right handed cartesian coordinate

Threejs使用的是右手坐标系,这源于opengl默认情况下,也是右手坐标系。下面是右手坐标系的图例,如果对这个概念不理解,可以百度一下,我保证你伸出手比划的那一瞬间你就明白了,如果不明白请给作者留言,我会尽快补上关于坐标系的知识。

图中右边那个手对应的坐标系,就是右手坐标系。在Threejs中,坐标和右边的坐标完全一样。x轴正方向向右,y轴正方向向上,z轴由屏幕从里向外。

line

在Threejs中,一条线由点,材质和颜色组成。

点由THREE.Vector3表示,Threejs中没有提供单独画点的函数,它必须被放到一个THREE.Geometry形状中,这个结构中包含一个数组vertices,这个vertices就是存放无数的点(THREE.Vector3)的数组。

为了绘制一条直线,首先我们需要定义两个点,如下代码所示:

1
2
3
var p1 = new THREE.Vector3( -100, 0, 100 );

var p2 = new THREE.Vector3( 100, 0, -100 );

请大家思考一下,这两个点在坐标系的什么位置,然后我们声明一个THREE.Geometry,并把点加进入,代码如下所示:

1
2
3
4
5
var geometry = new THREE.Geometry();

geometry.vertices.push(p1);

geometry.vertices.push(p2);

geometry.vertices的能够使用push方法,是因为geometry.vertices是一个数组。这样geometry 中就有了2个点了。

然后我们需要给线加一种材质,可以使用专为线准备的材质,THREE.LineBasicMaterial。

最终我们通过THREE.Line绘制了一条线,如下代码所示:

1
var line = new THREE.Line( geometry, material, THREE.LinePieces );

ok,line就是我们要的线条了。

chapter4

loop render

物体运动还有一个关键点,就是要渲染物体运动的每一个过程,让它显示给观众。渲染的时候,我们调用的是渲染器的render() 函数。代码如下:

renderer.render( scene, camera );

如果我们改变了物体的位置或者颜色之类的属性,就必须重新调用render()函数,才能够将新的场景绘制到浏览器中去。不然浏览器是不会自动刷新场景的。

如果不断的改变物体的颜色,那么就需要不断的绘制新的场景,所以我们最好的方式,是让画面执行一个循环,不断的调用render来重绘,这个循环就是渲染循环,在游戏中,也叫游戏循环。

为了实现循环,我们需要javascript的一个特殊函数,这个函数是requestAnimationFrame。

调用requestAnimationFrame函数,传递一个callback参数,则在下一个动画帧时,会调用callback这个函数。

于是,我们的游戏循环会这样写。

1
2
3
4
5
6
7
function animate() {

render();

requestAnimationFrame( animate );

}

这样就会不断的执行animate这个函数。也就是不断的执行render()函数。在render()函数中不断的改变物体或者摄像机的位置,并渲染它们,就能够实现动画了。

Stats

关于性能:测试一个程序,性能上是否有瓶颈,在3D世界里,经常使用帧数的概念,首先我们来定义一下帧数的意义。

帧数:图形处理器每秒钟能够刷新几次,通常用fps(Frames Per Second)来表示。如下是每秒钟59次刷新的应用:

当物体在快速运动时,当人眼所看到的影像消失后,人眼仍能继续保留其影像1/24秒左右的图像,这种现象被称为视觉暂留现象。是人眼具有的一种性质。人眼观看物体时,成像于视网膜上,并由视神经输入人脑,感觉到物体的像。一帧一帧的图像进入人脑,人脑就会将这些图像给连接起来,形成动画。

毫无疑问,帧数越高,画面的感觉就会越好。所以大多数游戏都会有超过30的FPS。为了监视FPS,看看你的程序哪里占用了很多的CPU时间,就需要学习一下性能监视器。

在Three.js中,性能由一个性能监视器来管理,它的介绍在https://github.com/mrdoob/stats.js 可以看到。性能监视器的截图如下所示:

其中FPS表示:上一秒的帧数,这个值越大越好,一般都为60左右。点击上面的图,就会变成下面的另一个视图。

MS表示渲染一帧需要的毫秒数,这个数字是越小越好。再次点击又可以回到FPS视图中。

性能监视器Stats的使用

在Three.js中,性能监视器被封装在一个类中,这个类叫做Stats,下面是一段伪代码,表示Stats的使用。

1
2
3
4
5
6
7
8
9
10
11
12
var stats = new Stats();
stats.setMode(1); // 0: fps, 1: ms
// 将stats的界面对应左上角
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.body.appendChild( stats.domElement );
setInterval( function () {
stats.begin();
// 你的每一帧的代码
stats.end();
}, 1000 / 60 );

你现在可以自己写一段代码,来验证一下,你的程序的帧数了。

Stats到底做了什么事情呢?我们来分析一下。

1、setMode函数

参数为0的时候,表示显示的是FPS界面,参数为1的时候,表示显示的是MS界面。

2、stats的domElement

stats的domElement表示绘制的目的地(DOM),波形图就绘制在这上面。

3、stats的begin函数

begin,在你要测试的代码前面调用begin函数,在你代码执行完后调用end()函数,这样就能够统计出这段代码执行的平均帧数了。

封装后的使用

1
2
3
4
5
6
7
8
9
10
function initStats() {
//监视器
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById('canvas-frame').appendChild(stats.domElement);
}

在需要监视的代码块中使用方法 stats.update();即可

Tween engine

上面介绍了通过移动相机和移动物体来产生动画的效果。使用的方法是在渲染循环里去移动相机或者物体的位置。如果动画稍微复杂一些,这种方式实现起来就比较麻烦一些了。

为了使程序编写更容易一些,我们可以使用动画引擎来实现动画效果。和three.js紧密结合的动画引擎是Tween.js,你可以再https://github.com/sole下载。

对于快速构件动画来说,Tween.js是一个容易上手的工具

使用步骤:

1 在head中引入 <-script src="../js/tween.min.js" data-ke-src="../js/tween.min.js"><-/script>

2 第二步,就是构件一个Tween对象,对Tween进行初始化,本例的代码是:

1
2
3
4
5
6

function initTween()
{
new TWEEN.Tween( mesh.position)
.to( { x: -400 }, 3000 ).repeat( Infinity ).start();
}

TWEEN.Tween的构造函数接受的是要改变属性的对象,这里传入的是mesh的位置。Tween的任何一个函数返回的都是自身,所以可以用串联的方式直接调用各个函数。

to函数,接受两个参数,第一个参数是一个集合,里面存放的键值对,键x表示mesh.position的x属性,值-400表示,动画结束的时候需要移动到的位置。第二个参数,是完成动画需要的时间,这里是3000ms。

repeat( Infinity )表示重复无穷次,也可以接受一个整形数值,例如5次。

Start表示开始动画,默认情况下是匀速的将mesh.position.x移动到-400的位置。

3 需要在渲染函数中去不断的更新Tween,这样才能够让mesh.position.x移动位置:

1
2
3
4
5
6
7
function animation()
{
renderer.render(scene, camera);
requestAnimationFrame(animation);
stats.update();
TWEEN.update();
}

其中的TWEEN.update()完成了让动画动起来到目标。如果不调用这个函数场景就不能动起来了。

springboot

发表于 2021-12-06 | 更新于 2019-05-14

springboot

概述

springboot是spring的一个boot版本,它的出现是为了让你能更快的体验或使用spring。我经常把它看做spring的简单易用版。那他是从那几个方面去做到spring的简单易用的呢?

1.约定优于配置 —自动配置
2.依赖自带了起步依赖的坐标。—起步依赖

springboot_quick (dome)

1.pom构建:继承springbootparent ,起步依赖配置

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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.itcast</groupId>
<artifactId>springboot_quick</artifactId>
<version>1.0-SNAPSHOT</version>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>

<packaging>war</packaging>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

</project>

2.创建启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.itcast;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.swing.*;

/**
* @className MysoringBoot
* @Descirption springboot boot class
* @Author WengLiangBao
* @Date 19-5-12 下午1:21
* @Vsersion 1.0
*/
@SpringBootApplication
public class MysoringBoot {
/*
SpringApplication.run(MysoringBoot.class) 代表运行SpringBoot的启动类,参数为SpringBoot启动类的字节码对象
*/
public static void main(String[] args){
SpringApplication.run(MysoringBoot.class);
}
}

3.构建访问控制层controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.itcast.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* @className QuickStartController
* @Descirption quick dome contorller
* @Author WengLiangBao
* @Date 19-5-12 下午1:37
* @Vsersion 1.0
*/
@Controller
public class QuickStartController {


@RequestMapping("/quick")
@ResponseBody
public String quick(){
return "springboot 访问成功!";
}
}

pom中添加热部署

1
2
3
4
5
<!-- 热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>

起步依赖

自动配置

springboot配置文件

SpringBoot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者application.yml(application.yaml)进行配置。

SpringBoot默认会从Resources目录下加载application.properties或application.yml(application.yaml)

上面提及过,SpringBoot的配置文件,主要的目的就是对配置信息进行修改的,但在配置时的key从哪里去查询
呢?我们可以查阅SpringBoot的官方文档
文档URL:https://docs.spring.io/spring-boot/docs/2.0.1.RELEASE/reference/htmlsingle/#common-application-properties

配置文件与配置类的属性映射关系

@value

1.在application.yml 中添加如下属性

1
2
3
person:
name: xiaoming
age: 18

2.在对应类中使用

1
2
3
4
@Value("${person.name}")
private String name;
@Value("${person.age}")
private Integer age;

@configurationProperties

1.在application.yml 中添加如下属性

1
2
3
person:
name: xiaoming
age: 18

2.在对应的实体类中使用

1
2
3
4
5
6
7
8
@ConfigurationProperties(prefix = "person")
@Data
public class QuickStartController {


private String name;

private int age;

注意使用configurationProperties需要为指定的属性添加getting或setting ,本人使用lombok的@Data自动生成了getting,setting

springboot整合

mybatis 整合

1.pom 中添加坐标

1
2
3
4
5
6
7
8
9
10
11
<!--由于spring官方没有对mybatis整合提供技术方案支持,所以这里需要使用mybatis提供的整合jar包,并指定版本-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

2.application.yml 中配置mybatis及数据库链接信息

1
2
3
4
5
6
7
8
9
10
#DB configuration
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/mybatis_study?useUnicode=true&charaterEncoding=utf-8&useSSL=true
username: spider
password: *****
#mybatis configuration
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml

3.创建dao控制层和mapper映射文件

dao层文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.itcast.dao;

import com.itcast.pojo.User;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
* @className UserMapper
* @Descirption
* @Author WengLiangBao
* @Date 19-5-12 下午3:09
* @Vsersion 1.0
*/
@Mapper
public interface UserMapper {

public List<User> queryUserList();
}

Mapper ,编写位置对应application.yml中mapper-locations的位置,内容为

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.itcast.dao.UserMapper">

<select id="queryUserList" resultType="com.itcast.pojo.User">
select * from user
</select>
</mapper>

4.数据库对应pojo编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.itcast.pojo;

import lombok.Data;

/**
* @className User
* @Descirption
* @Author WengLiangBao
* @Date 19-5-12 下午3:07
* @Vsersion 1.0
*/
@Data
public class User {

private Integer id;

private String username;

private String password;

private String name;

}

Junit 整合

1.pom中添加Junit起步依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>

2.编写测试类

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
package com.itcast.daoTest;

import com.itcast.MysoringBoot;
import com.itcast.dao.UserMapper;
import com.itcast.pojo.User;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.List;

/**
* @className UserMapperTest
* @Descirption 用户映射测试
* @Author WengLiangBao
* @Date 19-5-12 下午3:50
* @Vsersion 1.0
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MysoringBoot.class)//程序启动入口类
public class UserMapperTest {

@Autowired
private UserMapper userMapper;

@Test
public void queryListUser(){
List<User> users = userMapper.queryUserList();
Assert.assertNotNull(users);
List<User> array = new ArrayList<User>();
array.add(new User(1,"zhangsan","123","张三"));
array.add(new User(2,"lisi","123","李四"));
System.out.print(users);
Assert.assertArrayEquals(users.toArray(),array.toArray());
}
}

redis 整合

1.添加起步依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.application.yml 中配置redis所需属性

1
2
3
4
spring:
redis:
host: xx.104.xxx.29
port: 6379

3.编写测试类测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Autowired
private UserMapper userMapper;

@Autowired
private RedisTemplate<String,String> redisTemplate;

@Test
public void redisTest() throws JsonProcessingException {
String userListData = redisTemplate.boundValueOps("user.findAll").get();
if(userListData==null){
List<User> users = userMapper.queryUserList();
String uD = new ObjectMapper().writeValueAsString(users);
userListData = uD;
redisTemplate.boundValueOps("user.findAll").set(uD);
System.out.print("从数据库中获取");
}else {
System.out.print("从redis缓存中获取");
}
System.out.print(userListData);
}

springJPA 整合

1.添加spring Data JPA起步依赖及其所需的一些坐标

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

2.application.yml中配置数据库及jpa的相关属性

1
2
3
4
5
6
7
8
9
10
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_study?useUnicode=true&charaterEncoding=utf-8&useSSL=true
spring.datasource.username=spider
spring.datasource.password=*****

spring.jpa.database=MySQL
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy

3.创建pojo并映射

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
package com.example.demo.pojo;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

/**
* @className User
* @Descirption
* @Author WengLiangBao
* @Date 19-5-12 下午4:58
* @Vsersion 1.0
*/
@Entity
@Data
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String username;

private String password;

private String name;
}

4.编写dao层的对应操作接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.example.demo.dao;

import com.example.demo.pojo.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

/**
* @className UserRepository
* @Descirption
* @Author WengLiangBao
* @Date 19-5-12 下午5:21
* @Vsersion 1.0
*/
public interface UserRepository extends JpaRepository<User,Long> {
public List<User> findAll();
}

5.编写测试类测试验证

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
package com.example.demo;

import com.example.demo.dao.UserRepository;
import com.example.demo.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class DemoApplicationTests {

@Autowired
private UserRepository userRepository;

@Test
public void contextLoads() {
List<User> all = userRepository.findAll();
System.out.print(all);
}

}

mysql必知必会-note

发表于 2021-12-06 | 更新于 2019-09-10

了解sql

数据库基础术语

什么是数据库

数据库是一个以某种有组织的方式存储的数据集合。所以本人认为数据库是一个管理数据的概念。

表

表是某种特定类型数据的结构化清单。

模式

模式 是关于数据库和表的布局及特性的信息

列

列 column 表中的一个字段,所有表是由一个或多个列组成。

数据类型

数据类型 datatype 表示所容许的数据类型。每个表的列都有相应的数据类型,它限制了列中存储的数据。数据类型对于数据的排序,和优化磁盘使用方面起到了重要的作用。

行

行 row 表中的一行记录。

主键

主键 primarykey 由一列或列表示。它们与普通的列不同,它们具有其值具有表中唯一性切不为null的特性。

什么是sql

sql 的全称是structured query language (结构化查询语言)它是一个专门用来跟数据库通讯的语言。

sql具有的特定及优点如下

1.互通性 sql 不是某个DBMS厂商的专属语言,所以使用sql可以操作大多数DBMS产品 如MySQL

2.简单易学。sql的大多数功能由一个或几个关键字组成,所以学习成本较低。

3.在保持语法简单的情况下,做到了功能强大操作灵活。

mysql简介

什么是MySQL

首先MySQL它是一种DBMS产品,DBMS是databaseManageSystem 的简称。其次MySQL具有如下特性

1.成本底,由于MySQL是开源产品所以企业商业是不需要支付费用的。

2.性能,MySQL在DBMS系列产品中的性能优越

3.可信赖,就像Linux内核一样。MySQL的开源使得其的使用者和维护者增多。

4.简单易用

DBMS产品分类

DBMS产品可以大致分为2类,一类是基于共享文件系统如File Maker 等。另一类是基于客户机与服务器的如MySQL、Oracle等

MySQL工具(客户端)

MySQL workbench

mysql命令行(自带)

使用MySQL

链接

链接一般用于像 MySQL workbench 这样的工具或程序代码中。

链接案例如下:

1
root@localhost:3306

其中 root表示用户名

localhost 表示IP地址

3306 表示服务端口

选择数据库

登录数据库后使用MySQL命令行工具选择切换数据库可以使用关键字USE实现

1
USE 存在的数据库名称

关于SHOW的一些操作

显示所有的数据库名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| crashcourse |
| fileupload |
| maven_study |
| mybatis_study |
| mysql |
| performance_schema |
| sys |
+--------------------+
8 rows in set (0.00 sec)

显示数据库中的所有表名称,注意:此时需要使用USE选择要查看的数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> USE crashcourse;
Database changed
mysql> SHOW TABLES;
+-----------------------+
| Tables_in_crashcourse |
+-----------------------+
| customers |
| orderitems |
| orders |
| productnotes |
| products |
| vendors |
+-----------------------+
6 rows in set (0.00 sec)

显示表中列column的信息

1
2
3
4
5
6
7
8
9
mysql> SHOW COLUMNS FROM orders;
+------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+----------------+
| order_num | int(11) | NO | PRI | NULL | auto_increment |
| order_date | datetime | NO | | NULL | |
| cust_id | int(11) | NO | MUL | NULL | |
+------------+----------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

此查询还有一个快捷方式就是

1
2
3
4
5
6
7
8
9
mysql> DESCRIBE orders;
+------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+----------------+
| order_num | int(11) | NO | PRI | NULL | auto_increment |
| order_date | datetime | NO | | NULL | |
| cust_id | int(11) | NO | MUL | NULL | |
+------------+----------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

DESCRIBE 是 SHOW COLUMNS FROM的一种快捷方式

显示数据库创建语句或表的创建语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mysql> SHOW CREATE DATABASE crashcourse;
+-------------+---------------------------------------------------------------------------------------+
| Database | Create Database |
+-------------+---------------------------------------------------------------------------------------+
| crashcourse | CREATE DATABASE `crashcourse` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_bin */ |
+-------------+---------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SHOW CREATE TABLE order;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'order' at line 1
mysql> SHOW CREATE TABLE orders;
+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| orders | CREATE TABLE `orders` (
`order_num` int(11) NOT NULL AUTO_INCREMENT,
`order_date` datetime NOT NULL,
`cust_id` int(11) NOT NULL,
PRIMARY KEY (`order_num`),
KEY `fk_orders_customers` (`cust_id`),
CONSTRAINT `fk_orders_customers` FOREIGN KEY (`cust_id`) REFERENCES `customers` (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=20010 DEFAULT CHARSET=utf8 COLLATE=utf8_bin |
+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

关于SHOW的一些命令可以通过 HELP SHOW获取

检索数据

SELECT语句

select是sql中最常用的查询语句

检索单个列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> SELECT prod_name FROM products;
+----------------+
| prod_name |
+----------------+
| .5 ton anvil |
| 1 ton anvil |
| 2 ton anvil |
| Detonator |
| Bird seed |
| Carrots |
| Fuses |
| JetPack 1000 |
| JetPack 2000 |
| Oil can |
| Safe |
| Sling |
| TNT (1 stick) |
| TNT (5 sticks) |
+----------------+
14 rows in set (0.00 sec)

本次查询只是单纯的检索了单个列的数据,没有对数据做过滤和排序。

个人认为过滤不是必须的,但排序是必须的。因为不指定排序的数据顺序是不可控的。

检索多个列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> SELECT prod_id,prod_name,prod_price FROM products;
+---------+----------------+------------+
| prod_id | prod_name | prod_price |
+---------+----------------+------------+
| ANV01 | .5 ton anvil | 5.99 |
| ANV02 | 1 ton anvil | 9.99 |
| ANV03 | 2 ton anvil | 14.99 |
| DTNTR | Detonator | 13.00 |
| FB | Bird seed | 10.00 |
| FC | Carrots | 2.50 |
| FU1 | Fuses | 3.42 |
| JP1000 | JetPack 1000 | 35.00 |
| JP2000 | JetPack 2000 | 55.00 |
| OL1 | Oil can | 8.99 |
| SAFE | Safe | 50.00 |
| SLING | Sling | 4.49 |
| TNT1 | TNT (1 stick) | 2.50 |
| TNT2 | TNT (5 sticks) | 10.00 |
+---------+----------------+------------+
14 rows in set (0.00 sec)

注意多个列间使用,分割最后一个不需要加入,不然会出现语法错误

如果现在需要检索表中的所有列,可以使用通配符*代替列名。

检索数据去重

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
mysql> SELECT vend_id FROM products;
+---------+
| vend_id |
+---------+
| 1001 |
| 1001 |
| 1001 |
| 1002 |
| 1002 |
| 1003 |
| 1003 |
| 1003 |
| 1003 |
| 1003 |
| 1003 |
| 1003 |
| 1005 |
| 1005 |
+---------+
14 rows in set (0.00 sec)

mysql> SELECT DISTINCT vend_id FROM products;
+---------+
| vend_id |
+---------+
| 1001 |
| 1002 |
| 1003 |
| 1005 |
+---------+
4 rows in set (0.00 sec)

第一次select 查询获取了products.vend_id的所有数据
第二次select 查询内容同上但是加入了DISTINCT关键字对数据进行了去重。

DISTINCT使用注意事项

1.不能对部分列使用,如 prod_id,DISTINCT vend_id。

2.DISTINCT 位于 SELECT 后 列前 ,所有的被检索的列将都具有去重功能。

检索限制输出

想要控制输出就使用 limit关键字如下

输出查询结果的前5行

1
2
3
4
5
6
7
8
9
10
11
mysql> SELECT prod_name FROM products LIMIT 5;
+--------------+
| prod_name |
+--------------+
| .5 ton anvil |
| 1 ton anvil |
| 2 ton anvil |
| Detonator |
| Bird seed |
+--------------+
5 rows in set (0.00 sec)

从第1行开始显示3行

1
2
3
4
5
6
7
8
9
mysql> SELECT prod_name FROM products LIMIT 1,3;
+-------------+
| prod_name |
+-------------+
| 1 ton anvil |
| 2 ton anvil |
| Detonator |
+-------------+
3 rows in set (0.00 sec)

使用完全限定

之前的查询语句中使用的都是先对表名,举个对比的例子如下

未使用完全限定的查询语句

SELECT prod_name FROM products LIMIT 1,3

使用完全限定的查询语句

SELECT products.prod_name FROM crashcourse.products LIMIT 1,3

使用完全限定时语句更严谨。推荐使用!

排序检索数据

单列排序数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> SELECT prod_name FROM products ORDER BY prod_name;
+----------------+
| prod_name |
+----------------+
| .5 ton anvil |
| 1 ton anvil |
| 2 ton anvil |
| Bird seed |
| Carrots |
| Detonator |
| Fuses |
| JetPack 1000 |
| JetPack 2000 |
| Oil can |
| Safe |
| Sling |
| TNT (1 stick) |
| TNT (5 sticks) |
+----------------+
14 rows in set (0.00 sec)

根据products.prod_name 按字母升序排序。

多列排序顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> SELECT prod_id,prod_price,prod_name FROM products ORDER BY prod_price,prod_name;
+---------+------------+----------------+
| prod_id | prod_price | prod_name |
+---------+------------+----------------+
| FC | 2.50 | Carrots |
| TNT1 | 2.50 | TNT (1 stick) |
| FU1 | 3.42 | Fuses |
| SLING | 4.49 | Sling |
| ANV01 | 5.99 | .5 ton anvil |
| OL1 | 8.99 | Oil can |
| ANV02 | 9.99 | 1 ton anvil |
| FB | 10.00 | Bird seed |
| TNT2 | 10.00 | TNT (5 sticks) |
| DTNTR | 13.00 | Detonator |
| ANV03 | 14.99 | 2 ton anvil |
| JP1000 | 35.00 | JetPack 1000 |
| SAFE | 50.00 | Safe |
| JP2000 | 55.00 | JetPack 2000 |
+---------+------------+----------------+
14 rows in set (0.00 sec)

上述例子中的输出,仅在多行拥有相同的prod_price值时,才对产品prod_name进行排序。如果prod_price的值都是唯一的就不会使用prod_name的条件进行排序

排序的方向

默认排序的方向是升序,相反降序的案例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> SELECT prod_id,prod_price,prod_name FROM products ORDER BY prod_price DESC;
+---------+------------+----------------+
| prod_id | prod_price | prod_name |
+---------+------------+----------------+
| JP2000 | 55.00 | JetPack 2000 |
| SAFE | 50.00 | Safe |
| JP1000 | 35.00 | JetPack 1000 |
| ANV03 | 14.99 | 2 ton anvil |
| DTNTR | 13.00 | Detonator |
| FB | 10.00 | Bird seed |
| TNT2 | 10.00 | TNT (5 sticks) |
| ANV02 | 9.99 | 1 ton anvil |
| OL1 | 8.99 | Oil can |
| ANV01 | 5.99 | .5 ton anvil |
| SLING | 4.49 | Sling |
| FU1 | 3.42 | Fuses |
| FC | 2.50 | Carrots |
| TNT1 | 2.50 | TNT (1 stick) |
+---------+------------+----------------+
14 rows in set (0.00 sec)

另外 DESC 不同于 DISTINCT 它可用于不同的条件列,只需要在每个列后加入DESC即可。

SELECT prod_id,prod_price,prod_name FROM products ORDER BY prod_price DESC,prod_name;

表示按价格降序排序,如果有价格相同的按商品名称升序排序。

过滤数据

where子语句

where子语句可以添加一些查询条件,过滤数据。

根据where 操作符过滤

where操作符如下

= 等于

<> 不等于

!= 不等于

< 小于

<= 小于等于

大于

= 大于等于

BETWEEN 在指定两个值之间,包括指定的值

BETWEEN 使用

获取价格在5-10元的商品

1
2
3
4
5
6
7
8
9
10
11
mysql> SELECT prod_name,prod_price FROM products WHERE prod_price BETWEEN 5 AND 10 ORDER BY prod_price DESC;
+----------------+------------+
| prod_name | prod_price |
+----------------+------------+
| Bird seed | 10.00 |
| TNT (5 sticks) | 10.00 |
| 1 ton anvil | 9.99 |
| Oil can | 8.99 |
| .5 ton anvil | 5.99 |
+----------------+------------+
5 rows in set (0.00 sec)

空值null过滤

获取商品价格为null 的所有商品

1
2
mysql> SELECT prod_name FROM products WHERE prod_price IS NULL;
Empty set (0.00 sec)

获取商品价格不为null 的所有商品

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> SELECT prod_name FROM products WHERE prod_price IS NOT NULL;
+----------------+
| prod_name |
+----------------+
| .5 ton anvil |
| 1 ton anvil |
| 2 ton anvil |
| Detonator |
| Bird seed |
| Carrots |
| Fuses |
| JetPack 1000 |
| JetPack 2000 |
| Oil can |
| Safe |
| Sling |
| TNT (1 stick) |
| TNT (5 sticks) |
+----------------+
14 rows in set (0.00 sec)

AND OR IN NOT 操作符

操作符(operator)的定义:用来联结或改变WHERE字句中的关键字。也称为逻辑操作符

AND

相当于语句中的并且,用作条件追加。

查询厂商id为1003 并且价格大于等于10

1
2
3
4
5
6
7
8
9
10
mysql> SELECT vend_id,prod_id,prod_price,prod_name FROM products WHERE vend_id = 1003 AND prod_price >= 10 ORDER BY prod_name;
+---------+---------+------------+----------------+
| vend_id | prod_id | prod_price | prod_name |
+---------+---------+------------+----------------+
| 1003 | FB | 10.00 | Bird seed |
| 1003 | DTNTR | 13.00 | Detonator |
| 1003 | SAFE | 50.00 | Safe |
| 1003 | TNT2 | 10.00 | TNT (5 sticks) |
+---------+---------+------------+----------------+
4 rows in set (0.00 sec)

AND 可以在一次查询中多次使用

OR

相当于语句中或者,用于条件选择。

查询厂商id为1003或1002的商品信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> SELECT vend_id,prod_name,prod_price FROM products WHERE vend_id = 1003 OR vend_id = 1002 ORDER BY prod_name;
+---------+----------------+------------+
| vend_id | prod_name | prod_price |
+---------+----------------+------------+
| 1003 | Bird seed | 10.00 |
| 1003 | Carrots | 2.50 |
| 1003 | Detonator | 13.00 |
| 1002 | Fuses | 3.42 |
| 1002 | Oil can | 8.99 |
| 1003 | Safe | 50.00 |
| 1003 | Sling | 4.49 |
| 1003 | TNT (1 stick) | 2.50 |
| 1003 | TNT (5 sticks) | 10.00 |
+---------+----------------+------------+
9 rows in set (0.00 sec)

OR 可以在一次查询中多次使用

AND OR 联合使用

使用时需要注意优先级问题。

MySQL中默认 AND 与 OR 同时出现时 AND 优先被执行。

误区案例

查询厂商为1002或1003的价格大于等于10的商品

1
2
3
4
5
6
7
8
9
10
11
12
mysql> SELECT vend_id,prod_name,prod_price FROM products WHERE vend_id = 1002 OR vend_id = 1003 AND prod_price >= 10 ORDER BY prod_name;
+---------+----------------+------------+
| vend_id | prod_name | prod_price |
+---------+----------------+------------+
| 1003 | Bird seed | 10.00 |
| 1003 | Detonator | 13.00 |
| 1002 | Fuses | 3.42 |
| 1002 | Oil can | 8.99 |
| 1003 | Safe | 50.00 |
| 1003 | TNT (5 sticks) | 10.00 |
+---------+----------------+------------+
6 rows in set (0.00 sec)

此时发现 价格 为 3.42 和 8.99的商品被查出。原因是上面所说的优先级导致的。

误区解决

1
2
3
4
5
6
7
8
9
10
mysql> SELECT vend_id,prod_name,prod_price FROM products WHERE (vend_id = 1002 OR vend_id = 1003) AND prod_price >= 10 ORDER BY prod_name;
+---------+----------------+------------+
| vend_id | prod_name | prod_price |
+---------+----------------+------------+
| 1003 | Bird seed | 10.00 |
| 1003 | Detonator | 13.00 |
| 1003 | Safe | 50.00 |
| 1003 | TNT (5 sticks) | 10.00 |
+---------+----------------+------------+
4 rows in set (0.00 sec)

当OR条件加入()时它的优先级就大于了AND,就像是数学运算的优先级问题一样被()解决。

AND 和 OR 同时使用的场景下建议使用()。

IN

IN 用来指定条件范围
使用OR操作符过滤数据的一些案例,同样使用IN也可以完成

查询厂商id为1003或1002的商品信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> SELECT vend_id,prod_name,prod_price FROM products WHERE vend_id IN (1002,1003) ORDER BY prod_name;
+---------+----------------+------------+
| vend_id | prod_name | prod_price |
+---------+----------------+------------+
| 1003 | Bird seed | 10.00 |
| 1003 | Carrots | 2.50 |
| 1003 | Detonator | 13.00 |
| 1002 | Fuses | 3.42 |
| 1002 | Oil can | 8.99 |
| 1003 | Safe | 50.00 |
| 1003 | Sling | 4.49 |
| 1003 | TNT (1 stick) | 2.50 |
| 1003 | TNT (5 sticks) | 10.00 |
+---------+----------------+------------+
9 rows in set (0.00 sec)

IN与OR比较具有的优点如下

1.语法更清晰简洁

2.在使用IN时操作符优先级问题会减少

3.执行更快

4.IN可以包含其他的SELECT语句。使得能够动态创建WHERE字句

NOT

WHERE 字句中的NOT操作符有且只能有一个,功能是否定之后所跟的任何条件。

查询厂商id不是1002,1003的所有商品信息

1
2
3
4
5
6
7
8
9
10
11
mysql> SELECT prod_name,prod_price FROM products WHERE vend_id NOT IN (1002,1003) ORDER BY prod_name;
+--------------+------------+
| prod_name | prod_price |
+--------------+------------+
| .5 ton anvil | 5.99 |
| 1 ton anvil | 9.99 |
| 2 ton anvil | 14.99 |
| JetPack 1000 | 35.00 |
| JetPack 2000 | 55.00 |
+--------------+------------+
5 rows in set (0.00 sec)

NOT支持的取反的操作符有 IN、BETWEEN、EXISTS。

通配符进行过滤

LIKE

LIKE 是开启通配符匹配的关键字。

通配符号 % _

%表示匹配任意字符
_表示匹配一个字符

查询商品名称中包含 anvil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql> SELECT prod_id,prod_name FROM products WHERE prod_name LIKE '%anvil';
+---------+--------------+
| prod_id | prod_name |
+---------+--------------+
| ANV01 | .5 ton anvil |
| ANV02 | 1 ton anvil |
| ANV03 | 2 ton anvil |
+---------+--------------+
3 rows in set (0.00 sec)

mysql> SELECT prod_id,prod_name FROM products WHERE prod_name LIKE '_ ton anvil';
+---------+-------------+
| prod_id | prod_name |
+---------+-------------+
| ANV02 | 1 ton anvil |
| ANV03 | 2 ton anvil |
+---------+-------------+
2 rows in set (0.00 sec)

使用通配符的技巧

1.不要过度使用通配符,会造成数据库运算压力。

2.在明确需要使用通配符时,除非绝对有必要,否则不要把他们用在搜索模式的开始处。把通配符置于搜索模式的开始处,搜索起来是最慢的。

3.仔细注意通配符的位置。

使用正则表达式进行搜索

MySQL开启使用正则表达式的关键字符是 REGEXP

1
2
3
4
5
6
7
8
mysql> SELECT prod_name FROM products WHERE prod_name REGEXP '.000' ORDER BY prod_name;
+--------------+
| prod_name |
+--------------+
| JetPack 1000 |
| JetPack 2000 |
+--------------+
2 rows in set (0.00 sec)

创建计算字段

计算字段

计算字段是原先表中不存在的字段,通过使用数据库函数或计算后并重命名的字段。

拼接字段

vendors 表中厂商名称和地址是分离的。客户端需要获取一个vend—title的字段。格式是这样的 vend_name (vend_country)

1
2
3
4
5
6
7
8
9
10
11
12
mysql> SELECT Concat(Trim(vend_name),' (',Trim(vend_country),')') AS vend_title FROM vendors ORDER BY vend_name;
+-------------------------+
| vend_title |
+-------------------------+
| ACME (USA) |
| Anvils R Us (USA) |
| Furball Inc. (USA) |
| Jet Set (England) |
| Jouets Et Ours (France) |
| LT Supplies (USA) |
+-------------------------+
6 rows in set (0.00 sec)

其中函数 Concat 用于链接多个字符串

Trim函数用于去除字段左右的空格

AS 关键字用于起别名 alias

执行算数计算

在表查询中,需要添加一个字段,这个字段来至于表中2个字段的乘积

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mysql> SELECT prod_id,quantity,item_price,quantity*item_price AS expended_price FROM orderitems ORDER BY expended_price;
+---------+----------+------------+----------------+
| prod_id | quantity | item_price | expended_price |
+---------+----------+------------+----------------+
| SLING | 1 | 4.49 | 4.49 |
| OL1 | 1 | 8.99 | 8.99 |
| FB | 1 | 10.00 | 10.00 |
| FB | 1 | 10.00 | 10.00 |
| ANV03 | 1 | 14.99 | 14.99 |
| ANV02 | 3 | 9.99 | 29.97 |
| TNT2 | 5 | 10.00 | 50.00 |
| JP2000 | 1 | 55.00 | 55.00 |
| ANV01 | 10 | 5.99 | 59.90 |
| FC | 50 | 2.50 | 125.00 |
| TNT2 | 100 | 10.00 | 1000.00 |
+---------+----------+------------+----------------+
11 rows in set (0.00 sec)

测试计算

使用 SELECT 函数 或运算即可

测试now函数获取当前时间

1
2
3
4
5
6
7
mysql> SELECT NOW();
+---------------------+
| NOW() |
+---------------------+
| 2019-09-03 16:07:01 |
+---------------------+
1 row in set (0.00 sec)

测试 三乘五

1
2
3
4
5
6
7
mysql> SELECT 3*5;
+-----+
| 3*5 |
+-----+
| 15 |
+-----+
1 row in set (0.00 sec)

MySQL函数

sql是通用的,但是函数并不是通用的。以下介绍关于MySQL提供的函数

文本处理函数

下面列举一些关于MySQL处理文本的函数

Left() 返回字符串左边的字符

length() 返回字符串的长度

Locate() 找出字符串的一个子串

Lower() 将字符串转换为小写

LTrim() 去掉字符串左边的空格

Right() 返回字符串右边的字符

RTrim() 去掉字符串右边的空格

Soundex() 返回字符串的soundex值(用于声音模糊查询)

SubString() 返回字符串的字符

Upper() 将字符串转换为大写

Upper示例

1
2
3
4
5
6
7
8
9
10
11
12
mysql> SELECT vend_name,UPPER(vend_name) AS vend_name_upper FROM vendors ORDER BY vend_name;
+----------------+-----------------+
| vend_name | vend_name_upper |
+----------------+-----------------+
| ACME | ACME |
| Anvils R Us | ANVILS R US |
| Furball Inc. | FURBALL INC. |
| Jet Set | JET SET |
| Jouets Et Ours | JOUETS ET OURS |
| LT Supplies | LT SUPPLIES |
+----------------+-----------------+
6 rows in set (0.00 sec)

日期和时间处理函数

关于MySQL处理日期和时间的函数

AddDate() 添加一个日期

AddTime() 添加一个时间

CurDate() 返回当前日期

CurTime() 返回当前时间

Date() 返回日期时间的日期部分

DateDiff() 计算两个日期之差

Date_Add() 高度灵活的日期运算函数

Date_Format 返回一个格式化的日期或时间串

Day() 返回一个日期的天数部分

DayOfWeek() 对于一个日期返回对应的星期

Hour() 返回时间中的小时

Minute() 返回时间中的分钟

Month() 返回一个日期的月份部分

Now() 返回当前日期和时间

Second() 返回一个时间的秒

Time() 返回日期时间的时间部分

Year() 返回一个日期的年份部分

Year示例

1
2
3
4
5
6
7
8
9
10
11
mysql> SELECT order_date,Year(order_date) AS order_date_year From orders ORDER BY order_date_year;
+---------------------+-----------------+
| order_date | order_date_year |
+---------------------+-----------------+
| 2005-09-01 00:00:00 | 2005 |
| 2005-09-12 00:00:00 | 2005 |
| 2005-09-30 00:00:00 | 2005 |
| 2005-10-03 00:00:00 | 2005 |
| 2005-10-08 00:00:00 | 2005 |
+---------------------+-----------------+
5 rows in set (0.00 sec)

MySQL中的数值处理函数

Abs() 返回一个数的绝对值

Cos() 返回一个角度的余弦

Exp() 返回一个数的指数值

Mod() 返回操作的余数

Pi() 返回圆周率

Rand() 返回一个随机数

Sin() 返回一个角度的正弦

Sqrt() 返回一个数的平方根

Tan() 返回一个角度的正切

汇总数据

聚集函数

AVG

AVG() 返回某列的平均值

案例:获取所有商品的平均价格

1
2
3
4
5
6
7
mysql> SELECT AVG(prod_price) AS prod_price_avg FROM products;
+----------------+
| prod_price_avg |
+----------------+
| 16.133571 |
+----------------+
1 row in set (0.00 sec)

COUNT

COUNT() 统计表中的行数,参数(parameter)为*时代表所有列null也会被统计。当指定特定的列时只针对指定的列统计并且null不会计入统计。

案例:统计商品个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> SELECT COUNT(*) FROM customers;
+----------+
| COUNT(*) |
+----------+
| 5 |
+----------+
1 row in set (0.00 sec)

mysql> SELECT COUNT(cust_email) FROM customers;
+-------------------+
| COUNT(cust_email) |
+-------------------+
| 3 |
+-------------------+
1 row in set (0.00 sec)

MAX

MAX() 获取指定列中的最大值,必须传入指定的列。当遇到null会忽略

案例:获取商品的最大价格

1
2
3
4
5
6
7
mysql> SELECT MAX(prod_price) AS max_price FROM products;
+-----------+
| max_price |
+-----------+
| 55.00 |
+-----------+
1 row in set (0.00 sec)

MIN

MIN() 与MAX相反获取列中的最小值。注意事项一致。

案例:获取商品的最小值

1
2
3
4
5
6
7
mysql> SELECT MIN(prod_price) AS min_price FROM products;
+-----------+
| min_price |
+-----------+
| 2.50 |
+-----------+
1 row in set (0.00 sec)

SUN

SUN() 求和函数。null被忽略

案例:获取20005订单的商品的数量

1
2
3
4
5
6
7
mysql> SELECT SUM(quantity) AS items_ordered FROM orderitems WHERE order_num = 20005;
+---------------+
| items_ordered |
+---------------+
| 19 |
+---------------+
1 row in set (0.00 sec)

聚集函数中DISTINCT和All

聚集函数默认取值范围是all(指定列的所有值)我们可以在上面的几个聚集函数中使用DISTINCT去重后进行聚合计算

上面的5个函数中MAX和MIN使用DISTINCT无意义。因为我们要获取的是最大值或最小值

DISTINCT不能用于COUNT(*),因为不允许使用COUNT(DISTINCT)。类似的DISTINCT必须使用列名。

案例:获取不同商品价格的平均值

1
2
3
4
5
6
7
mysql> SELECT AVG(DISTINCT prod_price) AS avg_price FROM products;
+-----------+
| avg_price |
+-----------+
| 17.780833 |
+-----------+
1 row in set (0.00 sec)

组合聚合函数

案例:获取所有商品数量,商品的最大值、最小值以及商品的平均值

1
2
3
4
5
6
7
mysql> SELECT COUNT(*) AS num_item,MAX(prod_price) AS max_price,MIN(prod_price) AS min_price,AVG(prod_price) AS avg_price FROM products;
+----------+-----------+-----------+-----------+
| num_item | max_price | min_price | avg_price |
+----------+-----------+-----------+-----------+
| 14 | 55.00 | 2.50 | 16.133571 |
+----------+-----------+-----------+-----------+
1 row in set (0.00 sec)

分组数据

数据分组

数据为什么要分组,当遇到求每组每种……的需求时我们可以考虑使用分组。

使用group by可以实现分组声明。同时下面列举一些关于分组的规定

1.GROUP BY 分组后,数据将在最后规定的分组上进行汇总。也就是说分组后不能从个别的列中获取数据

2.GROUP BY子句中列出的每列都必须是检索或有效的表达式(不能是聚集函数)。如果在SELECT中使用表达式,则必须在GROUP BY子句中指定相同的表达式。

3.除聚集计算外,SELECT语句中的每个列必须在GROUP BY字句中给出

4.如果分组中存在null值,则将null作为一个分组返回。如果列中存在多行null值,它们将分为一组。

5.GROUP BY 语法顺序在 WHERE之后ORDER BY之前。

案例:获取每个厂商的商品个数

1
2
3
4
5
6
7
8
9
10
mysql> SELECT vend_id,COUNT(*) FROM products GROUP BY vend_id ORDER BY vend_id;
+---------+----------+
| vend_id | COUNT(*) |
+---------+----------+
| 1001 | 3 |
| 1002 | 2 |
| 1003 | 7 |
| 1005 | 2 |
+---------+----------+
4 rows in set (0.00 sec)

过滤分组

就是在分组的前提下过滤数据,可使用HAVING完成过滤

案例:获取每个厂商商品数量,厂商商品数量小于2过滤掉。

1
2
3
4
5
6
7
8
9
10
mysql> SELECT vend_id,COUNT(*) AS prod_num FROM products GROUP BY vend_id HAVING prod_num >=2;
+---------+----------+
| vend_id | prod_num |
+---------+----------+
| 1001 | 3 |
| 1002 | 2 |
| 1003 | 7 |
| 1005 | 2 |
+---------+----------+
4 rows in set (0.00 sec)

过滤分组+排序

案例:获取每个厂商商品数量,厂商商品数量小于2过滤掉。最后按商品数量的升序排列

1
2
3
4
5
6
7
8
9
10
mysql> SELECT vend_id,COUNT(*) AS prod_num FROM products GROUP BY vend_id HAVING prod_num >=2 ORDER BY prod_num;
+---------+----------+
| vend_id | prod_num |
+---------+----------+
| 1002 | 2 |
| 1005 | 2 |
| 1001 | 3 |
| 1003 | 7 |
+---------+----------+
4 rows in set (0.01 sec)

行过滤+分组过滤+排序

案例:获取每个厂商商品数量,过滤掉商品价格小于10的商品,厂商商品数量小于2也过滤掉。最后按商品数量的升序排列

1
2
3
4
5
6
7
8
mysql> SELECT vend_id,COUNT(*) AS prod_num FROM products WHERE prod_price >=10  GROUP BY vend_id HAVING prod_num >=2 ORDER BY prod_num;
+---------+----------+
| vend_id | prod_num |
+---------+----------+
| 1005 | 2 |
| 1003 | 4 |
+---------+----------+
2 rows in set (0.00 sec)

WHERE GROUP BY HAVING ORDER BY 各自的作用

WHERE 用于行过滤

GROUP BY 用于分组说明

HAVING 用于分组过滤

ORDER BY 用于输出排序

子查询

首先子查询是MySQL4.1引入的特性。

子查询可以组合使用

子查询的效率不高,尽量少用

下面列举2个例子

1.列出订购物品TNT2的所有客户信息

1
2
3
4
5
6
7
8
9
10

mysql> SELECT cust_name,cust_contact FROM customers WHERE cust_id IN (SELECT cust_id FROM orders WHERE order_num IN (SELECT order_num FROM orderitems WHERE prod_id = 'TNT2'))
-> ;
+----------------+--------------+
| cust_name | cust_contact |
+----------------+--------------+
| Coyote Inc. | Y Lee |
| Yosemite Place | Y Sam |
+----------------+--------------+
2 rows in set (0.00 sec)

2.获取customers表中每个顾客的订单总数

1
2
3
4
5
6
7
8
9
10
11
mysql> SELECT cust_name,cust_state,(SELECT COUNT(*) FROM orders WHERE customers.cust_id = orders.cust_id) AS orders FROM customers ORDER BY cust_name;
+----------------+------------+--------+
| cust_name | cust_state | orders |
+----------------+------------+--------+
| Coyote Inc. | MI | 2 |
| E Fudd | IL | 1 |
| Mouse House | OH | 0 |
| Wascals | IN | 1 |
| Yosemite Place | AZ | 1 |
+----------------+------------+--------+
5 rows in set (0.00 sec)

使用子查询的诀窍是分步骤循序渐进的运行(编写)sql

联结表

联结表就是将一些关系表联结起来,获取数据。

关系表

关系表简单说就是2张表之间存在关联性。它具有以下优点

1.因为每张表都是根据其特性建立的,所以它不允许出现重复的字段。复用性强

2.可以细粒度的利用字段进行查询过滤

3.操作简单,一次操作多次引用

联结案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> SELECT vend_name,prod_name,prod_price FROM vendors,products WHERE vendors.vend_id = products.vend_id ORDER BY vend_name,prod_name;
+-------------+----------------+------------+
| vend_name | prod_name | prod_price |
+-------------+----------------+------------+
| ACME | Bird seed | 10.00 |
| ACME | Carrots | 2.50 |
| ACME | Detonator | 13.00 |
| ACME | Safe | 50.00 |
| ACME | Sling | 4.49 |
| ACME | TNT (1 stick) | 2.50 |
| ACME | TNT (5 sticks) | 10.00 |
| Anvils R Us | .5 ton anvil | 5.99 |
| Anvils R Us | 1 ton anvil | 9.99 |
| Anvils R Us | 2 ton anvil | 14.99 |
| Jet Set | JetPack 1000 | 35.00 |
| Jet Set | JetPack 2000 | 55.00 |
| LT Supplies | Fuses | 3.42 |
| LT Supplies | Oil can | 8.99 |
+-------------+----------------+------------+
14 rows in set (0.00 sec)

内联结

笛卡尔集

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
mysql> SELECT vend_name,prod_name,prod_price FROM vendors,products ORDER BY vend_name,prod_name;
+----------------+----------------+------------+
| vend_name | prod_name | prod_price |
+----------------+----------------+------------+
| ACME | .5 ton anvil | 5.99 |
| ACME | 1 ton anvil | 9.99 |
| ACME | 2 ton anvil | 14.99 |
| ACME | Bird seed | 10.00 |
| ACME | Carrots | 2.50 |
| ACME | Detonator | 13.00 |
| ACME | Fuses | 3.42 |
| ACME | JetPack 1000 | 35.00 |
| ACME | JetPack 2000 | 55.00 |
| ACME | Oil can | 8.99 |
| ACME | Safe | 50.00 |
| ACME | Sling | 4.49 |
| ACME | TNT (1 stick) | 2.50 |
| ACME | TNT (5 sticks) | 10.00 |
| Anvils R Us | .5 ton anvil | 5.99 |
| Anvils R Us | 1 ton anvil | 9.99 |
| Anvils R Us | 2 ton anvil | 14.99 |
| Anvils R Us | Bird seed | 10.00 |
| Anvils R Us | Carrots | 2.50 |
| Anvils R Us | Detonator | 13.00 |
| Anvils R Us | Fuses | 3.42 |
| Anvils R Us | JetPack 1000 | 35.00 |
| Anvils R Us | JetPack 2000 | 55.00 |
| Anvils R Us | Oil can | 8.99 |
| Anvils R Us | Safe | 50.00 |
| Anvils R Us | Sling | 4.49 |
| Anvils R Us | TNT (1 stick) | 2.50 |
| Anvils R Us | TNT (5 sticks) | 10.00 |
| Furball Inc. | .5 ton anvil | 5.99 |
| Furball Inc. | 1 ton anvil | 9.99 |
| Furball Inc. | 2 ton anvil | 14.99 |
| Furball Inc. | Bird seed | 10.00 |
| Furball Inc. | Carrots | 2.50 |
| Furball Inc. | Detonator | 13.00 |
| Furball Inc. | Fuses | 3.42 |
| Furball Inc. | JetPack 1000 | 35.00 |
| Furball Inc. | JetPack 2000 | 55.00 |
| Furball Inc. | Oil can | 8.99 |
| Furball Inc. | Safe | 50.00 |
| Furball Inc. | Sling | 4.49 |
| Furball Inc. | TNT (1 stick) | 2.50 |
| Furball Inc. | TNT (5 sticks) | 10.00 |
| Jet Set | .5 ton anvil | 5.99 |
| Jet Set | 1 ton anvil | 9.99 |
| Jet Set | 2 ton anvil | 14.99 |
| Jet Set | Bird seed | 10.00 |
| Jet Set | Carrots | 2.50 |
| Jet Set | Detonator | 13.00 |
| Jet Set | Fuses | 3.42 |
| Jet Set | JetPack 1000 | 35.00 |
| Jet Set | JetPack 2000 | 55.00 |
| Jet Set | Oil can | 8.99 |
| Jet Set | Safe | 50.00 |
| Jet Set | Sling | 4.49 |
| Jet Set | TNT (1 stick) | 2.50 |
| Jet Set | TNT (5 sticks) | 10.00 |
| Jouets Et Ours | .5 ton anvil | 5.99 |
| Jouets Et Ours | 1 ton anvil | 9.99 |
| Jouets Et Ours | 2 ton anvil | 14.99 |
| Jouets Et Ours | Bird seed | 10.00 |
| Jouets Et Ours | Carrots | 2.50 |
| Jouets Et Ours | Detonator | 13.00 |
| Jouets Et Ours | Fuses | 3.42 |
| Jouets Et Ours | JetPack 1000 | 35.00 |
| Jouets Et Ours | JetPack 2000 | 55.00 |
| Jouets Et Ours | Oil can | 8.99 |
| Jouets Et Ours | Safe | 50.00 |
| Jouets Et Ours | Sling | 4.49 |
| Jouets Et Ours | TNT (1 stick) | 2.50 |
| Jouets Et Ours | TNT (5 sticks) | 10.00 |
| LT Supplies | .5 ton anvil | 5.99 |
| LT Supplies | 1 ton anvil | 9.99 |
| LT Supplies | 2 ton anvil | 14.99 |
| LT Supplies | Bird seed | 10.00 |
| LT Supplies | Carrots | 2.50 |
| LT Supplies | Detonator | 13.00 |
| LT Supplies | Fuses | 3.42 |
| LT Supplies | JetPack 1000 | 35.00 |
| LT Supplies | JetPack 2000 | 55.00 |
| LT Supplies | Oil can | 8.99 |
| LT Supplies | Safe | 50.00 |
| LT Supplies | Sling | 4.49 |
| LT Supplies | TNT (1 stick) | 2.50 |
| LT Supplies | TNT (5 sticks) | 10.00 |
+----------------+----------------+------------+
84 rows in set (0.00 sec)

inner join(ANSI SQL语法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> SELECT vend_name,prod_name,prod_price FROM vendors INNER JOIN products ON vendors.vend_id = products.vend_id;
+-------------+----------------+------------+
| vend_name | prod_name | prod_price |
+-------------+----------------+------------+
| Anvils R Us | .5 ton anvil | 5.99 |
| Anvils R Us | 1 ton anvil | 9.99 |
| Anvils R Us | 2 ton anvil | 14.99 |
| LT Supplies | Fuses | 3.42 |
| LT Supplies | Oil can | 8.99 |
| ACME | Detonator | 13.00 |
| ACME | Bird seed | 10.00 |
| ACME | Carrots | 2.50 |
| ACME | Safe | 50.00 |
| ACME | Sling | 4.49 |
| ACME | TNT (1 stick) | 2.50 |
| ACME | TNT (5 sticks) | 10.00 |
| Jet Set | JetPack 1000 | 35.00 |
| Jet Set | JetPack 2000 | 55.00 |
+-------------+----------------+------------+
14 rows in set (0.00 sec)

当两个表中vend_id都同时存在并对应的数据

性能问题

联结会影响数据库性能,所以使用联结时要尽量避免出现不必要的表联结。

高级联结

不同类型的联结

自联结

单个表使用别名被联结后获取数据的情况属于自联结

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> SELECT p1.prod_id,p1.prod_name FROM products as p1 INNER JOIN products as p2 ON p1.vend_id = p2.vend_id WHERE p2.prod_id = 'DTNTR' ORDER BY p1.prod_name;
+---------+----------------+
| prod_id | prod_name |
+---------+----------------+
| FB | Bird seed |
| FC | Carrots |
| DTNTR | Detonator |
| SAFE | Safe |
| SLING | Sling |
| TNT1 | TNT (1 stick) |
| TNT2 | TNT (5 sticks) |
+---------+----------------+
7 rows in set (0.00 sec)

自联结的好处是,它可以替代子查询完成相同的操作。并且比子查询性能要高

自然联结

明确联结后检索的列,并且不重复的的情况属于自然联结

之前的大多数联结案例都属于自然联结

外部联结

外部联结分为做联结和右联结

使用联结的注意事项

1.联结的时候,联结类型要考虑清楚

2.联结条件很重要,所以也需要深思。不提供条件的话会出现笛卡尔集

组合查询

组合查询就是将多条sql查询组合后使用。前提是这两条sql语句检索的列必须一致。

创建组合查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> SELECT vend_id,prod_id,prod_price FROM products WHERE prod_price <=5 UNION SELECT vend_id,prod_id,prod_price FROM products WHERE vend_id IN (1001,1002);
+---------+---------+------------+
| vend_id | prod_id | prod_price |
+---------+---------+------------+
| 1003 | FC | 2.50 |
| 1002 | FU1 | 3.42 |
| 1003 | SLING | 4.49 |
| 1003 | TNT1 | 2.50 |
| 1001 | ANV01 | 5.99 |
| 1001 | ANV02 | 9.99 |
| 1001 | ANV03 | 14.99 |
| 1002 | OL1 | 8.99 |
+---------+---------+------------+
8 rows in set (0.00 sec)

组合查询使用注意事项

  1. 组合查询中SELECT最少为2条

  2. 组合查询中每条查询语句中检索的列必须相同,顺序可以不一样。数据类型是数据库支持的类型

  3. 组合查询ORDER by要用在最后.

  4. 组合查询会自动过滤重复的数据。如果不想被过滤可以使用 union all 如下面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> SELECT vend_id,prod_id,prod_price FROM products WHERE prod_price <=5 UNION ALL SELECT vend_id,prod_id,prod_price FROM products WHERE vend_id IN (1001,1002);
+---------+---------+------------+
| vend_id | prod_id | prod_price |
+---------+---------+------------+
| 1003 | FC | 2.50 |
| 1002 | FU1 | 3.42 |
| 1003 | SLING | 4.49 |
| 1003 | TNT1 | 2.50 |
| 1001 | ANV01 | 5.99 |
| 1001 | ANV02 | 9.99 |
| 1001 | ANV03 | 14.99 |
| 1002 | FU1 | 3.42 |
| 1002 | OL1 | 8.99 |
+---------+---------+------------+
9 rows in set (0.00 sec)

全文本搜索

由于本章内容myISAM 引擎不支持中文所以不做笔记介绍

插入数据

插入完整一行

1
2
mysql> INSERT INTO customers VALUES(null,'Pep E. LaPew','100 Main Street','Los Angeles','CA','90046','USA',null,null);
Query OK, 1 row affected (0.04 sec)

提高性能降低insert的优先级

如果数据检索是最重要的,在insert into 将加入 low_priority可降低insert语句的优先级。这样别的用户的查询语句就有可能被优先执行。

1
insert low_priority into

插入多个行

1
2
3
mysql> INSERT INTO customers(cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country) values('Pep e. LaPew','100 Main Street','Los Angeles','CA','90046','USA'),('M.martian','42 Galaxy Way','New York','Ny','11213','USA');
Query OK, 2 rows affected (0.06 sec)
Records: 2 Duplicates: 0 Warnings: 0

插入检索后的行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
INSERT INTO customers(
cust_id,
cust_contact,
cust_email,
cust_name,
cust_address,
cust_city,
cust_country)
select
cust_id,
cust_contact,
cust_email,
cust_name,
cust_address,
cust_city,cust_country
from
custnew;

插入时的注意事项

  1. 插入数据时应该明确指定要插入的列

更新和删除数据

更新数据

1
2
3
mysql> UPDATE customers SET cust_email = 'elmer@fudd.com' WHERE cust_id = 10005;
Query OK, 1 row affected (0.09 sec)
Rows matched: 1 Changed: 1 Warnings: 0

IGNORE

如果update更新多行,并且更新中如果一行或多行出现错误时,会取消整个update操作。使用ignore可以忽略错误行完成修改更新。(谨慎使用,个人认为它打破了sql执行时数据的一致性)

删除数据

1
2
mysql> DELETE FROM customers Where cust_id = 10006;
Query OK, 1 row affected (0.06 sec)

删除表内所有数据

1
2
mysql> truncate table custnew1;
Query OK, 0 rows affected (0.24 sec)

update 和 delete使用注意事项

  1. 更新或删除数据时要考虑where条件,除非操作是针对全表内容的

  2. 更新或删除的where条件,操作前要在SELECT 中查询并确认

Linux-权限整合篇

发表于 2021-12-06 | 更新于 2019-08-05

基本权限管理

chmod

基本权限管理使用 chmod 命令

如给一个文件设置属主,所属组,其他的权限如下所示

1
2
3
4
spiderbao@spiderbao-CW65S:~/桌面$ touch test.txt
spiderbao@spiderbao-CW65S:~/桌面$ chmod 774 test.txt
spiderbao@spiderbao-CW65S:~/桌面$ ll test.txt
-rwxrwxr-- 1 spiderbao spiderbao 0 8月 5 16:29 test.txt*

给文件夹赋予权限同上所示,上面使用数字的方式给权限赋值,4表示读 ,2表示修改,1表示执行

chown

chown 命令可以修改文件的所有者以及所属组

案例:

1
2
3
4
5
6
#修改文件的所属组
spiderbao@spiderbao-CW65S:~/桌面$ ll test.txt
-rwxrwxr-- 1 spiderbao spiderbao 0 8月 5 16:29 test.txt*
spiderbao@spiderbao-CW65S:~/桌面$ sudo chown spiderbao.ftp test.txt
spiderbao@spiderbao-CW65S:~/桌面$ ll test.txt
-rwxrwxr-- 1 spiderbao ftp 0 8月 5 16:29 test.txt*

umask默认权限管理

查看系统默认umask

1
2
3
4
5
spiderbao@spiderbao-CW65S:~/桌面$ umask -S
u=rwx,g=rx,o=rx

spiderbao@spiderbao-CW65S:~/桌面$ umask
0022

解析 umaks 0022 代表什么

其中

第一位 特殊权限位,如果是1表示文件具有SBIT权限,2表示具有SetGID权限,4表示具有SetUID权限,0表示站位符

第二位 所属主位,4 (read),2 (write),1(execute)

第三位 所属组位,4 (read),2 (write),1(execute)

第四位 其他位,4 (read),2 (write),1(execute)

默认权限算法

文件夹的默认最大权限 777
文件的最大默认权限 666

文件的默认值是根据 文件的最大权限值与umask设置的值进行或得出的。

666 表示 rw-rw-rw- 022 表示 —-w–w- 进行或运算后得 rw r r 所以这个就是系统中创建文件后的默认权限值

ACL 权限管理

ACL 的全称是 Access Control List (访问控制列表)

它可以针对文件设置除基本权限外的 r w x的权限

举例,现在有一个文件test 是开发小组1共同需要操作所以小组 test拥有 rw的权限,同时文件的创建者拥有rw权限,其他人无权查看test文件的内容,有一天来了以为实习生,由于他刚来不久所以需要拥有test的r权限但是不能拥有w权限这时改基本权限又不太现实。那现在怎么办呢?看看接下来我用ACL权限是如何解决的吧


SetUID、SetGID、SBIT 权限管理

chatter 权限

sudo 权限

JNDI-转载

发表于 2021-12-06 | 更新于 2020-01-07

JNDI是 Java 命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一,不少专家认为,没有透彻理解JNDI的意义和作用,就没有真正掌握J2EE特别是EJB的知识。
那么,JNDI到底起什么作用?

要了解JNDI的作用,我们可以从“如果不用JNDI我们怎样做?用了JNDI后我们又将怎样做?”这个问题来探讨。

没有JNDI的做法:
程序员开发时,知道要开发访问MySQL数据库的应用,于是将一个对 MySQL JDBC 驱动程序类的引用进行了编码,并通过使用适当的 JDBC URL 连接到数据库。
就像以下代码这样:

Java code
Connection conn=null;
try {
Class.forName(“com.mysql.jdbc.Driver”, true, Thread.currentThread().getContextClassLoader());
conn=DriverManager.getConnection(“jdbc:mysql://MyDBServer?user=xxx&password=xxx”);
……
conn.close();
} catch(Exception e) {
e.printStackTrace();
} finally {
if(conn!=null) {
try {
conn.close();
} catch(SQLException e) {}
}
}

这是传统的做法,也是以前非Java程序员(如Delphi、VB等)常见的做法。这种做法一般在小规模的开发过程中不会产生问题,只要程序员熟悉Java语言、了解JDBC技术和MySQL,可以很快开发出相应的应用程序。

没有JNDI的做法存在的问题:
1、数据库服务器名称MyDBServer 、用户名和口令都可能需要改变,由此引发JDBC URL需要修改;
2、数据库可能改用别的产品,如改用DB2或者Oracle,引发JDBC驱动程序包和类名需要修改;
3、随着实际使用终端的增加,原配置的连接池参数可能需要调整;
4、……

解决办法:
程序员应该不需要关心“具体的数据库后台是什么?JDBC驱动程序是什么?JDBC URL格式是什么?访问数据库的用户名和口令是什么?”等等这些问题,程序员编写的程序应该没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令 —— 甚至没有数据库池或连接管理。而是把这些问题交给J2EE容器来配置和管理,程序员只需要对这些配置和管理进行引用即可。

由此,就有了JNDI。

用了JNDI之后的做法:
首先,在在J2EE容器中配置JNDI参数,定义一个数据源,也就是JDBC引用参数,给这个数据源设置一个名称;然后,在程序中,通过数据源名称引用数据源从而访问后台数据库。
具体操作如下(以JBoss为例):
1、配置数据源
在JBoss 的 D:/jboss420GA/docs/examples/jca 文件夹下面,有很多不同数据库引用的数据源定义模板。将其中的 mysql-ds.xml 文件Copy到你使用的服务器下,如 D:/jboss420GA/server/default/deploy。
修改 mysql-ds.xml 文件的内容,使之能通过JDBC正确访问你的MySQL数据库,如下:

<?xml version=”1.0” encoding=”UTF-8”?>



MySqlDS
jdbc:mysql://localhost:3306/lw
com.mysql.jdbc.Driver
root
rootpassword
org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter

mySQL


这里,定义了一个名为MySqlDS的数据源,其参数包括JDBC的URL,驱动类名,用户名及密码等。

2、在程序中引用数据源:
Java code
Connection conn=null;
try {
Context ctx = new InitialContext();
Object datasourceRef = ctx.lookup(“java:MySqlDS”);
//引用数据源
DataSource ds = (Datasource) datasourceRef;
conn = ds.getConnection();
……
c.close();
} catch(Exception e) {
e.printStackTrace();
} finally {
if(conn!=null) {
try {
conn.close();
} catch(SQLException e) {}
}
}

直接使用JDBC或者通过JNDI引用数据源的编程代码量相差无几,但是现在的程序可以不用关心具体JDBC参数了。
在系统部署后,如果数据库的相关参数变更,只需要重新配置 mysql-ds.xml 修改其中的JDBC参数,只要保证数据源的名称不变,那么程序源代码就无需修改。

由此可见,JNDI避免了程序与数据库之间的紧耦合,使应用更加易于配置、易于部署。

总结:
J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。JNDI 在 J2EE 中的角色就是“交换机” —— J2EE 组件在运行时间接地查找其他组件、资源或服务的通用机制。在多数情况下,提供 JNDI 供应者的容器可以充当有限的数据存储,这样管理员就可以设置应用程序的执行属性,并让其他应用程序引用这些属性(Java 管理扩展(Java Management Extensions,JMX)也可以用作这个目的)。JNDI 在 J2EE 应用程序中的主要角色就是提供间接层,这样组件就可以发现所需要的资源,而不用了解这些间接性。

在 J2EE 中,JNDI 是把 J2EE 应用程序合在一起的粘合剂,JNDI 提供的间接寻址允许跨企业交付可伸缩的、功能强大且很灵活的应用程序。这是 J2EE 的承诺,而且经过一些计划和预先考虑,这个承诺是完全可以实现的。

说白了就是把资源取个名字,再根据名字来找资源。

//===============================================================================================================
首先我们来回顾一下简单的问题,列在下面第一点。
1.我们知道,Java 的运行从 static main 开始,为什么一定要从 static 方法开始呢?
2.在我们知道这个世界上的另外一个地方有一个对象存在而且服务器也会在我们开始工作前为我们准备好,那么我该怎么找到它呢?如果这个对象是我这个类创建的,那么当然简单,直接用对象的引用就能调用它的方法,那如果这个对象不是我创建的,我想主动调用它的方法这似乎在任何编程语言中都不可能,记得写一个方法那是被别人调用的不是主动调用别人。

就像你找人一样,如果他还没有和你建立联系的话(建立联系就是保存一份对象的引用,如果两个对象彼此没有创建另外一个而且也没有被中间的第三方建立这种关系问题就出现了),请问你如何和他打交道?
现实中是:
a. 我们拨通 114
Java 中:Context ctx = new InitialContext();
b. 请问哪里有通马桶的?114 答,xxx… 为您转接中,请稍候。
Java 中:DataSource ds = (DataSource) ctx.lookup(“便民服务公司”);
c. 过了一会儿,人来了,你说:师付,请帮我通马桶吧。
Java 中: ds.getConnection();

上面的话,我没有回答你什么是 JNDI, 但是我回答了为什么我们需要 JNDI. 希望你在概念上了解了它存在的必要性。

下面的话,给你一点指导如何更好的理解 JNDI 实现:
1.一个对象如果它在另外一个地方(可能与当前运行的程序不在同一个 VM / 同一进程中), 对象怎么可能从一个 VM 中发送到另外一个 VM 中呢?像 LDAP 这种,对象的状态还需要持久地保存的话(重启服务器进程后它还在),又该怎么办呢?请看 JNDI StateFactory, 它用一种方法把一个对象转换成某种方式保存下来,就像我们把一个 Entity 对象保存下来时,我们会用 SQL 来做一样。

2.有一个对象上次已经保存了状态,现在服务器重启了,上次的对象肯定不在内存里面,我们怎么恢复上次的状态呢?
请看 JNDI ObjectFactory. 它读取一些上次保存的状态信息,来创建并初始化一个对象。比如:我们配置了一个 XML,它是某个 JDBC 数据源的配置数据,Application Server 启动时读取这个信息(相当于上次的状态),然后重启对象。

3.企业应用这么复杂,面向接口编程,那如何用一种简单的方式来配置新的实现类呢?Java 的做法是:
已经定义了 SPI (Service Provider Interface). 包括以下几点:
接口准备好了,如:StateFactory / ObjectFactory.
配置:先搜索 JRE 下面的某个 jndiprovider.properties 文件当作默认实现,再查找用户 classpath 根路径下 /jndi.properties. 另外还有 System.getProperties() 和在创建 InitialContext 给一个 hashtable 作为参数,这三个参数, 有优先级的关系,越是后面具体的参数优先级越高,越前面越通用型的参数优先级越低。这一点,请看 JDK ResourceManager 这个类的源码。
实现类与初始化它们是如何自动完成的呢?这个你需要看 Context 接口里面的常量,以及拿 Sun LDAP InitialContextFactory 运行样例来看 Context 接口的常量一个样本参数值,一般我们很重要的是 InitialContextFactory 这个参数,但也有时候也有其他参数要配置,比如:pkgs, 它是说,我们给一个包名,JNDI 管理器要查找实现时用这个包名列表当成包名,类名就是 协议名 + 固定的后缀:比如: ldap://localhost:389, 它会用一个’包名前缀.协议名.协议名 + URLContextFactory’ 作为类名来搜索一个类,如果它存在就把它当成实现类,如果没找到再尝试另外一个包名前缀。你可以看 com.sun.jndi.url 名,下面有例子看,比如说 ldap:// 的情况就是 找一个类 com.sun.jndi.url.ldap.ldapURLContextFactory,如果是 dns://www.163.com/xxx 就找个 com.sun.jndi.url.dns.dnsURLContextFactory。这是 URL context factory 也就是当你使用 ctx.lookup(“java:xxxx/yyy”) 这种带协议前缀的时候。

另外你也可以类比地看 com.sun.www.protocol 包里面的类,它是另外不一个与 JNDI 不相关的 URLStreamHandler 处理的规则,与些设计和配置几乎完全相同。我以前写过一个 jdbc:oracle:username/password:@localhost:1521:training/[select A from C where DEL_IND = 0] , 在 java 程序中输入这个 URL 我们可以把数据库里面的数据读取出来,效果就根你输入 file:/C:/boot.ini 读取了这个文件内容一样,办法就是我写了一个支持 jdbc 协议的 URLStreamHandler 在命令行配置一个使用它,其他的应用程序类就能自动处理,它们都不知道我是从数据库里面读取的数据。

  1. J2EE 1.3 开始,资源的管理由应用服务器单独来管理和配置,这与 J2EE 1.2 不同,在 J2EE 1.2 中我们直接在应用程序中配置我们要用的资源。J2EE 1.3 中我们配置一个数据源在服务器上,我们在应用程序中只需要说明我们配置的资源的引用就行了,比如我们只在 web.xml 或 ejb-jar.xml 配置 而不是 data source 本身。这有什么好处?比如:我们定义了两个 training 的数据源:jdbc/training/db2. jdbc/oracle/db2. 一个是开发环境,一个是 UAT 环境,现在开发时我们建立一个 指向jdbc/training/db2,那么就用 db2 数据库,UAT 测试时我们建立另外一个 指向 jdbc/training/oracle, 就会使用 oracle 数据库,而这本身不需要修改代码,只是修改了 web.xml / ejb-jar.xml ,而且现在连接到数据的用户名和密码不再是应用程序开发本身的事情,因为你不需要配置资源也就不需要知道它的登录名和密码,而是由管理员在服务器上配置数据源,这里注意,开发人员做他代码部分的事情,服务器管理员负责配置资源源,J2EE component provider 和 Deployer 两个角色的职责分开了,虽然现实中 deployer 都是委托给了开发人员,但 J2EE 规范是分开来描述的。

5.上面说了半天,目的是什么呢?这是我的痛苦经历,第一次写 EJB, 买了本书,J2EE 从入门到精通(就是那本传说中的黃皮宝典系列),写了一个无状态 session bean 来访问数据源,死活找不到数据源:NamingException: xxx not found. 在 IBM developerworks 上看到一篇文章,茅塞顿开,原来那本书讲的是J2EE 1.2, 我用的 WSAD 5.1.2 开发用的默认配置都是 J2EE 1.3。这里面引出了 JNDI LinkRef, 为了实现上面 4 里面所说的服务器上配置一个资源,但应用程序里面配置一个引用的话,现在的应用服务器在处理这点JNDI技术实现上基本上都是用 LinkRef 来实现的,这是 JNDI 里面的一个类。服务器启动时会创建一个 jdbc/training/db2 和 jdbc/training/oracle 两个 DataSource 对象 (用的是 ObjectFactory), 当一个应用程序访问了准备访问数据源时,服务器检测到了 web.xml/ejb-jar.xml 中指定了 它就会创建一个 LinkRef 放到 context 中去,它的名称是:jdbc/training,但它的 ref 是 jdbc/training/db2.
这样我们 ctx.lookup(“java:jdbc/training”) 时,java 协议对应的 javaURLContextFactory 会把这个 jdbc/training 对象找出来,在检测到它是一个 LinkRef 对象时,会自动再用它的 ref 值(这里是 jdbc/training/db2) 再 lookup 一遍,这下终于找到 jdbc/training/db2 这个 data source 对象。

6.JNDI 里面还有其他的相关的东西。再结合一个 Reference 概念看,LinkRef 是继承它的。想再具体的了解一个实现细节,请拿一份 apache commons-xxx.jar (名字我忘记了,不过用过 spring / hibernate 来创建数据源的人可能知道它们用 xxxDataSource 做一个不需要在服务器上配置,但却能使用 data source 的办法),我不是推荐你这个 jar, 我是推荐你看这个xxxDataSource 源码,里面演示了一个 ObjectFactory 用法。这和 JMS ConnectionFactory等其他 J2EE 托管资源的配置和使用都是用的同样的技术实现的。举一反三。
想了解更,就再看一个 StateFactory 的实现以及 Reference 的源码之类的。作为期望迈入 J2EE 中级编程的你,至少在概念和理论上要知道 ObjectFactory / LinkRef / SPI / resource ref 配置这几点,如果你再知道 StateFactory 是怎么实现的就更好了。

转载自https://blog.csdn.net/wn084/article/details/80729230

Ubuntu系统

发表于 2021-12-06 | 更新于 2020-04-21

自安装程序的图标显示

进入 ~/.local/share/applications/目录,在此目录下创建一个程序名称.desktop的文件可以在应用显示程序中加入此程序

example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(base) spiderbao@spiderbao-CW65S:~$ cd ~/.local/share/applications/
(base) spiderbao@spiderbao-CW65S:~/.local/share/applications$ ls
Anaconda.desktop jetbrains-idea-1.desktop jetbrains-idea.desktop jetbrains-pycharm.desktop jetbrains-toolbox.desktop jetbrains-webstorm.desktop mimeapps.list mimeinfo.cache Typora.desktop
(base) spiderbao@spiderbao-CW65S:~/.local/share/applications$
(base) spiderbao@spiderbao-CW65S:~/.local/share/applications$ cat Anaconda.desktop
[Desktop Entry]
Encoding=UTF-8
Name=Anaconda
Comment=Anaconda
Exec=/home/spiderbao/anaconda3/bin/anaconda-navigator
Icon=anaconda_icon_128x128
Terminal=false
Type=Application
Categories=GNOME;GTK;Utility;TextEditor;
Name[zh_CN]=anaconda

icon的位置 在/usr/share/pixmaps下

example:

1
2
3
4
5
6
7
(base) spiderbao@spiderbao-CW65S:/usr/share/pixmaps$ pwd
/usr/share/pixmaps
(base) spiderbao@spiderbao-CW65S:/usr/share/pixmaps$ ls -l an
anaconda_icon_128x128.png anki.png anki.xpm
(base) spiderbao@spiderbao-CW65S:/usr/share/pixmaps$ ls -l anaconda_icon_128x128.png
-rw-rw-r-- 1 spiderbao spiderbao 4212 4月 21 09:30 anaconda_icon_128x128.png
(base) spiderbao@spiderbao-CW65S:/usr/share/pixmaps$

ubuntu 下的软件管理

发表于 2021-12-06 | 更新于 2019-05-15

概念部分

Ubuntu软件包管理概述

Ubuntu Linux采用了Debian的软件包管理机制。由于软件包具有易用性、灵活性和扩展性的特点,再加上Internet的支持,使用户随时都能拥有最新的 Ubuntu系统,这也是Ubuntu受到推崇的一个重要原因。因而,Deb软件包管理也成为Ubuntu中最有活力的部分。本章介绍Ubuntu软件包 管理和dpkg软件包管理器。

Deb软件包概述

Deb软件包本质上是文件包,这点类似于tar文件将多个文件合并为一个归档文件。但是Deb的身价不在于整合文件,而在于使应用程序更易于传播。

当年流行的两种软件包管理机制

最初,基于Linux系统的开发者在完成应用程序开 发后,将很多二进制文件发给用户,用户使用之前需要将相关程序逐个安装。因此,Debian Linux首先提出“软件包”的管理机制——Deb软件包,将应用程序的二进制文件、配置文档、man/info帮助页面等文件合并打包在一个文件中,用 户使用软件包管理器直接操作软件包,完成获取、安装、卸载、查询等操作。

随即,Redhat Linux基于这个理念推出了自己的软件包管理机制——Rpm软件包。当然,Redhat Linux采用了自己的打包格式生成Rpm包文件,由Rpm包管理器负责安装、维护、查询,甚至软件包版本管理。由于Redhat Linux系统的普及,Rpm软件包被广泛使用,甚至出现第三方开发的软件管理工具,专门管理Rpm格式的软件包。

随着Linux操作系统规模的不断扩大,系统中软件 包之间复杂的依赖关系,导致Linux用户麻烦不断。为了解决这个问题,Debian Linux开发出了APT软件包管理器。它能够自动检查和修复软件包之间的依赖关系。并且,利用Internet网络带来的快捷的连通手段,APT工具可 以帮助用户主动获取软件包。因此,APT工具再次促进了Deb软件包更为广泛地使用,成为Debian Linux的一个无法替代的亮点。

Ubuntu Linux系统的软件包管理机制延续了Debian的包管理方法。

软件包的类型

Debian包文件包含了二进制可执行文件、库文件、配置文件和man/info帮助页面等文档。通常Debian包文件的后缀为.deb,因此称为“Deb软件包”。Ubuntu有两种类型的软件包:二进制软件包(deb)和源码包(deb-src)。

  ●    二进制软件包(Binary Packages):包含可执行文件、库文件、配置文件、man/info页面、版权声明和其他文档。

  ●    源码包(Source Packages):包含软件源代码、版本修改说明、构建指令以及编译工具等。先由tar工具归档为.tar.gz文件,然后再打包成.dsc文件。

用户不确定一个软件包具体类型时,可以使用file命令查看文件类型。例如下面命令用于证实一个软件包的文件类型是否是Deb软件包文件。

wdl@UbuntuFisher:~$ file  g++_4.1.2-9ubuntu2_i386.deb

g++_4.1.2-9ubuntu2_i386: Debian binary package (format 2.0)

在Ubuntu Linux中,需要说明一个概念——虚拟软件包。将系统中具有相同或相近功能的多个软件包作为一个软件包集合,称为虚拟软件包,并指定其中一个软件包作为 虚拟软件包的默认首选项。提出虚拟软件的意图就是为了防止软件安装过程中发生冲突。例如,exim、sendmail和postfix软件包都是用于邮件 传输代理,将“mail-transport-agent”指定为它们的虚拟软件包。当用户安装“mail-transport-agent”时,将选择 安装exim、sendmail和postfix其中的首选项。

软件包的命名

在Ubuntu Linux中,软件包的命名遵循以下约定:

Filename_Version-Reversion_Architecture.deb

其中,Filename表示软件包文件名,Version表示软件版本号,Reversion表示修订版本号,Architecture表示适用计算机架构。通常,修订版本号是由Ubuntu开发者或创建这个软件包的人指定。在软件包被修改过之后,将修改版本号加1。

以g++_4.1.2-9ubuntu2_i386.deb软件包为例,g++是软件包名,4.1.2是软件版本号,9ubuntu2是修订版本号,i386是适用的计算机架构。

配置部分

Ubuntu 方便宜用,最值得让人称道的便是其安装软件的方式, 一条命令: sudo apt-get install xxx 就几乎能帮你搞定所有的软件安装难题。但是有时你可能有这样的需求,查看某个软件包是否安装、安装在哪..., 那我们就来介绍一下 Ubuntu 的软件包管理方式。

一、Ubuntu 采用 Debian 的软件包管理器 dpkg 来管理软件包, 类似 RPM. 系统中所有 packages 的信息都在 /var/lib/dpkg/
目录下, 其子目录 /var/lib/dpkg/info 用于保存各个软件包的配置文件列表:
 (1).conffiles 记录了软件包的配置文件列表
 (2).list 保存软件包中的文件列表, 用户可以从 .list 的信息中找到软件包中文件的具体安装位置.
 (3).md5sums 记录了软件包的md5信息, 这个信息是用来进行包验证的.
 (4).prerm 脚本在 Debian 包解包之前运行, 主要作用是停止作用于即将升级的软件包的服务, 直到软件包安装或升级完成.
 (5).postinst 脚本是完成 Debian 包解开之后的配置工作, 通常用于执行所安装软件包相关命令和服务重新启动.

/var/lib/dpkg/available 文件的内容是软件包的描述信息, 该软件包括当前系统所使用的 Debian 安装源中的所有软件包,
其中包括当前系统中已安装的和未安装的软件包.

/var/cache/apt/archives 目录是在用 apt-get install 安装软件时,软件包的临时存放路径

/etc/apt/sources.list 存放的是软件源站点, 当你执行 sudo apt-get install xxx 时,Ubuntu 就去这些站点下载软件包到本地并执行安装

命令操作部分

apt和dpkg混合命令

(1)查看某软件包的安装内容
dpkg -L xxx

(2)查找软件库中的软件包
apt-cache search 正则表达式

(3)显示系统安装包的统计信息
apt-cache stats

(4)显示系统全部可用软件包的名称
apt-cache pkgnames

(5)显示某软件包的详细信息
apt-cache show xxx

(6)查找某文件属于哪个包
apt-file search xxx

(7)查看已经安装了哪些软件包
dpkg -l

(8)查询某软件依赖哪些软件包
apt-cache depends xxx

(9)查询某软件被哪些软件包依赖
apt-cache rdepends xxx

(10)增加一个光盘源
sudo apt-cdrom add
注: 顾名思义, 就是安装更新软件包时让其优先从Ubuntu 光盘上找(如果你不能上网安装/更新, 但有 Ubuntu 的 DVD ISO, 这会对你非常有用)

(11)系统升级
sudo apt-get update

(12)清除所有已删除软件包的残馀配置文件
dpkg -l |grep ^rc|awk ‘{print $2}’ |sudo xargs dpkg -P

(13)编译时缺少h文件的自动处理
sudo auto-apt run ./configure

(14)查看安装软件时下载软件包的临时存放目录
ls /var/cache/apt/archives

(15)备份当前系统安装的所有软件包的列表
dpkg –get-selections | grep -v deinstall > ~/somefile

(16)从上面备份的安装包的列表文件恢复所有包
dpkg –set-selections < ~/somefile
sudo dselect

(17)清理旧版本的软件缓存
sudo apt-get autoclean

(18)清理所有软件缓存
sudo apt-get clean

(19)删除系统不再使用的孤立软件
sudo apt-get autoremove

(20)查看软件包在服务器上面的地址
apt-get -qq –print-uris install ssh | cut -d\’ -f2

(21)查看安装软件的一些路径信息
dpkg -L 软件包名

deb包安装

sudo dpkg -i  'deb文件路径加文件名'

java8新特性

发表于 2020-07-26

Stream

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

forEach

内部迭代,在集合中迭代运行指定的方法

1
2
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

map

map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:

1
2
3
4
5
6
7
        List<Integer> list = Arrays.asList(1,2,2,3,4);
List<Integer> squaresList = list
.stream()
.map(integer->integer*integer)
// .distinct()
.collect(Collectors.toList());
squaresList.forEach(System.out::println);

其中的distinct是用来去除重复项的

filter

filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:

1
2
3
4
5
List<String> strings = Arrays.asList("abc","bc","efg","abcd","","jkl");
String collect = strings.stream()
.filter(string -> !string.isEmpty())
.collect(Collectors.joining(","));
System.out.print(collect);

limit

limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:

1
2
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

sorted

sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:

1
2
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);

parallelStream

parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量:

1
2
3
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();

collectors

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:

1
2
3
4
5
6
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

System.out.println("筛选列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);

统计

另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。

1
2
3
4
5
6
7
8
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();

System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());

注意:这里使用的Arrays.asList 中的ArrayList是Arrays类下的。与ArrayList不同

1…567
WenLiangBao

WenLiangBao

个人技术总结。(*^_^*)温故而知新,可以为师矣

62 日志
7 标签
Creative Commons
© 2024 WenLiangBao
由 Hexo 强力驱动 v3.8.0
|
主题 – NexT.Mist v7.0.0