db.createCollection('students', { validator: { $jsonSchema: { bsonType: 'object', required: ['name', 'year', 'major', 'address'], properties: { name: { bsonType: 'string', description: 'must be a string and is required' }, year: { bsonType: 'int', minimum: 2017, maximum: 3017, description: 'must be an integer in [ 2017, 3017 ] and is required' }, major: { enum: ['Math', 'English', 'Computer Science', 'History', null], description: 'can only be one of the enum values and is required' }, gpa: { bsonType: ['double'], description: 'must be a double if the field exists' }, address: { bsonType: 'object', required: ['city'], properties: { street: { bsonType: 'string', description: 'must be a string if the field exists' }, city: { bsonType: 'string', description: 'must be a string and is required' } } } } } } })
db.createCollection('contacts2', { validator: { $jsonSchema: { bsonType: 'object', required: ['phone'], properties: { phone: { bsonType: 'string', description: 'must be a string and is required' }, email: { bsonType: 'string', pattern: '@mongodb.com$', description: 'must be a string and match the regular expression pattern' }, status: { enum: ['Unknown', 'Incomplete'], description: 'can only be one of the enum values' } } } }, validationAction: 'warn' })
# tail -10f /var/log/mongodb/mongod.log 2020-07-09T12:20:17.391+0800 I NETWORK [listener] Listening on /tmp/mongodb-27017.sock 2020-07-09T12:20:17.392+0800 I NETWORK [listener] Listening on 127.0.0.1 2020-07-09T12:20:17.392+0800 I NETWORK [listener] waiting for connections on port 27017
记录锁(Record Lock)锁定一个记录上的索引,而不是记录本身。例如,执行 SELECT value FROM t WHERE value BETWEEN 10 and 20 FOR UPDATE; 后,会禁止任何其他事务插入、更新或删除 t.value 值在 10 到 20 范围之内的数据,因为该范围内的所有现有值之间的间隙已被锁定。
-- 对 id 为 1 的记录添加 X 型记录锁 SELECT*FROM `t` WHERE `id` =1FORUPDATE;
-- 延迟 20 秒执行后续语句,保持锁定状态 SELECT SLEEP(20);
-- 释放锁 COMMIT;
事务二、被锁定的行记录无法修改
1 2 3 4 5
-- 修改 id = 10 的行记录,正常执行 UPDATE `t` SET `value` =0WHERE `id` =10;
-- 修改 id = 1 的行记录,由于 id = 1 被 X 型记录锁锁定,直到事务一释放锁,方能执行 UPDATE `t` SET `value` =0WHERE `id` =1;
间隙锁(Gap Lock)
间隙锁(Gap Lock)锁定索引之间的间隙,但是不包含索引本身。
间隙锁虽然存在 X 型间隙锁和 S 型间隙锁,但是并没有什么区别,它们彼此不冲突,不同事务可以在间隙上持有冲突锁,并不存在互斥关系。例如,事务 A 可以在某个间隙上持有 S 型间隙锁,而事务 B 在同一间隙上持有 X 型间隙锁。允许存在冲突间隙锁的原因是:如果从索引中清除记录,则必须合并不同事务在该记录上持有的间隙锁。
只有在可重复读或以上隔离级别下的特定操作才会取得 Gap Lock 或 Next-Key Lock,在 Select、Update 和 Delete 时,除了基于唯一索引的查询之外,其它索引查询时都会获取 Gap Lock 或 Next-Key Lock,即锁住其扫描的范围。主键索引也属于唯一索引,所以主键索引是不会使用 Gap Lock 或 Next-Key Lock。
在 MySQL 中,Gap Lock 默认是开启的,即 innodb_locks_unsafe_for_binlog 参数值是 disable 的,且 MySQL 中默认的是可重复读事务隔离级别。
当我们执行以下查询 SQL 时,由于 value 列为非唯一索引,此时又是 RR 事务隔离级别,所以 SELECT 的加锁类型为 Gap Lock,这里的 gap 范围是 (4,+∞)。
1
SELECT*FROM test wherevalue=4forupdate;
执行查询 SQL 语句获取的 Gap Lock 并不会导致阻塞,而当我们执行以下插入 SQL 时,会在插入间隙上再次获取插入意向锁。插入意向锁其实也是一种 gap 锁,它与 Gap Lock 是冲突的,所以当其它事务持有该间隙的 Gap Lock 时,需要等待其它事务释放 Gap Lock 之后,才能获取到插入意向锁。
以上事务 A 和事务 B 都持有间隙 (4,+∞)的 gap 锁,而接下来的插入操作为了获取到插入意向锁,都在等待对方事务的 gap 锁释放,于是就造成了循环等待,导致死锁。
在 system.indexes 插入数据,可以创建索引。但除此之外该表信息是不可变的(特殊的 drop index 命令将自动更新相关信息)。system.users 是可修改的。system.profile 是可删除的。
MongoDB 数据类型
数据类型
描述
String
字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer
整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Boolean
布尔值。用于存储布尔值(真/假)。
Double
双精度浮点值。用于存储浮点值。
Min/Max keys
将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Array
用于将数组或列表或多个值存储为一个键。
Timestamp
时间戳。记录文档修改或添加的具体时间。
Object
用于内嵌文档。
Null
用于创建空值。
Symbol
符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
Date
日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。
Object ID
对象 ID。用于创建文档的 ID。
Binary Data
二进制数据。用于存储二进制数据。
Code
代码类型。用于在文档中存储 JavaScript 代码。
Regular expression
正则表达式类型。用于存储正则表达式。
MongoDB CRUD
数据库操作
查看所有数据库
1
show dbs
创建数据库
1
use <database>
如果数据库不存在,则创建数据库,否则切换到指定数据库。
【示例】创建数据库,并插入一条数据
刚创建的数据库 test 并不在数据库的列表中, 要显示它,需要插入一些数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14
> use test switched to db test > > show dbs admin 0.000GB config 0.000GB local 0.000GB > db.test.insert({"name":"mongodb"}) WriteResult({ "nInserted" : 1 }) > show dbs admin 0.000GB config 0.000GB local 0.000GB test 0.000GB
依赖查找(Dependency Lookup):容器中的受控对象通过容器的 API 来查找自己所依赖的资源和协作对象。
理解 Ioc 的关键是要明确两个要点:
谁控制谁,控制什么:传统 Java SE 程序设计,我们直接在对象内部通过 new 进行创建对象,是程序主动去创建依赖对象;而 IoC 是有专门一个容器来创建这些对象,即由 Ioc 容器来控制对象的创建;谁控制谁?当然是 IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
IoC 不是一种技术,而是编程思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了 IoC 容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
其实 IoC 对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在 IoC/DI 思想中,应用程序就变成被动的了,被动的等待 IoC 容器来创建并注入它所需要的资源了。
IoC 很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由 IoC 容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
IoC 和 DI
其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以 2004 年大师级人物 Martin Fowler 又给出了一个新的名字:“依赖注入”,相对 IoC 而言,“依赖注入”明确描述了“被注入对象依赖 IoC 容器配置依赖对象”。
IoC 容器就是具有依赖注入功能的容器。IoC 容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中 new 相关的对象,应用程序由 IoC 容器进行组装。在 Spring 中 BeanFactory 是 IoC 容器的实际代表者。
Spring IoC 容器如何知道哪些是它管理的对象呢?这就需要配置文件,Spring IoC 容器通过读取配置文件中的配置元数据,通过元数据对应用中的各个对象进行实例化及装配。一般使用基于 xml 配置文件进行配置元数据,而且 Spring 与配置文件完全解耦的,可以使用其他任何可能的方式进行配置元数据,比如注解、基于 java 文件的、基于属性文件的配置都可以。
由 IoC 容器管理的那些组成你应用程序的对象我们就叫它 Bean。Bean 就是由 Spring 容器初始化、装配及管理的对象,除此之外,bean 就与应用程序中的其他对象没有什么区别了。那 IoC 怎样确定如何实例化 Bean、管理 Bean 之间的依赖关系以及管理 Bean 呢?这就需要配置元数据,在 Spring 中由 BeanDefinition 代表,后边会详细介绍,配置元数据指定如何实例化 Bean、如何组装 Bean 等。
Spring IoC
Spring IoC 容器中的对象仅通过构造函数参数、工厂方法的参数或在对象实例被构造或从工厂方法返回后设置的属性来定义它们的依赖关系(即与它们一起工作的其他对象)。然后容器在创建 bean 时注入这些依赖项。这个过程基本上是 bean 本身通过使用类的直接构造或诸如服务定位器模式之类的机制来控制其依赖关系的实例化或位置的逆过程(因此称为控制反转)。
org.springframework.beans 和 org.springframework.context 是 IoC 容器的基础。
IoC 容器
在 Spring 中,有两种 IoC 容器:BeanFactory 和 ApplicationContext。
BeanFactory:**BeanFactory 是 Spring 基础 IoC 容器**。BeanFactory 提供了 Spring 容器的配置框架和基本功能。
ApplicationContext:**ApplicationContext 是具备应用特性的 BeanFactory 的子接口**。它还扩展了其他一些接口,以支持更丰富的功能,如:国际化、访问资源、事件机制、更方便的支持 AOP、在 web 应用中指定应用层上下文等。
实际开发中,更推荐使用 ApplicationContext 作为 IoC 容器,因为它的功能远多于 BeanFactory。
org.springframework.context.ApplicationContext 接口代表 Spring IoC 容器,负责实例化、配置和组装 bean。容器通过读取配置元数据来获取关于要实例化、配置和组装哪些对象的指令。配置元数据以 XML、Java 注释或 Java 代码表示。它允许您表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系。
<beanid="petStore"class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl"> <propertyname="accountDao"ref="accountDao"/> <propertyname="itemDao"ref="itemDao"/> <!-- additional collaborators and configuration for this bean go here --> </bean>
<!-- more bean definitions for services go here -->
<beanid="accountDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao"> <!-- additional collaborators and configuration for this bean go here --> </bean>
<beanid="itemDao"class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao"> <!-- additional collaborators and configuration for this bean go here --> </bean>
<!-- more bean definitions for data access objects go here -->
Spring 支持通过多个 xml 文件来定义 Bean,每个单独的 XML 配置文件都代表架构中的一个逻辑层或模块。可以使用 ApplicationContext 构造函数从所有这些 XML 片段加载 bean 定义。或者,使用 <import/> 元素从另一个或多个文件加载 bean 定义。如下所示:
DI 是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解 DI 的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
谁依赖于谁:当然是应用程序依赖于 IoC 容器;
为什么需要依赖:应用程序需要 IoC 容器来提供对象需要的外部资源;
谁注入谁:很明显是 IoC 容器注入应用程序某个对象,应用程序依赖的对象;
注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
IoC 依赖注入 API
根据 Bean 名称注入
根据 Bean 类型注入
注入容器内建 Bean 对象
注入非 Bean 对象
注入类型
实时注入
延迟注入
依赖注入模式
依赖注入模式可以分为手动注入模式和自动注入模式。
手动注入模式
手动注入模式:配置或者编程的方式,提前安排注入规则
XML 资源配置元信息
Java 注解配置元信息
API 配置元信息
自动注入模式
自动注入模式即自动装配。自动装配(Autowiring)是指 Spring 容器可以自动装配 Bean 之间的关系。Spring 可以通过检查 ApplicationContext 的内容,自动解析合作者(其他 Bean)。
自动装配可以显著减少属性或构造函数参数的配置。
随着对象的发展,自动装配可以更新配置。
注:由于自动装配存在一些限制和不足,官方不推荐使用。
自动装配策略
当使用基于 XML 的配置元数据时,可以使用 <bean/> 元素的 autowire 属性为 Bean 指定自动装配模式。自动装配模式有以下类型:
模式
说明
no
默认值,未激活 Autowiring,需要手动指定依赖注入对象。
byName
根据被注入属性的名称作为 Bean 名称进行依赖查找,并将对象设置到该属性。
byType
根据被注入属性的类型作为依赖类型进行查找,并将对象设置到该属性。
constructor
特殊 byType 类型,用于构造器参数。
org.springframework.beans.factory.config.AutowireCapableBeanFactory 是 BeanFactory 的子接口,它是 Spring 中用于实现自动装配的容器。
// the SimpleMovieLister has a dependency on a MovieFinder privatefinal MovieFinder movieFinder;
// a constructor so that the Spring container can inject a MovieFinder publicSimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; }
// business logic that actually uses the injected MovieFinder is omitted... }
// the SimpleMovieLister has a dependency on the MovieFinder private MovieFinder movieFinder;
// a setter method so that the Spring container can inject a MovieFinder publicvoidsetMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; }
// business logic that actually uses the injected MovieFinder is omitted... }
HTTP Status 407 - Need authentication!!! type Status report message Need authentication!!! description The client must first authenticate itself with the proxy (Need authentication!!!). Apache Tomcat/5.5.29