iOS用对FMDB封装
一个对FMDB进行Hibernate样式类封装的iOS库
GitHub: scubers
如有问题或bug,请随时向我提交issue,或通过邮件。感谢使用
2.0更新
- 数据库字段名从默认的_ivar名更改为property名: _name -> name
- 数据库操作对象的获取改为使用连接池: [JRDBMgr defaultDB] -> [JRDBMgr shareInstance].getHandler
- 取消缓存功能
- 新增And, Or查询语句
描述(Description)
- 使用分类模式,模仿Hibernate,对FMDB进行简单封装
- 使用协议,不继承基类,可以 храм arbitrarily NSObject 进行入库操作
- Objective-C(Swift请参阅Swift扩展)
目录(Index)
安装(安装)
pod 'JRDB'
开始(开始)
JRDBMgr 使用
设置数据库路径
[[JRDBMgr shareInstance] setDefaultDatabasePath:@"/Users/mac/Desktop/test11.sqlite"];
是否打印sql语句
[JRDBMgr shareInstance].debugMode = YES;
获取处理器
- 从连接池中获取数据库处理器
[[JRDBMgr shareInstance] getHandler];
关闭数据库
[[JRDBMgr shareInstance] close];
注册
- 需要使用本库的类都需要注册。
[[JRDBMgr shareInstance] registerClazzes:@[
[Person class],
]];
表名
默认类名为表明,可以自定义表名,在主类中重写一下方法即可
+ (NSString *)jr_customTableName {
return @"my_tableName";
}
主键
默认每个对象入库都会持有一个ID [person ID]
, 作为数据库的主键,库通过这个 ID
来识别对象是否与数据库关联,所以不是必要时,不要操作此属性
自定义主键
不同的业务需求,有可能使用的主键有特定的业务意义,需要自行定义。
在需要自定义的实体类中实现一下方法
/// 自定义主键的对应的属性 (需要是属性的全名)
+ (NSString *)jr_customPrimarykey {
return @"name"; // 对应property的属性名
}
/// 自定义主键属性值
- (id)jr_customPrimarykeyValue {
return self.name;
}
通过下面的方法可以获取对应的值
/**
* 如果有自定义主键,则返回自定义主键key,例如 name,若没有实现,则返回默认主键key : @"_ID"
*/
[Person jr_primaryKey];
/**
* 如果有自定义主键,则返回自定义主键的值,如果没有,则返回 [self ID]
*/
[p jr_primaryKeyValue];
自定义字段名
默认使用Property的属性名进行字段名定义,可以对每个字段进行自定义数据库字段名,在主类中重写以下方法即可
- 无返回字段默认使用property属性名当做数据库列名
+ (NSDictionary<NSString *,NSString *> *)jr_databaseNameMap {
return @{
@"name" : @"db_name",
@"age" : @"db_age",
@"height" : @"db_height",
};
}
忽略字段
默认非数据库基本类型都会忽略不入库。
数据库基本类型
- NSString
- NSDate
- NSData
- int, unsigned int, double, float, long.....
非以上类型都会自动忽略不入库。
若有特定需要忽略字段,需要实现以下方法
/// 忽略age属性,不做入库操作
+ (NSArray *)jr_excludePropertyNames {
return @[
@"age",
];
}
表操作(TableOperation)
建表
J_CreateTable(Person)
更新表
- 更新表时只会添加字段,不会删除或更新字段名,有需要的话需要自行写sql语句解决
J_UpdateTable(Person)
删除表
J_DropTable(Person)
重建表
J_TruncateTable(Person)
保存(Save)
BOOL result = J_Insert(p)
.InDB([JRDBMgr shareInstance].getHandler) // by Default
.Recursive(NO) // by default
.Sync(YES) // by default
.Transaction(YES) // by default
.updateResult; // 执行
// 可以省略为
BOOL result = J_Insert(p).updateResult;
// 数组保存,两种 api 自由使用
BOOL result = J_Insert(p1, p2, p3).updateResult;
BOOL result = J_Insert(@[p1, p2, p3]).updateResult;
更新 (Update)
更新操作需要提供对象的主键,请确保需要更新的对象都是从数据库查出来的;(也可以手动设置主键让库识别,不建议)
BOOL result = J_Update(p)
.Columns(@[@"age", @"name"]) // 更新指定列
// .Ignore(@[@"age", @"name"]) // 忽略指定列
.InDB([JRDBMgr shareInstance].getHandler) // by default
.Recursive(NO) // by default
.Sync(YES) // by default
.Transaction(YES) // by default
.updateResult; // 执行
BOOL result = J_Update(p).Ignore(@[@"phone"]).updateResult;
// 更新数组
BOOL result = J_Update(p1, p2).updateResult;
BOOL result = J_Update(@[p1, p2, p3]).updateResult;
删除(Delete)
删除操作需要提供对象的主键,请确保需要更新的对象都是从数据库查出来的;(也可以手动设置主键让库识别,不建议)
// 删除
BOOL result = J_Delete(p)
.InDB([JRDBMgr shareInstance].getHandler) // by default
.Recursive(NO) // by default
.Sync(YES) // by default
.Transaction(YES) // by default
.updateResult; // 执行
查询(Query)
// 条件查询
// And Or 对应Property中的属性名
NSArray<Person *> = J_Select(Person) // select * from person [where 1=1]
.And(@"age").lt(@10) // and age < 10
.Or(@"height").gt(@120) // or height > 120
.Or(@"name").like(@"Wang%") // or name like 'Wang%'
.And(@"weight").nq(@200) // and weight <> 200
.list
// 普通查询
NSArray<Person *> *result =
J_Select(Person) // 指定查询对象
.Recursive(YES) // 默认 可省略
.Sync(YES) // 默认 可省略
.Desc(NO) // 默认 可省略
.Where(@"name like ? and height > ?") // 对应数据库中的字段名
.Params(@[@"L%", @150]) // 对应条件语句的 ? 可省略
.Group(@"level") // Group 可省略
.Order(@"age") // Order 可省略
.Limit(0, 10) // 分页 start, length 可省略
.list;
// 自定义查询
NSArray<Person *> *result1 =
J_SelectColumns(@[@"age", @"name"])
.From([Person class])
.Recursive(YES) // 在自定义查询中不会起作用
.Sync(YES) // 默认 可省略
.Where(@"name like ? and height > ?") // 对应数据库中的字段名
.Params(@[@"L%", @150])
.Group(@"level") // 对应数据库中的字段名
.Order(@"age") // 对应数据库中的字段名
.Limit(0, 10)
.Desc(NO)
.list;
NSUInteger count =
J_SelectCount(Person) // 查询哪个类
.Recursive(YES) // 在自定义查询中不会起作用
.Sync(YES) // 默认 可省略
.Where(@"name like ? and height > ?")
.Params(@[@"L%", @150])
.Group(@"level")
.Order(@"age")
.Limit(0, 10)
.Desc(NO)
.count;
链式调用配置(Configuration)
配置 | 功能 | 参数类型 |
---|---|---|
InDB | 默认使用[JRDBMgr shareInstance].getHandler; | id |
From | 自定义查询时指定的类名 或子查询的Chain对象 |
Class | JRDBChain * |
Recursive | 默认为NO; NO:效率高, YES:关联操作效率低 |
YES或NO |
事务 | 默认为YES; NO:本操作不包含事务,外界需要事务支持 YES:包含事务 |
YES或NO |
同步 | 默认为YES; YES:阻塞本线程,线程安全同步执行数据库操作; NO:在本线程执行数据库操作,线程不安全 |
YES或NO |
Where | Where后面的条件筛选语句,使用?作为参数占位符 使用的字段需要与数据库字段相同 |
NSString * |
WhereIdIs | 等同于Where(@"_id = ?") | NSString * |
WherePKIs | 等同于Where(@"<#primary key#> = ?") | id |
And | And语句,跟随属性名And(@"name") | NSString |
Or | Or语句,跟随属性名Or(@"age") | NSString |
eq | eq语句,跟随参数eq(@10)相当于SQL语句的「=」 | id |
nq | nq语句,跟随参数nq(@10)相当于SQL语句的「<>」 | id |
gt | gt语句,跟随参数eq(@10)相当于SQL语句的「>」 | id |
lt | lt语句,跟随参数eq(@10)相当于SQL语句的「<」 | id |
gtOrEq | gtOrEq语句,跟随参数eq(@10)相当于SQL语句的「>=」 | id |
ltOrEq | ltOrEq语句,跟随参数eq(@10)相当于SQL语句的「<=」 | id |
like | like语句,跟随参数eq(@10)相当于SQL语句的「like」 | id |
Params | Where语句占位符对应的参数 | NSArray * |
Columns | 更新时指定更新的列 | NSArray * |
Ignore | 更新时指定忽略的列 | NSArray * |
Group | 按group by字段分组 | NSString * |
Order | 按order by字段排序 | NSString * |
页码 | 分页字段(start, length) | unsigned long, unsigned long |
降序 | 默认为NO; 是否根据orderby进行降序 | YES或NO |
关联操作(Link)
描述:当一个类的一个属性为一个实体类,在操作数据库时,通过配置,也可以进行同时操作
例如:当保存Person时,也想同时保存card对象,Money数组,以及children数组,则可以进行关联操作。需要在对应的类实现以下方法,并且子对象也需要注册
// 注册子model类
[[JRDBMgr shareInstance] registerClazzes:@[
[Person class],
[Card class],
[Money class],
]];
@interface Person : NSObject
@property (nonatomic, strong) Card *card;
@property (nonatomic, strong) NSMutableArray<Money *> *money;
@property (nonatomic, strong) NSMutableArray<Person *> *children;
@end
@implementation
/// 单个对象关联
+ (NSDictionary<NSString *,Class<JRPersistent>> *)jr_singleLinkedPropertyNames {
return @{
@"card" : [Card class],
};
}
/// 数组对象关联
+ (NSDictionary<NSString *,Class<JRPersistent>> *)jr_oneToManyLinkedPropertyNames {
return @{
@"money" : [Money class],
@"children" : [Person class],
};
}
@end
关联操作(保存)
Person *p = [Person new];
Card *c = [Card new];
p.card = c;
p.money = @[m1,m2,m3];
p.children = @[p1,p2,p3];
BOOL result = J_Insert(p)
.Recursive(YES) // 默认为NO, 需要手动指定关联保存
.updateResult;
关联操作(更新)
注意:如果子对象都是未保存的(即数据库中没有的对象),则全部保存。如果有已存在对象,则不保存也不更新。
-
基于更新的操作的随意性比较重要,更新时不执行任何关联操作,即更新时,只更新本模型的相关信息,不更新所有子模型的信息。当层级较多时,需要从子层级开始逐步更新(因此不建议建立太多层级)。
-
更新本模型的信息包括:
- 子模型的ID将保存(如果有)。
- 子模型数组的数量(如果子模型数组数量发生变化,将进行更新,但不会将子模型数组更新到数据库)。
BOOL result = J_Update(p).Recursive(YES).updateResult;
--
关联操作(删除)
- 与更新一样,删除时只会删除本模型的信息,不会进行任何关联操作。
- 删除时,会删除一对多的中间表中的无用信息。
BOOL result = J_Delete(p).Recursive(YES).updateResult;
关联操作(查询)
NSArray<Person *> *list = J_Select(Person).Recursive(YES).list;
宏(Macro)
- 使用宏,使调用更加智能。
From([Person class]) --> FromJ(Person)
Where(@"name = ?") --> WhereJ(_name = ?)
Order(@"name") --> OrderJ(name)
Group(@"name") --> GroupJ(name)
Params(@[@"jack", @"mark"]) --> ParamsJ(@"jack", @"mark")
Ignore(@[@"name", @"age"]) --> IgnoreJ(@"name", @"age")
Columns(@[@"name", @"age"]) --> ColumnsJ(@"name", @"age")
// example
NSArray *result = J_Select(Person)
.WhereJ(name like ? and height > ?)
.ParamsJ(@"a%", @100)
.GroupJ(h_double)
.OrderJ(d_long_long)
.list;
BOOL result = J_Update(person)
.ColumnsJ(J(name), J(age))
// .IgnoreJ(J(name), J(age))
.updateResult;
子查询(SubQuery)
// 正常查询只能先排序再分页,加入子查询,可以先分页,再从子结果中排序 . ie.
NSArray<Person *> *list =J_Select(Person)
.From(
J_Select(Person).Limit(0, 10) // 放入一个子查询,外部查询则从子查询的结果里继续查询
).OrderJ(age)
.Descend
.list;
NSObject+JRDB
为NSObject添加分类方法,以便快速方便地使用本库的渐变功能。
- (BOOL)jr_saveOrUpdateOnly;// 非关联操作
- (BOOL)jr_saveOrUpdate; // 关联操作
#pragma mark - save
- (BOOL)jr_saveOnly;
- (BOOL)jr_save;
#pragma mark - update
- (BOOL)jr_updateOnlyColumns:(NSArray<NSString *> * _Nullable)columns;
- (BOOL)jr_updateColumns:(NSArray<NSString *> * _Nullable)columns;
- (BOOL)jr_updateOnlyIgnore:(NSArray<NSString *> * _Nullable)Ignore;
- (BOOL)jr_updateIgnore:(NSArray<NSString *> * _Nullable)Ignore;
#pragma mark - delete
+ (BOOL)jr_deleteAllOnly;
+ (BOOL)jr_deleteAll;
- (BOOL)jr_deleteOnly;
- (BOOL)jr_delete;
#pragma mark - select
/// 关联查询
+ (instancetype _Nullable)jr_findByID:(NSString * _Nonnull)ID;
+ (instancetype _Nullable)jr_findByPrimaryKey:(id _Nonnull)primaryKey;
+ (NSArray<id<JRPersistent>> * _Nonnull)jr_findAll;
/// 非关联查询
+ (instancetype _Nullable)jr_getByID:(NSString * _Nonnull)ID;
+ (instancetype _Nullable)jr_getByPrimaryKey:(id _Nonnull)primaryKey;
+ (NSArray<id<JRPersistent>> * _Nonnull)jr_getAll;
线程安全
使用本库的数据库,都是阻塞本线程,并且线程安全的,所有操作都带有事务。
操作本库管理的数据库时,请使用本库提供的API进行操作,否则可能会产生数据库锁问题。
泛型提示
通过泛型,查询结果可直接由编译器识别为对应对象,从而减少强制类型转换操作。