1、前言
本文主要记录swift开发Core Data数据库相关知识,从项目搭建,到plist数据准备,再到数据库的增删改查。并且提供源码免费下载。好记性不如烂笔头,记录一下方便以后学习,毕竟是初学者,哈哈,学无止境,以后会持续不断更新。
2、环境搭建
2.1、新建CoreData工程
新建包含CoreData的工程,xCode->File->New->Project,下面一步时,记得勾选“Use Core Data”。
新建完成之后,会看到工程根目录下有个和工程名称一模一样的后缀名为"xcdatamodeld"的文件,该文件就是数据库文件。再打开工程的
AppDelegate
可以看到有关该文件的全局变量和全局方法已自动生成。
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
//CoredataPractice为数据库文件的名称,可以修改,但必须一致。
let container = NSPersistentContainer(name: "CoredataPractice")
container.loadPersistentStores(completionHandler: {
(storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
2.2、旧工程引入CoreData
新建时没有选择use core data,也可以随时新建数据库文件,如下:
新建的文件命名可以随意,新建完成之后,这里取名为
test
,打开AppDelegate
,将上面的那段自动生成的代码加进去,特别注意NSPersistentContainer对象初始化的name必须与后缀名为xcdatamodeld
的文件名一模一样。
3、数据库文件维护
3.1、Entity/Attribute
点击Add Entity
,新建一个Entity,这里相当于一个数据库表。在里面增加Attribute,相当于数据库表字段。Attribute有很多种类型,如下:
除了一些常用的基本数据类型外,Transformable类型可以包含数组,字典等。选择完成数据类型之后可以对该字段做一些个性化的设置,如下图:
设置字段是否是可选,最小最大长度,默认值,或者正则表达式等等。注意如果数据库中已有数据,再要修改数据库某字段的类型,再次运行到手机上之后,程序会崩溃,需先卸载再安装,当然仅限于开发阶段。
3.2、Relationship
关联关系这个应该是经常用到的了,比如本案例,数据库表字段如下:
Cartoon动画片表
字段 | 类型 | 说明 |
---|---|---|
name | String | 动画片名称 |
Character人物角色表
字段 | 类型 | 说明 |
---|---|---|
name | String | 角色名称 |
isProtagonist | Bool | 是否主角 |
age | Int 16 | 年龄 |
remark | String | 备注 |
一部动画片对应多个人物角色。此时需要建立关联关系。在Cartoon表中建立Relationship,取名为roles
,如下图:
- Relationship命名为roles。
- Destination选择Character。
- 右边Delete Rule选择为
Cascade
,即关联删除,如果删除了这个动画片,就会同样删除动画片关联的所有人物角色。 - 右边type选为To Many即一对多。
在Character表中同样需要建立关系,这个人物所属的动画,如下图:
- Relationship命名为cartoon_name。
- Destination选择Cartoon。
- Inverse选择
roles
,即对应Cartoon表中的关系名称。 - 右边Delete Rule选择为
Nullify
,无任何关联。 - 右边type选为To One即一对一,一个人物角色只能对应一部动画片。
此时可以回到Cartoon表添加roles
关系的指向了,如下图:
至此,一个完整的关联关系建立起来了。至于如何赋值,可以参考后文5.3。
4、plist文件操作
plist文件可以给数据库预置一些数据。新建plist文件,New File->Property List(Resource)。如下:
新建完成之后,设置根节点类型,这里选为Array,Item类型选为字典
添加一些字段,及对应的数据
至此准备工作完毕。
5、数据库读写
5.1、读取plist文件数据
//该段代码是读取datalist.plist文件中的所有数据
let plistPath : String = Bundle.main.path(forResource: "datalist.plist", ofType:nil)!
let plistArray : NSArray = NSArray(contentsOfFile: plistPath)!
for item in plistArray {
let itemDict: [String: Any] = item as! [String : Any]
print(item)
}
5.2、数据库增查方法
/// Cartoon插入记录
///
/// - Parameter name: 动画片名称
/// - Returns: Cartoon对象
func saveCartoon(name : String) -> Cartoon {
//创建对象
let obj = NSEntityDescription.insertNewObject(forEntityName: "Cartoon", into: context!) as! Cartoon
//对象赋值
obj.name = name
//保存
app?.saveContext()
return obj
}
/// Character插入数据
///
/// - Parameters:
/// - name: 角色名称
/// - age: 年龄
/// - isProtagonist: 是否主角
/// - cartoon_name: 动画片对象
/// - remark: 备注
/// - Returns: Character对象
func saveCharacter(name: String,
age: Int16,
isProtagonist: Bool,
cartoon_name: Cartoon,
remark: String? = nil) -> Character {
let obj = NSEntityDescription.insertNewObject(forEntityName: "Character",
into: context!) as! Character
obj.name = name
obj.age = age
obj.isProtagonist = isProtagonist
obj.cartoon_name = cartoon_name
obj.remark = remark ?? ""
app?.saveContext()
return obj
}
/// 查询Cartoon
///
/// - Parameter name: 名称
/// - Returns: 结果数组
func queryCartoon(name : String? = nil) -> [Cartoon]{
//声明数据的请求
let fetchRequest = NSFetchRequest<Cartoon>(entityName: entity_Cartoon)
if name != nil {
//设置查询条件
let predicate = nspredicate(format: "name==%@", name!)
fetchRequest.predicate = predicate
}
fetchRequest.fetchOffset = 0 //查询的偏移量
//查询操作
do {
let fetchedobjects = try context!.fetch(fetchRequest)
print(String.init(format:"%@.count = %i", entity_Cartoon, fetchedobjects.count))
return fetchedobjects
}
catch {
fatalError("查询异常“:\(error)")
}
return []
}
其中context 和 app即为数据库相关的定义在AppDelegate中的全局变量。获取方法如下:
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
5.3、plist数据写入数据库
完整代码如下:
//解析plist文件数据
let plistPath : String = Bundle.main.path(forResource: "datalist.plist", ofType:nil)!
let plistArray : NSArray = NSArray(contentsOfFile: plistPath)!
for item in plistArray {
let itemDict: [String: Any] = item as! [String : Any]
let name : String = itemDict["name"] as! String
let age : Int16 = itemDict["age"] as! Int16
let isProtagonist : Bool = itemDict["isProtagonist"] as! Bool
let remark : String? = itemDict["remark"] as? String
let cartoon : String = itemDict["cartoon"] as! String
var cartoonItem : Cartoon!
//插入Cartoon表之前需要先查询下是否已经存在,存在不需要再插入
var cararray = queryCartoon(name: cartoon)
if cararray.isEmpty {
cartoonItem = saveCartoon(name: cartoon)
} else {
cartoonItem = cararray[0]
}
//Cartoon对象获取到之后可以插入Character表
let character = saveCharacter(name: name,
age: age,
isProtagonist: isProtagonist,
cartoon_name: cartoonItem,
remark: remark)
//Character表插入完成之后,需要给Cartoon设置关联关系。
cartoonItem.addToRoles(character)
}
5.4、带条件查询
/// 分页查询
///
/// - Parameters:
/// - pageSize: 每一页条数
/// - currentPage: 当前页
/// - Returns: 查询结果,两个参数同时为空,查询全部数据
func queryCharacter(pageSize : Int? = nil, currentPage : Int? = nil) ->[Character] {
//声明数据的请求
let fetchRequest = NSFetchRequest<Character>(entityName : entity_Character)
if pageSize != nil && currentPage != nil {
fetchRequest.fetchLimit = pageSize! //限定查询结果的数量
fetchRequest.fetchOffset = (currentPage! - 1) * pageSize!
} else {
fetchRequest.fetchOffset = 0 //查询的偏移量
}
//查询操作
do {
let fetchedobjects = try context!.fetch(fetchRequest)
print(String.init(format:"%@.count = %i", entity_Character, fetchedobjects.count))
return fetchedobjects
}
catch {
fatalError("查询异常“:\(error)")
}
return []
}
/// 设置查询条件
///
/// - Parameter predicate: 查询条件
/// - Returns: 查询结果
func queryCharacter(predicate : nspredicate) ->[Character] {
//声明数据的请求
let fetchRequest = NSFetchRequest<Character>(entityName : entity_Character)
fetchRequest.fetchOffset = 0 //查询的偏移量
fetchRequest.predicate = predicate
//查询操作
do {
let fetchedobjects = try context!.fetch(fetchRequest)
print(String.init(format:"%@.count = %i", entity_Character, fetchedobjects.count))
return fetchedobjects
}
catch {
fatalError("查询异常“:\(error)")
}
return []
}
/// 对查询结果排序
///
/// - Parameter sort: 排序规则
/// - Returns: 查询结果
func queryCharacter(sort : [NSSortDescriptor]) ->[Character] {
//声明数据的请求
let fetchRequest = NSFetchRequest<Character>(entityName : entity_Character)
fetchRequest.fetchOffset = 0 //查询的偏移量
fetchRequest.sortDescriptors = sort
//查询操作
do {
let fetchedobjects = try context!.fetch(fetchRequest)
print(String.init(format:"%@.count = %i", entity_Character, fetchedobjects.count))
return fetchedobjects
}
catch {
fatalError("查询异常“:\(error)")
}
return []
}
设置对应的查询条件
5.4.1、条件查询
//批量查询,查询某一个字段在一个数据范围内
let predicate = nspredicate(format: "name IN %@", ["佩奇", "乐迪"])
//精确查询,查询某一个字段等于特定值
let predicate = nspredicate(format: "age == %@", NSNumber(value: 4))
//模糊查询
//查询角色名称包含“小“或“猪”的记录,*号表示通配符
let predicate = nspredicate(format: "name CONTAINS %@ OR name like %@", "小", "*猪*")
//查询角色名称以猪开头的记录
let predicate = nspredicate(format: "name BEGINSWITH %@", "猪")
//查询角色名称以”迪“结尾的记录
let predicate = nspredicate(format: "name ENDSWITH %@", "迪")
//条件查询
//查询年龄大于18的记录
let predicate = nspredicate(format: "age > %@", NSNumber(value: 18))
//查询年龄介于5-30之间的
let predicate = nspredicate(format: "age BETWEEN {%@, %@}", NSNumber(value: 5),NSNumber(value: 30))
5.4.2、对结果排序
//名称降序
let nameSort = NSSortDescriptor.init(key: "name", ascending: false)
//名称升序
let nameSort = NSSortDescriptor.init(key: "name", ascending: true)
//年龄降序
let ageSort = NSSortDescriptor.init(key: "age", ascending: false)
//年龄升序
let ageSort = NSSortDescriptor.init(key: "age", ascending: true)
let array : [NSSortDescriptor] = []
array.append(ageSort)
array.append(nameSort)
queryCharacter(sort: array)
排序可以组合使用,但是需要注意先添加进数组的排序规则会优先满足,比如这里会先按年龄排序再按名称排序
5.3、数据库修改,删除
/// 删除,删除Cartoon表中所有数据.
// 因为设置了级联删除,Character表中的所有数据也会被删除
func deleteObjects() {
let array = queryCartoon()
for item in array {
context?.delete(item)
}
app?.saveContext()
}
/// 删除数据库表中所有数据,该方法慎用
func deleteObjects() {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "aaa")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try getContext().execute(deleteRequest)
} catch let error as NSError {
print(error)
}
}
/// 更新Character表数据
///
/// - Parameters:
/// - name: 姓名,主键
/// - age: 年龄,修改项
/// - Returns: 更新结果
func updateCharacter(name: String, age: Int16) -> Bool {
let predicate = nspredicate(format: "name == %@", name)
let itemArr = queryCharacter(predicate: predicate)
if itemArr.isEmpty {
return false
}
let item = itemArr[0]
item.age = age
app?.saveContext()
return true
}
6、案例源码
6.1、界面展示
6.2、下载地址
CSDN下载地址:
github源码地址:点击跳转
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。