I have noticed that setting PK is not obligatory in Realm and simply can be omitted. But in documentation is stated that:
Indexes are created automatically for primary key properties.
And I'd like to clear up some questions:
1) What is the default value for PK is defined by Realm, if I don't assign it by myself. Is it hash or whatever ? (If I don't set PK and call [MyRealmObject primaryKey] it returns nil)
2) If this implicit PK is indexed by default ? Should I worry about it, because if it is not indexed, does it mean that it affects the general performance of this Entity (for example,fetching objects) ?
3) Is it a good practice to define PK every time for each RLMObject subclass or it isn't necessary for Realm and simply may rely on it's internal realization defined by Realm itself?
(Disclaimer: I work for Realm.)
Yep! Setting a primary key in Realm isn't obligatory, nor necessary, which is why it's completely up to the developer and the requirements of the app to determine whether it's necessary or not in their implementation.
In response to your questions:
1) There are no default values; you specify one of your own properties as a primary key. primaryKey returns nil by default since you need to override that yourself in order to indicate to Realm which property you want to act as a primary key. Some users have set integers as primary keys, but more often than not, using a UUID string is the most common.
2) There's no implicit primary key. You must use the [RLMObject primaryKey] method to explicitly state which property is the primary key, and THEN it will be indexed. :)
3) In my own (spare-time) development experience, I usually find having a primary key makes it a lot easier to identify and handle specific objects. For example, if you're passing an object across threads, you can simply pass the primary key value and use [RLMObject objectForPrimaryKey:] to refetch the object. Obviously this depends on your own implementation requirements. You probably shouldn't add a primary key unless you find out you really need one.
As an example, here's what you would add to your RLMObject subclass if you wanted to set a UUID string as a primary key:
#interface MyObject : RLMObject
#property NSString *uuid;
#end
#implementation MyObject
+ (NSString *)primaryKey
{
return #"uuid";
}
+ (NSDictionary *)defaultPropertyValues
{
#{#"uuid": [[NSUUID UUID] UUIDString]};
}
#end
I hope that helped!
Addendum: To elaborate upon some of the comments made below, primary keys are explicitly necessary for any Realm APIs that change their functionality depending on if an object with the same key already exists in the database. For example +[RLMObject createOrUpdateInRealm:] will add a new Realm object to the database if an object with that primary key doesn't already exist, and will simply update the existing object otherwise.
As such, in these instances where a primary key is a critical component of the subsequent logic, they are required. However, since these APIs are a subset of the different ways in which it is possible to add/update data in Realm, if you choose to not use them, you still not required to have a primary key.
The horse has been beaten to death already, but I couldn't help but reference the Realm code which throws an exception if a Realm Object is created or updated without having a primary key.
+ (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withValue:(id)value {
// verify primary key
RLMObjectSchema *schema = [self sharedSchema];
if (!schema.primaryKeyProperty) {
NSString *reason = [NSString stringWithFormat:#"'%#' does not have a primary key and can not be updated", schema.className];
#throw [NSException exceptionWithName:#"RLMExecption" reason:reason userInfo:nil];
}
return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, true);
}
Related
I'm wondering if I can change a value of a primary key member of a composite primary key in a Grails Domain class? For example having this domain:
class StudentHistory implements Serializable {
String studentNumber
String schoolYear
Integer yearLevel
String section
Float average
String status
static mapping = {
...
id composite: ["studentNumber", "schoolYear", "yearLevel", "section"]
...
}
}
Let say, On the schoolYear: "2014-2015", a certain yearLevel: 1 student with studentNumber: "2011-488-MN-0" transferred section from section: "1D" to section: "1N". Now to perform this record update, we do something similar inside a service:
StudentHistory record = StudentHistory.find {
eq("studentNumber", "2014-488-MN-0")
eq("schoolYear", "2014-2015")
eq("yearLevel", 1)
eq("section", "1D")
}
record.setSection("1N")
record.save(flush: true, insert: false)
The problem is that the update on the primary key doesn't take effect but when I tried to update other non-Primary fields such as average and status, updating them works fine (I tried performing an SQL directly on the database to confirm). How can I update primary keys?
PS: Now, based on this design, I know some will suggest that why not just create another record, then just fetch the record that has been last entered? But what I am required to do is to update that composite primary key instead.
PPS: Please don't suggest on removing the old instance, and create a new one, copying the old details except for the section. I cannot do that since many tables are connected to this table.
I believe it is a good practice to avoid changing primary keys. Primary key is a unique identifier of an object and changing it effectively means creating a new object. So if your composite primary key is mutable (or can mutate) then you should use a surrogate key - an artificial primary key. At the same time you can create a unique constraint on the 4 fields currently being your primary key.
In your case it would be:
static mapping = {
...
}
static constraints = {
studentNumber(unique: ["schoolYear", "yearLevel", "section"])
}
Hope it makes sense.
I am developing an application in Swift and started using Core Data recently.
I must define which attribute to my entity will be my primary key. For example:
I have an entity that has the attributes of the class:
id
name
age
I need the "id" attribute is my primary key.
May be the same in Objective-C, just need to know how to define it.
Each NSManagedObject has its own unique key built in which is available in its objectID property.
This id is internally used to link entities over its relations. There is no need to maintain an own id as a primary key as you are used to do in SQL.
You can always get the id by NSManagedObject.objectID.
Fetching an object by its id can be performed directly by the managed object context using NSMananagedObjectContext.objectWithId(...)
Why do you need id to be your primary key, it's not SQL and there is not primary key (even if behind core data it's SQL you don't use SQL and primary key). Rename your attribute id in userId, or entityId. When you want to get your entity with your id use a NSPredicate:
[NSPredicate predicateWithFormat:#"(entityId == %d)", entityId]
What is the difference between transient and derived properties of a core data entity? I would like to create a "virtual" property that can be used in a fetch operation to return localized country names from a core data entity.
The operation is to be done this way:
retrieve the country name from the database in english
do a NSLocalizedString(countryNameInEnglish, nil) to obtain the localized country name.
2 is to be done by this "virtual" property.
Which one should I use? transient or derived and how do I do that?
I have nothing to show you because I have no clue what I should use.
thanks
According to Apple's guide for Non-Standard Persistent Attributes:
You can use non-standard types for persistent attributes either by using transformable attributes or by using a transient property to represent the non-standard attribute backed by a supported persistent property. The principle behind the two approaches is the same: you present to consumers of your entity an attribute of the type you want, and “behind the scenes” it’s converted into a type that Core Data can manage. The difference between the approaches is that with transformable attributes you specify just one attribute and the conversion is handled automatically. In contrast, with transient properties you specify two attributes and you have to write code to perform the conversion.
I suggest using transient attributes. Idea is that you create 2 string attributes: countryName (non-transient) and localizedCountryName (transient):
And then, in your NSManagedObjectSubclass, you simply implement a getter for localizedCountryName:
- (NSString *)localizedCountryName
{
NSString *result;
if ([self.countryName length] > 0) {
result = NSLocalizedString(self.countryName, nil);
}
return result;
}
I'm starting to create an application with Core Data, to retrieve a data for sectioned table i want to use NSFetchedResultController, in the example from apple there are two additional properties.
primitiveTimeStamp
primitiveSectionIdentifier
For the case of primitiveSectionIdentifier apple says that
In contrast, with transient properties you specify two attributes and
you have to write code to perform the conversion.
because the sectionidentifier is transient property.
But what about the timeStamp ?this attribute is not a transient, why there is a primitiveTimeStamp property ? and why there is explicit setter for timeStamp ?
- (void)setTimeStamp:(NSDate *)newDate {
// If the time stamp changes, the section identifier become invalid.
[self willChangeValueForKey:#"timeStamp"];
[self setPrimitiveTimeStamp:newDate];
[self didChangeValueForKey:#"timeStamp"];
[self setPrimitiveSectionIdentifier:nil];
}
or maybe it's not a actual setter? where is _timeStamp=newDate?
CoreData generates the accessors for you. It generates "public and primitive get and set accessor methods for modeled properties".
So in this case it has generated:
-(NSDate*)timeStamp;
-(void)setTimeStamp:;
-(NSDate*)primitiveTimeStamp;
-(void)setPrimitiveTimeStamp:;
"why there is a primitiveTimeStamp property ?"
The declaration is merely to suppress compiler warnings. ie. If you removed the declaration of the property you'd find a warning on compilation but the code would still run.
Or alternatively you could use [self setPrimitiveValue:newDate forKey:#"timeStamp"];
"why there is explicit setter for timeStamp ?"
This is required since setting the timeStamp requires the 'sectionIdentifier' to be recalculated. This is achieved by setting it no nil and letting the get accessor recalculate it lazily.
"where is _timeStamp=newDate?"
The equivalent of this is essentially done in the auto generated implementation of setPrimitiveTimeStamp.
A quote from the docs:
By default, Core Data dynamically creates efficient public and primitive get and set accessor methods for modeled properties (attributes and relationships) of managed object classes. This includes the key-value coding mutable proxy methods such as addObject: and removes:, as detailed in the documentation for mutableSetValueForKey:—managed objects are effectively mutable proxies for all their to-many relationships.
Note: If you choose to implement your own accessors, the dynamically-generated methods never replace your own code.
For example, given an entity with an attribute firstName, Core Data automatically generates firstName, setFirstName:, primitiveFirstName, and setPrimitiveFirstName:. Core Data does this even for entities represented by NSManagedObject. To suppress compiler warnings when you invoke these methods, you should use the Objective-C 2.0 declared properties feature, as described in “Declaration.”
I would like to separate my reference data from my user data in my Core Data model to simplify future updates of my app (and because, I plan to store the database on the cloud and there is no need to store reference data on the cloud as this is part of my application). Therefore, I've been looking for a while for a way to code a cross-store relationship using fetched properties. I have not found any example implementations of this.
I have a Core Data model using 2 configurations :
data model config 1 : UserData (entities relative to user)
data model config 2 : ReferenceData (entities relative to application itself)
I set up 2 different SQLite persistent stores for both config.
UserData config (and store) contains entity "User"
ReferenceData config (and store) contains entities "Type" and "Item".
I would like to create two single-way weak relationships as below :
A "User" has a unique "Type"
A "User" has many "Items"
Here are my questions :
How do I set up my properties?
Do I need 2 properties for each relation (one for storing Unique ID and another to access my fetched results)?
Could this weak relationship be ordered?
Could someone give me an example implementation of this?
As a follow-on to Marcus' answer:
Looking through the forums and docs, I read that I should use the URI Representation of my entity instance instead of objectID. What is the reason behind this?
// Get the URI of my object to reference
NSURL * uriObjectB [[myObjectB objectID] URIRepresentation];
Next, I wonder, how do I store my object B URI (NSURL) in my parent object A as a weak relationship? What attribute type should I use? How do I convert this? I heard about archive... ?
Then, later I should retrieve the managed object the same way (by unconvert/unarchive the URIRepresentation) and get Object from URI
// Get the Object ID from the URI
NSManagedObjectID* idObjectB = [storeCoordinator managedObjectIDForURIRepresentation:[[myManagedObject objectID] URIRepresentation]];
// Get the Managed Object for the idOjectB ...
And last but not least, shouId I declare two properties in my entity A, one for persisting of URI needs and another for retrieving direclty object B?
NSURL * uriObjectB [objectA uriObjectB];
ObjectB * myObjectB = [objectA objectB];
As you can read, I really miss some simple example to implement thes weak relationships ! I would really appreciate some help.
Splitting the data is the right answer by far. Reference data should not be synced with the cloud, especially since iCloud has soft caps on what it will allow an application to sync and store in documents.
To create soft references across to stores (they do not need to be SQLite but it is a good idea for general app performance) you will need to have some kind of unique key that can be referenced from the other side; a good old fashioned foreign key.
From there you can create a fetched property in the model to reference the entity.
While this relationship cannot be ordered directly you can create order via a sort index or if it has a logical sort then you can sort it once you retrieve the data (I use convenience methods for this that return a sorted array instead of a set).
I can build up an example but you really are on the right track. The only fun part is migration. When you detect a migration situation you will need to migrate each store independently before you build up your core data stack. It sounds tricky but it really is not that hard to accomplish.
Example
Imagine you have a UserBar entity in the user store and a RefBar entity in the reference store. The RefBar will then have a fetchedProperty "relationship" with a UserBar thereby creating a ToOne relationship.
UserBar
----------
refBarID : NSInteger
RefBar
--------
identifier : NSInteger
You can then create a fetched property on the RefBar entity in the modeler with a predicate of:
$FETCHED_PROPERTY.refBarID == identifier
Lets name that predicate "userBarFetched"
Now that will return an array so we want to add a convenience method to the RefBar
#class UserBar;
#interface RefBar : NSManagedObject
- (UserBar*)userBar;
#end
#implementation RefBar
- (UserBar*)userBar
{
NSArray *fetched = [self valueForKey:#"userBarFetched"];
return [fetched lastObject];
}
#end
To create a ToMany is the same except your convenience method would return an array and you would sort the array before returning it.
As Heath Borders mentioned, it is possible to add a sort to the NSFetchedProperty if you want but you must do it in code. Personally I have always found it wasteful and don't use that feature. It might be more useful if I could set the sort in the modeler.
Using the ObjectID
I do not recommend using the ObjectID or the URIRepresentation. The ObjectID (and therefore the URIRepresentation of that ObjectID) can and will change. Whenever you migrate a database that value will change. You are far better off creating a non-changing GUID.
The weak relationship
You only need a single value on the M side of the relationship and that stores the foreign identifier. In your object subclass you only need to implement accessors that retrieve the object (or objects).
I would go with just one store.
For storing stuff in the cloud, you will anyway have to serialize the data, either as JSON or SQL statements, or whatever scheme you prefer.
You will need a local copy of the data on the user's device, so he can access it quickly and offline. The cloud store can have only the user entity, while the local store (part of the app) can also have the reference entity.
I have a similar project with a huge reference store (20000 records) with geographic information, and user generated content ("posts"). I use a single store. When I ship the app, the "posts" entity is also defined but empty. When I update the data model I simply re-generate the whole reference store before shipping.
I see absolutely no reason to go for a cross store solution here.