Skip to content

Objc 增删查改

qiuwenchen edited this page Mar 7, 2024 · 2 revisions

增删查改是数据库最常用的功能,因此 WCDB Objc 对其进行了特别的封装,使其通过一行代码即可完成操作。

插入操作

插入操作有 insertObjectinsertOrReplaceObjectinsertOrIgnoreObject三类接口。故名思义,第一个只是单纯的插入数据,当数据出现冲突时会失败,而后两个在主键冲突等约束冲突出现时,insertOrReplaceObject会把新数据会覆盖旧数据,insertOrIgnoreObject则是会忽略冲突的数据,而不产生错误。

以已经完成模型绑定的类 Sample 为例:

@interface Sample : NSObject<WCTTableCoding>

@property(nonatomic, assign) int identifier;
@property(nonatomic, strong) NSString* content;

WCDB_PROPERTY(identifier)
WCDB_PROPERTY(content);

@end

@implementation Sample

WCDB_IMPLEMENTATION(Sample)
WCDB_SYNTHESIZE_COLUMN(identifier, "id")
WCDB_SYNTHESIZE(content)
WCDB_PRIMARY(identifier)

@end

BOOL ret = [database createTable:@"sampleTable" withClass:Sample.class];
    
Sample* object = [[Sample alloc] init];
object.identifier = 1;
object.content = @"insert";
ret = [database insertObject:object intoTable:@"sampleTable"];// 插入成功

ret = [database insertObject:object intoTable:@"sampleTable"];// 插入失败,因为主键 identifier = 1 已经存在

object.content = @"insertOrReplace";
ret = [database insertOrReplaceObject:object intoTable:@"sampleTable"];// 插入成功,且 content 的内容会被替换为 "insertOrReplace"

object.content = @"insertOrIgnore";
ret = [database insertOrIgnoreObject:object intoTable:@"sampleTable"];// 插入成功,但 content 的内容不会变更,还是"insertOrReplace"

关于自增插入,可参考模型绑定 - 自增属性一章。

需要插入的对象多时,还可以使用批量插入。当插入的对象数大于 1 时,WCDB 会自动开启事务,进行批量化地插入,以获得更好的性能。

Sample* object2 = [[Sample alloc] init];
object2.identifier = 2;
object2.content = @"insert2";
Sample* object3 = [[Sample alloc] init];
object3.identifier = 3;
object3.content = @"insert3";
// 两个对象要么一起插入成功,要么一起插入失败
ret = [database insertObjects:@[object2, object3] intoTable:@"sampleTable"];

除了插入整个对象,也可以只插入对象的部分属性:

Sample* object4 = [[Sample alloc] init];
object4.identifier = 4;
object4.content = @"insert4";
// 部分插入,没有指定 Sample.content, @"insert4"这个内容没有写入数据库
ret = [database insertObject:object4 onProperties:Sample.identifier intoTable:@"sampleTable"];

这里的onProperties传入的实际是WCTProperties,是一个描述对象属性组合的列表,在读写的场合用来指定读写对象的部分属性,我们会在语言集成查询中进一步介绍。

删除操作

删除操作可以使用deleteFromTable系列接口,后面可组合接 whereorderslimitoffset以删除部分数据。

以下是删除接口的示例代码:

// 删除 sampleTable 中所有 identifier 大于 1 的行的数据
ret = [database deleteFromTable:@"sampleTable" where:Sample.identifier > 1];

// 删除 sampleTable 中 identifier 降序排列后的前 2 行数据
ret = [database deleteFromTable:@"sampleTable"
                         orders:Sample.identifier.asOrder(WCTOrderedDescending)
                          limit:2];

// 删除 sampleTable 中 content 非空的数据,按 identifier 降序排列后的前 3 行的后 2 行数据
ret = [database deleteFromTable:@"sampleTable"
                          where:Sample.content.notNull()
                         orders:Sample.identifier.asOrder(WCTOrderedDescending)
                          limit:2
                         offset:3];

// 删除 sampleTable 中的所有数据
ret = [database deleteFromTable:@"sampleTable"];

这里的 wherelimitoffset 本质都是遵循 WCDB::Expression 的对象,可以是数字、字符串、字段或其他更多的组合。同样地,我们会在语言集成查询进一步介绍。

删除接口不会删除表本身,开发者需要调用 dropTable: 接口删除表。

更新操作

更新数据库的时候可以使用Objc对象或者直接使用具体值来更新数据库,使用Objc对象的接口为: updateTable:setProperties:toObject:,后面可以接whereorderslimitoffset以指定更新范围,下面是示例代码:

Sample* object = [[Sample alloc] init];
object.content = @"update";

// 将 sampleTable 中所有 identifier 大于 1 且 content 字段不为空 的行的 content 字段更新为 "update"
ret = [database updateTable:@"sampleTable"
              setProperties:Sample.content
                   toObject:object
                      where:Sample.identifier > 1 && Sample.content.notNull()];

// 将 sampleTable 中前三行的 content 字段更新为 "update"
ret = [database updateTable:@"sampleTable" setProperties:Sample.content toObject:object limit:3];

而使用值来更新数据库的接口分别是 updateTable:setProperty:toValue:和: updateTable:setProperties:toRow:后面都可以接whereorderslimitoffset以指定更新范围。row 是遵循 WCTColumnCoding 协议的类型的数组。WCTColumnCoding 协议会在后续自定义字段映射类型中进一步介绍。这里只需了解,能够进行字段映射的类型基本都遵循 WCTColumnCoding 协议。下面是使用示例:

// 将 sampleTable 中所有 identifier 大于 1 且 content 字段不为空的行的 content 字段更新为 "update"
ret = [database updateTable:@"sampleTable"
                setProperty:Sample.content
                    toValue:@"update"
                      where:Sample.identifier > 1 && Sample.content.notNull()];

// 将 sampleTable 中 identifier 为 1 行的 identifier 字段更新为2,content 字段更新为 "update2"
WCTOneRow* row = @[@2, @"update2"];
ret = [database updateTable:@"sampleTable"
              setProperties:Sample.allProperties
                      toRow:row
                      where:Sample.identifier == 1];

这里用到的Sample.allProperties等价于{Sample.identifier, Sample.content}allProperties是获取ORM类所有DB配置的属性的便捷方法

查询操作

查找接口接口较多,但大部分都是为了简化操作而提供的便捷接口,和更新接口类似,实现上主要分为对象查找和值查找两大类接口。

对象查询操作

getObjectOfClass:fromTable:getObjectsOfClass:fromTable: 都是对象查找的接口,他们直接返回已进行模型绑定的对象,它们后面都可以接whereorderslimitoffset以指定查找范围。 而 getObjectOfClass 等价于 limit为1 时的 getObjectsOfClass 接口。不同的是,它直接返回 Object 对象,而不是一个数组,使用上更便捷。 以下是对象查找操作的示例代码:

// 返回 sampleTable 中的所有数据
NSArray<Sample*>* allObjects = [database getObjectsOfClass:Sample.class
                                                 fromTable:@"sampleTable"];

// 返回 sampleTable 中 identifier 小于 5 或 大于 10 的行的数据
NSArray<Sample*>* objects = [database getObjectsOfClass:Sample.class
                                              fromTable:@"sampleTable"
                                                  where:Sample.identifier < 5 || Sample.identifier > 10];

// 返回 sampleTable 中 identifier 最大的行的数据
Sample* object = [database getObjectOfClass:Sample.class
                                  fromTable:@"sampleTable"
                                     orders:Sample.identifier.asOrder(WCTOrderedDescending)];

对象部分查询

与 "insert"、"update" 类似,对象查找操作也支持指定字段,这种接口有getObjectOnResultColumns:fromTable:getObjectsOnResultColumns:fromTable: 两个,区别还是前者查单个对象,后者查一组对象,这两个接口后面都可以接whereorderslimitoffset以指定查找范围。下面是使用示例:

// 返回 sampleTable 中的所有identifier字段,返回结果中的content属性则都是nil
    NSArray<Sample*>* allObjects = [database getObjectsOnResultColumns:Sample.identifier fromTable:@"sampleTable"];

值查询操作

值查询有下面四类接口,这些接口后面都可以接whereorderslimitoffset以指定查找范围:

  • getValueOnResultColumn:fromTable:
  • getRowOnResultColumns:fromTable:
  • getColumnOnResultColumn:fromTable:
  • getRowsOnResultColumns:fromTable:

试考虑,表中的数据可以想象为一个矩阵的存在,假设其数据如下:

identifier content
1 "sample1"
2 "sample1"
3 "sample2"
4 "sample2"
5 "sample2"

在不考虑 whereorderslimitoffset 参数的情况下:

  1. getRowsOnResultColumns:fromTable:接口获取整个矩阵的所有内容,即返回值为二维数组。
  2. getRowOnResultColumns:fromTable: 接口获取某一横行的数据,即返回值为一维数组。
  3. getColumnOnResultColumn:fromTable: 接口获取某一纵列的数据,即返回值为一维数组。
  4. getValueOnResultColumn:fromTable: 接口获取矩阵中某一个格的内容。

以下是值查询操作的示例代码:

// 获取所有内容
WCTColumnsXRows* allRows = [database getRowsOnResultColumns:Sample.allProperties fromTable:@"sampleTable"];
NSLog(@"%@", allRows[2][0]); // 输出 3

// 获取第二行
WCTOneRow* secondRow = [database getRowOnResultColumns:Sample.allProperties
                                             fromTable:@"sampleTable"
                                                offset:1];
NSLog(@"%@", secondRow[0]); // 输出 2

// 获取第二行 content 列的值
WCTOneColumn* contentColumn = [database getColumnOnResultColumn:Sample.content fromTable:@"sampleTable"];
NSLog(@"%@", contentColumn); // 输出 "sample1", "sample1", "sample1", "sample2", "sample2"

WCTValue* value = [database getValueOnResultColumn:Sample.content fromTable:@"sampleTable" offset:1];
NSLog(@"%@", value);// 输出 "sample1"

// 获取 identifier 的最大值
WCTValue* maxId = [database getValueOnResultColumn:Sample.identifier.max() fromTable:@"sampleTable"];

这里 Sample.identifier.max() 是可以转换 WCDB::ResultColumn 的对象,用于查找 identifier 列的最大值。我们会在语言集成查询中进一步介绍。

Clone this wiki locally