如何在一个 NSFetchRequest 中获取核心数据关系?

Posted

技术标签:

【中文标题】如何在一个 NSFetchRequest 中获取核心数据关系?【英文标题】:How to fetch core data relationships in one single NSFetchRequest? 【发布时间】:2016-06-02 14:52:03 【问题描述】:

我有一个核心数据应用程序,其模型设置如下图所示:

关系是这样设置的:

类别>子类别>项目

现在,是否有可能在一次提取中从具有名为 categoryName 的相同属性的 Subcategory 和 Item 实体中获取对象?我认为这可以通过使用关系来完成,但是我使用的代码不起作用:

let fetchNote = NSFetchRequest(entityName: "Category")
    fetchNote.predicate = NSPredicate(format: "ANY subcategory.name == %@ AND item.name == %@", "Badaue", "Badaue")
    let notes = try! appDel.managedObjectContext.executeFetchRequest(fetchNote) as? [Category]
    print(notes)

【问题讨论】:

什么不起作用? 请说明您要获取的实体 - 每次获取都将返回一个数组,其中包含 要么 类别对象, 子类别对象,项对象,但不是不同实体的混合。 当然。例如,我想获取 Category 实体并从 Subcategory 和 Item 实体中获取结果,这些实体具有名为“categoryName”的属性,值为“Badaue”。 对不起,我还是很困惑:如果你获取 Category 实体,你会从 Category 实体中得到结果。您可以使用谓词限制结果,但不能使用谓词从不同实体“获取结果”。 那么建立关系有什么用呢?我虽然可以通过关系获取数据。 【参考方案1】:

为避免混淆,我建议将 item 重命名为 items 以反映这是一对多的关系。 subcategory 也是如此。

EDIT2 - 您要求获取具有特定categoryNameSubcategoryItem 对象,对吗?您上面编写的查询根本没有这样做。

首先,是的,您需要 2 个查询,因为您一次只能获取 1 个 Entity 类型。

其次,让我们重新编写你的语法,因为现在它非常危险。

do 
  let predicate = NSPredicate(format: "categoryName == %@", "yourCategoryHere")
  let fetchSubcategory = NSFetchRequest(entityName: "Subcategory")
  fetchSubcategory.predicate = predicate
  if let subCategoryResults = try context.executeFetchRequest(fetchSubcategory) as? [Subcategory] 
    //do stuff
  
  let fetchItem = NSFetchRequest(entityName: "Item")
  fetchItem.predicate = predicate
  if let itemResults = try context.executeFetchRequest(fetchItem) as? [Item] 
    //do stuff
  
catch 
  print(error)

【讨论】:

应用程序正在停止,当我设置异常断点时,它指向这行代码:let notes = try! appDel.managedObjectContext.executeFetchRequest(fetchNote) 为? [Category] ​​但它在日志屏幕上不输出任何内容。 如果我禁用异常断点,输出日志为:*** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“此处不允许使用多对多键” 正如我所怀疑的那样 - 你不能查询 to-many 关系的 to-many 关系。您需要编写两个单独的查询。【参考方案2】:

如果您获取Categories,您将获得Category 对象的数组。这些对象中的每一个都有一个属性subcategory,它是一个包含所有相关Subcategory 对象的集合。这些对象中的每一个都有一个属性item,它是一个包含与Subcategory 相关的所有Item 对象的集合。相关对象“嵌套”在CategorySubcategory 对象中。

当您获取类别时,您不需要指定谓词来获取相关对象。只要您访问subcategoryitem 属性,它们就会自动获取。因此,例如,如果 myCategory 是您获取的 Category 对象,

let mySubcategories = myCategory.subcategory
for subcat in mySubcategories 
    print("\(subcat)")

应该打印每个相关的子类别。

如果这些属性为 nil,则可能是您尚未建立关系。这通常在您第一次创建对象时完成。例如,如果您使用categoryName = "Badaue" 创建一个Subcategory,您可以像这样与Categoryname = "Badaue" 建立关系:

let newSubcategory = ....
newSubcategory.categoryName = "Badaue"
let fetchCategory = NSFetchRequest(entityName: "Category")
fetchCategory.predicate = NSPredicate(format: "name == %@", newSubcategory.categoryName)
let categories = try! appDel.managedObjectContext.executeFetchRequest(fetchNote) as? [Category]
if (categories!.count > 0) 
    let firstCategory = categories[0]
    newSubCategory.category = firstCategory
 else 
    // no existing Category with the correct name, so create one....
    let newCategory = ....
    // ... and then establish the relationship to the Subcategory
    newSubCategory.category = newCategory

【讨论】:

【参考方案3】:

应该这样尝试: 我有两个实体 1. 触发器 2. 数字 关系是多对多的:- 一个触发器可以附加一个或多个手机号码,而该号码可能是触发器表中多个触发器的一部分。

步数:1

步骤:2

现在你会遇到这样的情况:

Xcode生成的代码是这样的。

import Foundation
import CoreData


extension MobileNumber 

@nonobjc public class func fetchRequest() ->  NSFetchRequest<MobileNumber> 
    return NSFetchRequest<MobileNumber>(entityName: "MobileNumber");


@NSManaged public var mNumber: String?
@NSManaged public var title: String?
@NSManaged public var triggers: NSSet?



// MARK: Generated accessors for triggers
extension MobileNumber 

@objc(addTriggersObject:)
@NSManaged public func addToTriggers(_ value: Trigger)

@objc(removeTriggersObject:)
@NSManaged public func removeFromTriggers(_ value: Trigger)

@objc(addTriggers:)
@NSManaged public func addToTriggers(_ values: NSSet)

@objc(removeTriggers:)
@NSManaged public func removeFromTriggers(_ values: NSSet)

同样,您也会为其他实体生成类。 我已经定义了一个用于获取具有 UUID 的实体的函数,它对于我的 Trigger 表的所有行都是唯一的。

 func fetchTriggerWithUUID(identifier: String) -> [Trigger]
  
    let managedObjectContext =  (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Trigger")
    let predicate = NSPredicate(format: "uuid = %@", identifier)

    fetchRequest.predicate = predicate

    do 

        let results = try managedObjectContext.fetch(fetchRequest) as! [Trigger]

        return results

     catch let error as NSError 

        print(error)

    

    return []

现在,无论我需要处理任何特定 [row] 的数据,或者您甚至可以处理一组 [rows],这在我的例子中都非常简单。

    let result = AppDelegate.getAppDelegate().fetchTriggerWithUUID(identifier: notifIdentifier).first

        numbers.removeAll()

        for num in (result?.numbers)!
        
            let numHere = num as! MobileNumber
            numbers.append(numHere.mNumber!)


        

        let messageHere = "Wanted to inform you! \n" +  (result?.title)! + "\n" + (result?.desc)!

您可以在 [.xcdatamodelID] 文件中使用尽可能多的实体。

【讨论】:

以上是关于如何在一个 NSFetchRequest 中获取核心数据关系?的主要内容,如果未能解决你的问题,请参考以下文章

核心数据 NSFetchRequest 按类别排序方法返回值

你如何构造 NSFetchRequest setHavingPredicate: 的谓词?

如何在类方法中执行 Core Data NSFetchRequest?

在 NSArray 中获取 NSFetchRequest 的所有结果

NSFetchRequest 如何知道哪个托管对象上下文在范围内?

如何使用 NSExpression 使用自定义函数设置 NSFetchRequest propertiesToFetch