Posts Tagged ‘ORM’
Persist domain models in Android using ORMLite
Recent days I am spending my spare time writing a small app for Android. As usual I have to deal with persistence. I really don’t want to write a persistence mechanism for myself. I started searching for an ORM in Android world. After some evaluation I decided to use ORMLite. It did a pretty good job of mapping my models to db. ORMLite use annotation based configuration, I will go into details later in this post. ORMLite will support entity relationship, lazy loading, etc. Another big advantage is, it’s an open source. Downside is it’s file size, above 200kb (great powers comes with some file size). I felt very comfortable with ORMLite and decided to use it in my app.
I modified the ContactList test application to see how ORMLite works. Let’s go through in detail.
-
Download ormlite-android-4.24.jar and ormlite-core-4.24.jar from sourceforge.
-
Refer the jar files in ContactList project.
Note for non java devs. In Eclipse to refer a jar file, In navigator window right click on the project and select properties. Select Java Build Path from the list and click on Libraries tab. Click on ‘Add External JARs’ button and point to the required jar files.
Next we need to annotate the Contact entity to specify the mapping. Let’s see the Contact entity.
package com.contactlist; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; @DatabaseTable(tableName="contact") public class Contact { Contact(){//used by ORMLite} public Contact(String id, String displayName) { _id=id; _displayName=displayName; } @DatabaseField(columnName="id") private String _id; @DatabaseField(columnName="display_name") private String _displayName; public String getId(){return _id;} public String getDisplayName(){return _displayName;} }
As you can see the Contact class is annotated with DatabaseTable and specified the table to map. Also the private field is annotated with DatabaseField to map, columnName is optional. If not mentioned then ORMLite will use the variable name as the table column name. Another advantage is, ORMLite works on private variable, so we can make our class immutable.
You can see the basics of using ORMLite in Android here. As per the guideline we need to create a dbHelper extends from OrmLiteSqliteOpenHelper. Let’s see the db helper of ContactList app.
package com.contactlist.repository; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.util.Log; import com.contactlist.*; import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; import com.j256.ormlite.support.ConnectionSource; import com.j256.ormlite.table.TableUtils; public class DBHelper extends OrmLiteSqliteOpenHelper { private static DBHelper _helperInstance; public static DBHelper getInstance(Context context) { if(_helperInstance==null) _helperInstance=new DBHelper(context); return _helperInstance; } public DBHelper(Context context) { super(context, AppConstants.DATABASE_NAME, null, AppConstants.DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase sqliteDatabase, ConnectionSource connectionSource) { try { TableUtils.createTable(connectionSource, Contact.class); } catch (Exception e) { Log.e(AppConstants.LOG_NAME, "could not create table Contact",e); } } @Override public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) { } @Override public void close() { super.close(); _helperInstance=null; } }
When we implement dbHelper we need to override onCreate and onUpgrade as shown below. onCreate will get called when the app is installed for the first time. onUpgrade will get called when and upgrade to the app, so that we can add scripts for creating new tables, etc. In the above code in onCreate I created the table called contact using one of the class provided by ORMLite.
I done with all the infrastructure stuff. It’s the time to create our repository class to persist our Contacts to a Sqlite db.
package com.contactlist.repository; import java.io.InputStream; import java.sql.SQLException; import java.util.List; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.provider.ContactsContract; import com.contactlist.*; import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; import com.j256.ormlite.dao.Dao; /** * @author sony arouje * */ public class ContactRepository { private ContentResolver _contentResolver; private Context _context; public ContactRepository(ContentResolver contentResolver, Context context) { this._contentResolver = contentResolver; this._context=context; } public List<Contact> getContacts() { ContactList contactList = new ContactList(); Uri uri = ContactsContract.Contacts.CONTENT_URI; String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; Cursor cur = _contentResolver.query(uri, null, null, null, sortOrder); if (cur.getCount() > 0) { String id; String name; while (cur.moveToNext()) { id = cur.getString(cur .getColumnIndex(ContactsContract.Contacts._ID)); name = cur .getString(cur .getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); Contact c = new Contact(id,name); contactList.addContact(c); } } cur.close(); return contactList.getContacts(); } public void saveContacts(List<Contact> contacts) throws SQLException { OrmLiteSqliteOpenHelper dbHelper= DBHelper.getInstance(_context); Dao<Contact, Integer> daoContact=dbHelper.getDao(Contact.class); for (Contact contact : contacts) { daoContact.create(contact); } dbHelper.close(); } }
In the above code I persist the Contacts using saveContacts(). I don’t think it really need any explanation, it’s a pretty simple function.
For testing I called the save function ContactListActivity as shown below
_contactRepo=new ContactRepository(getContentResolver(),getApplicationContext()); List<Contact> contactList=_contactRepo.getContacts(); try { _contactRepo.saveContacts(contactList); } catch (SQLException e) { e.printStackTrace(); }
If you want to verify your db, you can do it very easily through eclipse. You can find a detailed step by step description here to view the db created in the emulator. I use SQLiteSpy to browse the SQlite db.
Download source here (ContactList_ORMLite.zip).
Entity Framework Vs NHibernate
I worked on EF POCO model for some times and I come across with several stopovers. EF doesn’t have a very good error reporting, it will just throw some error and we need to run pillar to post to find out why on earth we are getting this error. Another issue is working in disconnected mode, I will explain in detail later in this post. For testing I used EF CTP5, hopefully things might have improved in the latest release.
After dating with EF for quiet some time, I realized that EF is not matured enough to hang around with, it’s still evolving and need time. I thought I need to look around and see any other beautiful and healthy ORM’s around. Yes I do aware of NHibernate a powerful ORM in the market for quiet a long period. So I decided to see in depth. I decided to write a small app to compare both ORM’s. For my comparison I used FluentNHibernate, I like to map my entities to table using code rather than an xml.
I haven’t completed the comparison yet. One of the important focus of my test is how CRUD operation works in disconnected model. In connected model the context can keep track of the changes and do the necessary Create/Update/Delete operation without much issue. So my test app have a WCF service to supply data to presentation layer.
First Test
The presentation layer issue a search request and get an author object from the WCF service, make some changes to author data and send it back to service for persisting it.
Entity Framework: Presentation layer get author object, did some changes to First Name. Sent the modified object to Service for persisting. In the persistence layer attached the author object to ObjectContext and called SaveChanges(). But nothing happened, EF couldn’t recognize the changes made to the object and didn’t persist the changes.
NHibernate: Presentation layer get author object, did some changes to First Name. Sent the modified object to Service for persisting. In the persistence layer make a call to SaveOrUpdate() of Session object with the modified entity. I could see that the changes get persisted to the db.
Test Summary
In my first test I could see that update operation in disconnected mode is pretty easy in NHibernate than EF. I am not sure whether am doing some thing wrong in EF thus it’s not persisting. But from a devs stand point we really don’t want to do much stuffs with an ORM to persist any changes. It’s ORM’s headache to figure out whether the entity state is dirty or not, if dirty then update it.
After this small test I feel like I need to embrace NHibernate as my ORM. Also one of the good feature of NHibernate is it’s error reporting. NH will report error with sql statement it issued to db, it will be helpful for a dev to analyze the error and take necessary actions.
EF might be strong in Autogenerated code, with EDMX, blah, blah. But not yet matured enough to support POCO model. In my point of view NHibernate is the right ORM to choose in comparison with Entity Framework, if you are serious in POCO model.
Note: NHibernate uses lazy loading extensively and you may have some trouble in WCF side while serializing it. It’s advisable to use DTO to transfer data between layers. Trent Foley have a nice post to explain how to perform eager loading with NHibernate.
This is just an initial test and I will move forward to test different scenarios. If I get time I will post the results in future posts.
Overview of Test Application
The test application consist of Presention layer (Winform), WCF service, Domain model and Persistence layer. The service layer and the persistence layer is loosely coupled. At any point of time Service layer doesn’t know to whom he is interacting, it can be EF repository instance or NH instance, I achieved it by Caste Windsor IOC. This loosely coupled approach help me to compare both ORM without any single code change rather with a xml configuration change.
For the test app I used Pubs database. One of domain entity is shown below.
namespace ORM.Domain { [DataContract] public class Author { [DataMember] public virtual string Id { get; private set; } [DataMember] public virtual string LastName { get; set; } [DataMember] public virtual string FirstName { get; set; } [DataMember] public virtual string Phone { get; set; } [DataMember] public virtual string Address { get; set; } [DataMember] public virtual IList<Titles> TitlesAuthored { get; set; } public virtual bool Contract { get; set; } public Author() { this.TitlesAuthored = new List<Titles>(); } public virtual void AddTitles(Titles titles) { this.TitlesAuthored.Add(titles); titles.Authors.Add(this); } }
It’s just a anemic entity without much responsibility. Anyway my focus is not in DDD, so just bare with my entity structure.
Now I need to map my entity to Author table. I used fluent API’s of the ORM to do the mapping.
Entity Framework Mapping
public class AuthorConfiguration:EntityTypeConfiguration<Author> { public AuthorConfiguration() { HasKey(a => a.Id); HasMany(a => a.TitlesAuthored); Property(a => a.Id).HasColumnName("au_id"); Property(a => a.Address).HasColumnName("address"); Property(a => a.FirstName).HasColumnName("au_fname"); Property(a => a.LastName).HasColumnName("au_lname"); Property(a => a.Phone).HasColumnName("phone"); Property(a => a.Contract).HasColumnName("contract"); ToTable("dbo.authors"); } }
FluentHibernate Mapping
public class AuthorMap:ClassMap<Author> { public AuthorMap() { Id(x => x.Id,"au_id"); Map(x => x.LastName, "au_lname"); Map(x => x.FirstName, "au_fname"); Map(x => x.Address, "address"); Map(x => x.Phone, "phone"); Map(x => x.Contract, "contract"); HasManyToMany(t => t.TitlesAuthored).Cascade.All().Table("titleauthor")
.ChildKeyColumn("title_id")
.ParentKeyColumn("au_id");Table("dbo.authors"); } }
What ever I posted here is just my findings. Don’t consider this as a benchmark. You better consider your scenarios and do a small tracer and find out which suites your requirement. Pay careful attention before deciding on any ORM else you will suffer later.
I think it’s better to stop here, you all can look into my source code for more details. I uploaded my test application to Sky drive, if interested pls have a look at it.