Being able to populate data in a simple data store is great, but that is usually not good enough for any real world application. Most real world models would have some relationship between classes.
Let’s take our Note example. If you have no idea what example I’m talking about, maybe start here first.
Let’s say we needed to associate our notes to a specific case. The first step would be to modify the NoteORM unit to look like this:
unit NoteORM; interface uses Classes, SynCommons, mORMot; type TNoteCase = class(TSQLRecord) private FDescription: RawUTF8; published property Description: RawUTF8 read FDescription write FDescription; end; TNote = class(TSQLRecord) private FBody: RawUTF8; FTitle: RawUTF8; FNoteCase: TNoteCase; published property NoteCase: TNoteCase read FNoteCase write FNoteCase; property Body: RawUTF8 read FBody write FBody; property Title: RawUTF8 index 80 read FTitle write FTitle; end; implementation end.
Here we’ve added the TNoteCase class, and added the NoteCase property to TNote. NoteCase is of type TNoteCase.
Next, we add the new TNoteCase class to our model:
RestServer := TSQLRestServerDB.CreateWithOwnModel( [NoteORM.TNote, NoteORM.TNoteCase], 'Test.DB' );
And now we can generate some data:
procedure TestIt(rest:TSQLRest); var aCase : NoteORM.TNoteCase; aNote : NoteORM.TNote; begin aCase := NoteORM.TNoteCase.Create; try aCase.Description := 'Case 1'; rest.Add( aCase, true ); aNote := NoteORM.TNote.Create; try aNote.Title := 'Note 1'; aNote.Body := 'Note 1 body. Lots of other stuff too.'; aNote.NoteCase := aCase.AsTSQLRecord; rest.Add( aNote, true ); aNote.Title := 'Note 2'; aNote.Body := 'Note 2 body. Lots of other stuff too. Some more things here.'; rest.Add( aNote, true ); finally aNote.Free; end; finally aCase.Free; end; end;
The most important bit to notice is:
aNote.NoteCase := aCase.AsTSQLRecord;
This is because mORMot stores the referential key, and not the actual instance. That means that under normal circumstances you can not access aNote.NoteCase.Description, as this will generate an Access Violation.
Once the records have been committed to the storage engine, however, there are other ways of dealing with these classes.
One way is by using the CreateJoined constructor:
procedure TestIt(rest:TSQLRest); var aNote : NoteORM.TNote; begin aNote := NoteORM.TNote.CreateJoined( rest, 4); try writeln( 'Case: ', aNote.NoteCase.Description ); writeln( 'Title: ', aNote.Title ); writeln( 'Body: ', aNote.Body ); finally aNote.Free; end; end;
This will auto load and populate and manage all the contained instances. This method can be slow, especially if the class graph is big.
The other option is to use lazy loading, where classes are created and managed as and when they’re required:
procedure TestIt(rest:TSQLRest); var aCase : NoteORM.TNoteCase; aNote : NoteORM.TNote; begin aNote := NoteORM.TNote.Create( rest, 4); try aCase := NoteORM.TNoteCase.Create( rest, aNote.NoteCase ); try writeln( 'Case: ', aCase.Description ); writeln( 'Title: ', aNote.Title ); writeln( 'Body: ', aNote.Body ); finally aCase.Free; end; finally aNote.Free; end; end;
Reblogged this on burningrump.
LikeLike
aNote := NoteORM.TNote.Create( rest, 4);
This method will return the joined record but… if we don’t know the note id?
LikeLike
Refer to https://tamingthemormot.wordpress.com/2015/07/14/investigating-the-orm-retrieving-data/ where I investigate finding records using the ORM
LikeLike
In the lazy loading sample, it shows that the aNote.NoteCase can have two states in aNote, either loaded or not, so my question is how do we know if aNote.NoteCase is loaded ? Imaging, if we are passing aNote around, have ability to know that is very important.
LikeLike