13 JSON和ORM
在本章中,我们将学习如何使用V内置库,即json和orm。构建RESTful API等Web服务时,了解与客户端或其他RESTful API交换的内容类型至关重要。JavaScript对象表示法(JSON)已成为现代应用程序交换数据的首选格式。在本章中,我们将简要介绍JSON以及如何在V中使用它。
在构建面向数据的应用程序时,对象关系映射器(ORM)成为建立对象世界和关系型数据库世界之间通信的重要组成部分。在本章中,我们将介绍随V安装程序一起提供的内置库orm。
在本章中,我们将学习以下内容:
使用JSON入门
使用ORM入门
在本章的最后,你将学习内置的json和orm V库。了解这些库将帮助你编写数据驱动的Web API或微服务,以JSON格式交换数据。
技术要求
虽然不强制要求,但最好已掌握SQL知识。本章的完整源代码可在 https://github.com/ToughStyle/V-programming-book-cn/tree/main/codes/Chapter13 上找到。
使用JSON入门
JSON是应用程序之间通信数据的最常用格式,例如HTTP API或Web应用程序。其他数据格式包括XML,CSV,TSV和文本文件等。
为了进行演示,请考虑以下Note结构体:
struct Note {
id int
message string
status bool
}我们可以将这个Note结构体表示为JSON格式,如下所示:
{
"id": 1,
"message": "Plan a holiday",
"status": false
}在上面的代码中,JSON对象的表示易于阅读和理解:
JSON对象通常以
{(左花括号)开头,并以}(右花括号)结尾。JSON对象具有各种属性,表示为键值对,由
:(冒号)分隔。键始终用
""(双引号)括起来。值可以是任何数据类型,例如字符串,整数,浮点数,布尔值,数组或任何其他JSON对象。
一组JSON对象由逗号分隔,并且所有这些对象都包含在[和]内。例如,我们可以将多个Note对象表示为JSON数组,如下所示:
[
{
"id": 1,
"message": "Plan a holiday",
"status": false
},
{
"id": 2,
"message": "Get groceries",
"status": false
}
]如上例所示,JSON对象的文件以.json扩展名结尾。这些文件称为JSON文件。
V具有名为json的第三方库,用于编码和解码JSON。要处理JSON数据,我们需要在编写编码或解码JSON的代码中导入json库。在以下各节中,我们将看如何在V中解码和编码JSON。
解码JSON对象
JSON解码是将JSON数据转换为V中的对象的过程。
decode函数接受两个参数。第一个参数是需要将JSON解码为的类型。第二个参数是表示为字符串的JSON数据。
decode函数具有可选的返回类型。因此,如果提供了不正确的输入,我们可以使用or {}块处理错误。在成功解码的情况下,它返回一个对象,其类型等效于作为decode函数输入参数提供的第一个参数。以下示例显示解码JSON对象:
import json
fn main() {
n := json.decode(Note, '{"id":1,"message":"Plan a
holiday","status":false}') or {
panic('invalid json data')
}
println(typeof(n).name) // Note
println(n)
}为简洁起见,在上面的代码中没有指定Note结构体。我们可以看到,要解码JSON字符串,我们必须使用import json语句,并使用两个输入参数调用解码函数。在此示例中,我们要将JSON字符串解码为Note类型。我们还可以看到decode函数后面跟着一个or块,并且我们正在处理不正确JSON数据的情况。
上面代码的输出结果如下:
Note
Note{
id: 1
message: 'Plan a holiday'
status: false
}如果JSON字符串格式错误,如下面的代码所示,则将在控制台上打印错误消息:
n := json.decode(Note, '{"id":1,"message":"Plan a holiday","status":false') or {
panic('invalid json data')
}在上述代码中,JSON字符串末尾缺少}。因此,在这种情况下,程序将使用错误消息invalid json data抛出异常。
接下来,我们将学习如何将V中的对象编码为JSON数据。
将对象编码为JSON数据
将对象编码为JSON是将V中的对象转换为JSON字符串的过程。encode函数接受一个输入参数,并返回表示JSON数据的字符串。
以下代码演示了如何使用encode方法:
import json
fn main() {
m := Note{
id: 2
message: 'Get groceries'
status: false
}
j := json.encode(m)
println(j)
}在上述代码中,我们正在对Note结构体进行编码,并将输出打印到控制台上,输出结果如下:
{"id":2,"message":"Get groceries","status":false}使用ORM入门
理解ORM属性
有一些属性可用于装饰结构体及其字段,使其类似于关系型数据库中的表格模式。属性必须包含在方括号[和]中。这些属性如下表所示:
属性装饰符 用途
[table: 'TABLE_NAME'] Struct TABLE_NAME 可以是正在设计的表的任何描述性名称。如果未指定此属性,则认为结构体名称是表名。
[primary] Field 将字段设置为主键。
[sql: 'COLUMN_NAME'] Field 设置表中的列名。
[sql: COLUMN_TYPE] Field 设置表中列的类型,常见类型为int,string,bool以及称为serial的特殊类型,用于表示自动递增。serial类型通常与主键一起使用。
[unique] Field 将字段设置为具有在一组行中具有唯一值的字段。
[unique: 'GROUP_NAME'] Field GROUP_NAME 可以是任何组名。它使用提供的名称将该字段添加到唯一组中。
[nonull]属性
[nonull]属性[nonull]属性是V语言中一个用于属性装饰的属性,它指定必须为字段提供一个值或使用基于类型的默认零值。
在使用V的ORM编写针对表的查询时,即使使用[table:'TABLE_NAME']属性修饰了结构体,语法也必须引用结构体的名称。对于结构体的字段来说,同样适用,不管是否使用[sql:'COLUMN_NAME']属性将其装饰为不同的列名。
创建ORM结构体
已经了解了V的ORM属性,现在我们将创建一个简单的结构体,类似于关系型数据库中的表格映射。
考虑以下Note结构体,我们想将其与数据库中的Notes表关联起来:
[table:'Notes']
struct Note {
id int [primary; sql: serial]
message string [sql: 'detail'; unique]
status bool [nonull]
}在上面的代码中,可以看到table属性已将表名指定为Notes。但是,在操作Notes表上的数据时,我们将引用Note结构体的名称而不是数据库中表名。
您还可以看到id字段使用两个属性primary和sql:serial进行了指定,并用;分隔。message字段被标记为unique。此外,我们希望message字段有一个不同的名称,例如detail,作为数据库中相应的列名。因此,我们在message字段上指定了sql:'detail'作为附加属性。status字段使用nonull属性进行修饰,表示该注释是否已完成。
现在,我们将了解如何使用V的ORM建立连接并执行数据定义语言(DDL)操作,例如使用V的ORM创建和删除数据表。
使用ORM库
尽管ORM是V中的内置库,但我们只是使用其API与数据库进行通信。要使用ORM库,必须按照以下步骤操作:
将SQLite作为第三方库安装到V的安装目录中
设置使用SQLite库的ORM项目
建立与数据库的连接
在以下子部分中,我们将详细介绍这些步骤。
将SQLite作为第三方库安装到V的安装目录中
为了与数据库建立通信,我们将在V的安装目录中将SQLite安装为第三方库。
为此,请转到 https://sqlite.org/download.html ,并下载SQLite的源代码。下载完成后,您将看到档案。提取档案并将文件放置在V安装目录下的~\v\thirdparty\sqlite目录中。这是Windows 10操作系统上的样子:
C:\V\THIRDPARTY\SQLITE
shell.c
sqlite3.c
sqlite3.h
sqlite3ext.h如果您在Ubuntu操作系统上,则需要运行以下命令作为最终步骤来完成SQLite安装:
sudo apt-get install libssl-dev
sudo apt-get install libsqlite3-dev现在,我们已将SQLite作为第三方库安装到V的安装目录中。下一节中,我们将创建一个项目,以使用SQLite来学习ORM。
设置使用SQLite库的ORM项目
让我们使用以下命令在命令提示符中创建一个名为orm_demo的新项目:
v new orm_demo
cd orm_demo运行v new orm_demo命令后,您将被提示提供描述、版本和许可证信息(可选)。然后,您可以按Enter键。
我们的orm_demo项目将如下所示:
C:\ORM_DEMO
.gitignore
orm_demo.v
v.mod现在,将当前目录设置为我们刚刚创建的新项目的位置。随着我们在本节的相关主题中逐步进行修改项目gradually。因此,如果要查看每个更改的输出,可以使用以下命令从命令提示符运行该项目:
v run .由于它是一个新项目,所以您将看到Hello World!的输出或类似于应用程序的main函数中的print语句的输出。现在,让我们继续学习如何使用刚刚安装的SQLite第三方库连接到数据库。
建立与数据库的连接
要连接到数据库,我们需要导入sqlite库,然后使用connect方法,该方法在导入sqlite后将可用。
现在,让我们开始编辑orm_demo.v文件。替换orm_demo.v文件的内容,以使其如下所示:
module main
import sqlite
[table: 'Notes']
struct Note {
id int [primary; sql: serial]
message string [sql: 'detail'; unique]
status bool [nonull]
}
fn main() {
db := sqlite.connect('NotesDB.db') or { panic(err) }
}由于我们将SQLite安装为第三方库,因此我们将导入它以建立与SQLite数据库的连接。因此,在上面的代码中,我们可以看到import sqlite语句。在导入后,我们将sqlite数据库连接实例分配给主函数内的db变量。
现在,我们将学习使用V的ORM执行各种数据库操作。
简要了解使用V的ORM的数据库操作
在本节中,我们将了解各种数据库操作。这些操作包括DDL操作(例如创建和删除表)。我们还将学习如何使用V的ORM执行数据操作(DML)操作,例如插入、select、update和delete。
请注意,上述DDL和DML不构成完整列表。为简洁起见,我们将只关注一组有限的DDL和DML操作,这些操作将帮助我们构建微服务。
使用V的ORM执行DDL操作
在本节中,我们将学习如何使用V的ORM执行基本和最常见的DDL操作,称为创建和删除。
创建表格
由于我们已经使用V定义了Notes表的外形,如Note结构体所示,因此我们将修改main函数以使用以下代码创建表格:
fn main() {
db := sqlite.connect('NotesDB.db') or { panic(err) }
db.exec('drop table if exists Notes')
sql db {
create table Note
}
}从上面的代码中,我们确保使用提供给db连接的exec函数的普通SQL语法字符串参数删除Notes表,如果该表已存在。这表示您可以直接使用db.exec命令运行SQL查询。在此命令之后,我们正在执行一个表格创建操作,使用V语法执行。create table Note操作将生成以下等效的SQL语法:
CREATE TABLE IF NOT EXISTS `Notes` (
`id` INTEGER,
`detail` TEXT,
`status` INTEGER NOT NULL,
PRIMARY KEY(id)
)接下来,我们将学习如何执行删除表格操作。
删除表格
在上面的代码中,我们看到如何通过调用使用db.exec的基于SQL的命令直接删除表格。或者,您可以使用V的ORM语法删除表格,如下所示:
sql db {
drop table Note
}现在,让我们学习如何执行DML操作。
使用V的ORM执行DML操作
在本节中,我们将学习如何在Notes表上执行基本的DML操作。我们将要学习的V中的ORM语句可以放置在main函数的create table语句之后,以我们即将学习的顺序。为了减少重复的代码,我们将只关注以下各节中ORM的DML语句。
插入记录
要执行插入操作,我们将构建一对Note结构类型的对象,如下所示:
n1 := Note{
message: 'Get some milk'
status: false
}
n2 := Note{
message: 'Get groceries'
status: false
}从上面的代码中,我们可以看到n1和n2对象是Note类型的。现在,我们将使用V的ORM语法执行插入操作,如下所示:
sql db {
insert OBJECT_VAR into STRUCT_NAME
}从上述语法中,OBJECT_VAR一词指的是STRUCT_NAME类型的对象。 STRUCT_NAME一词将是Note结构体的名称。由于我们有两个Note对象n1和n2,因此我们将对这两个对象执行插入操作,如下所示:
sql db {
insert n1 into Note
insert n2 into Note
}V的ORM为Notes表格执行插入操作生成的相应SQL代码如下:
INSERT INTO `Notes` (`detail`, `status`) VALUES (?1, ?2);注意:
前面代码中的SQL语句不会在控制台上打印出来,但它们是V ORM内部生成的。我提到它们是为了方便理解。
要获取ORM已执行任何DML操作的记录的主键标识符,我们可以通过调用last_id()函数访问它,如下所示:
println(db.last_id())该函数返回格式为orm.Primitive(<VALUE>)的值。 last_id()函数,可以在数据库连接上访问,其类型为Primitive,可以通过将其强制转换为相应的基本数据类型(包括time.Time类型),来访问 VALUE。
由于Note中的id主键是整数类型,我们可以将其转换为int类型,如下所示:
println(db.last_id() as int)前面的打印语句将last_id()转换为int数据类型。
选择记录
现在,我们已经成功地将记录插入了数据库,我们将查询数据库并使用select语句验证记录。以下语法显示如何使用V的ORM编写select查询:
ROWS_VAR := sql db {
select from STRUCT_NAME
}在前面的语法中,选择操作返回与结构类型相对应的数组。我们将使用V的ORM在Notes表格上执行select操作,如下所示:
all_notes := sql db {
select from Note
}V ORM作为上一段代码的一部分生成的SQL语句如下:
SELECT `id`, `detail`, `status` FROM `Notes` ORDER BY `id` ASC;从上面的SQL语句中,我们可以看到select语句隐式指定了表的所有列。我们还可以看到,在我们的情况下,默认应用于主键(即id)的升序排序。
现在,让我们使用以下代码打印all_notes变量及其类型:
println(all_notes)
println('Type of all_notes is : ${typeof(all_notes).name}')前面代码的输出如下所示:
[Note{
id: 1
message: 'Get some milk'
status: false
}, Note{
id: 2
message: 'Get groceries'
status: false
}]
Type of all_notes is : []Note在这里,我们可以看到使用ORM时选择语句的结果是[]Note类型。现在,让我们学习如何使用order by子句排序结果。
使用order by子句进行选择
order by子句进行选择在前面的代码中,我们学习到,使用ORM默认情况下,结果按id的升序排列,即Notes表的主键列。如果我们希望结果在id列上按降序排列,则必须编写以下代码:
notes_sorted := sql db {
select from Note order by id desc
}V的ORM作为前面查询的一部分生成的SQL语句如下所示:
SELECT `id`, `detail`, `status` FROM `Notes` ORDER BY `id` DESC;从前面的输出中,我们可以看到结果按Notes表的id列的降序排序。现在,让我们使用limit子句选择记录。
使用limit子句进行选择
limit子句进行选择limit子句指定要作为查询执行结果返回的记录数。您可以将limit与order by或where子句一起使用。我们将在下一节中学习where子句。假设您想要检索最后一个插入Notes表格的记录。为此,我们可以使用以下代码:
notes_limited := sql db {
select from Note order by id desc limit 1
}在前面的代码中,我们将我们的select语句限制为仅返回一条记录。这可以从limit 1中看出,它成为整个select语句的一部分。
作为前一个查询的一部分生成的V ORM生成的SQL语句将如下所示:
SELECT `id`, `detail`, `status` FROM `Notes` ORDER BY `id` DESC LIMIT ?1;在这里,我们可以看到ORM生成的SQL根据id以降序排序注释,并将结果限制为只有1条。
现在,让我们使用以下代码打印notes_limited的值和类型:
println(notes_limited)
println('Type returned by select when limit is 1:
${typeof(notes_limited).name}')前面代码的输出如下所示:
Note{
id: 2
message: 'Get groceries'
status: false
}
Type returned by select when limit is 1: Note从前面的输出中,我们可以看到,选择语句仅返回一个记录,正如预期的那样。我们还可以看到,具有值1的限制子句自动导致值为Note类型的值,而不是其他选择语句中获得的[]Note。
使用where子句进行选择
where子句进行选择where子句用于基于条件语句过滤记录数量。我们将使用select语句,连同where子句,后跟条件语句。这些条件用于过滤与某些列值匹配的记录。以下代码显示使用>关系运算符根据id过滤选择的行:
notes_latest := sql db {
select from Note where id > 1
}作为前面查询的一部分生成的V ORM生成的SQL语句将如下所示:
SELECT `id`, `detail`, `status` FROM `Notes`
WHERE `id` > ?1 ORDER BY `id` ASC;在这里,由ORM生成的SQL根据id > 1条件过滤记录。如果我们打印notes_latest,我们将看到以下输出:
[Note{
id: 2
message: 'Get groceries'
status: false
}]以下是关于V的ORM中where子句的几个注意点:
where子句可以与select、update或delete一起使用。where子句的条件语句使用关系运算符,该运算符求值为布尔结果。您可以使用逻辑运算符(即
&&或||)将多个条件组合在一起。可以将
where子句与order by和limit子句中的一个或两个组合使用。在这种情况下,where子句出现在语句的第一部分,然后是order by,然后是limit。
更新记录
要更新Notes表格中的任何记录,我们可以使用update。使用V的ORM,以下代码将status设置为true,以用于id为2的Notes表格:
sql db {
update Note set status = true where id == 2
}作为前面的查询的一部分生成的V ORM生成的SQL语句将如下所示:
UPDATE `Notes` SET `status` = ?1 WHERE `id` = ?2;要查看更新后的记录,我们可以进行以下查询:
notes_updated := sql db {
select from Note where id == 2
}
println(notes_updated)前面的代码将生成以下输出,我们可以看到状态为id为2的Note已设置为true:
Note{
id: 2
message: 'Get groceries'
status: true
}从前一个输出中,除了将status字段更新为true之外,V ORM还自动返回单个记录,而不是只有一个与where子句持有的id == 2条件匹配的记录[]Note。
删除记录
删除操作从数据库中删除记录。要根据条件删除记录,我们可以使用where子句。以下代码显示如何删除其id为2的记录:
sql db {
delete from Note where id == 2
}作为前面的查询的一部分生成的V ORM生成的SQL语句将如下所示:
DELETE FROM `Notes` WHERE `id` = ?1;现在,让我们通过运行ORM选择查询来查看表中剩余的记录,如下所示:
notes_leftover := sql db {
select from Note
}
println(notes_leftover)上述代码的输出将如下所示:
[Note{
id: 1
message: 'Get some milk'
status: false
}]在对状态标记为true的Note执行删除操作之后,我们只剩下了一条Note,如上面的输出所示。
总结
在本章中,我们简要介绍了JSON和ORM。然后我们详细学习了在V中使用json和orm库的方法。在本章的前几节中,我们学习了如何使用json库执行JSON编码和解码。在本章后面的部分中,我们重点介绍了内置的orm库。我们学习了如何使用orm库,并设置了一些要求,例如手动安装第三方sqlite库以与SQLite数据库一起工作。然后,我们看到了如何使用orm库在V中执行DDL和DML操作。本章为您编写数据驱动的Web API使用orm和json库提供了基础。在本章中获得的ORM知识将帮助您访问和与数据库交互。此外,在V中使用JSON数据格式的知识将帮助您在各种基于Web的API或服务之间交换数据。
在接下来的章节中,我们将学习如何使用vweb、orm、json和sqlite库构建微服务。
最后更新于