Podczas implementacji ekranu edycji dokumentu natknąłem się na ciekawy błąd związany z ORM.

Podczas próby aktualizacji encji

DocumentFile

otrzymałem poniższy błąd:

pl.lantkowiak.sdm E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.RuntimeException: java.sql.SQLException: Cannot update class pl.lantkowiak.sdm.core.entity.DocumentFile because it doesn't have an id field
 at com.j256.ormlite.dao.RuntimeExceptionDao.update(RuntimeExceptionDao.java:264)
 at pl.lantkowiak.sdm.activities.EditDocumentActivity$$anonfun$updateFiles$2.apply(EditDocumentActivity.scala:134)
 at pl.lantkowiak.sdm.activities.EditDocumentActivity$$anonfun$updateFiles$2.apply(EditDocumentActivity.scala:130)
 at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59)
 at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
 at pl.lantkowiak.sdm.activities.EditDocumentActivity.updateFiles(EditDocumentActivity.scala:130)
 at pl.lantkowiak.sdm.activities.EditDocumentActivity.updateDocument(EditDocumentActivity.scala:101)
 at pl.lantkowiak.sdm.activities.EditDocumentActivity.onOptionsItemSelected(EditDocumentActivity.scala:45)
 at android.app.Activity.onMenuItemSelected(Activity.java:2566)
 at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:404)
 at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:167)
 at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:100)
 at android.support.v7.app.AppCompatDelegateImplV7.onMenuItemSelected(AppCompatDelegateImplV7.java:646)
 at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:811)
 at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152)
 at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:958)
 at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:948)
 at android.support.v7.widget.ActionMenuView.invokeItem(ActionMenuView.java:618)
 at android.support.v7.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:139)
 at android.view.View.performClick(View.java:4209)
 at android.view.View$PerformClick.run(View.java:17431)
 at android.os.Handler.handleCallback(Handler.java:725)
 at android.os.Handler.dispatchMessage(Handler.java:92)
 at android.os.Looper.loop(Looper.java:153)
 at android.app.ActivityThread.main(ActivityThread.java:5297)
 at java.lang.reflect.Method.invokeNative(Native Method)
 at java.lang.reflect.Method.invoke(Method.java:511)
 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
 at dalvik.system.NativeStart.main(Native Method)
Caused by: java.sql.SQLException: Cannot update class pl.lantkowiak.sdm.core.entity.DocumentFile because it doesn't have an id field
 at com.j256.ormlite.stmt.mapped.MappedUpdate.build(MappedUpdate.java:33)
 at com.j256.ormlite.stmt.StatementExecutor.update(StatementExecutor.java:402)
 at com.j256.ormlite.dao.BaseDaoImpl.update(BaseDaoImpl.java:350)
 at com.j256.ormlite.dao.RuntimeExceptionDao.update(RuntimeExceptionDao.java:261)
 at pl.lantkowiak.sdm.activities.EditDocumentActivity$$anonfun$updateFiles$2.apply(EditDocumentActivity.scala:134) 
 at pl.lantkowiak.sdm.activities.EditDocumentActivity$$anonfun$updateFiles$2.apply(EditDocumentActivity.scala:130) 
 at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59) 
 at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48) 
 at pl.lantkowiak.sdm.activities.EditDocumentActivity.updateFiles(EditDocumentActivity.scala:130) 
 at pl.lantkowiak.sdm.activities.EditDocumentActivity.updateDocument(EditDocumentActivity.scala:101) 
 at pl.lantkowiak.sdm.activities.EditDocumentActivity.onOptionsItemSelected(EditDocumentActivity.scala:45) 
 at android.app.Activity.onMenuItemSelected(Activity.java:2566) 
 at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:404) 
 at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:167) 
 at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:100) 
 at android.support.v7.app.AppCompatDelegateImplV7.onMenuItemSelected(AppCompatDelegateImplV7.java:646) 
 at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:811) 
 at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152) 
 at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:958) 
 at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:948) 
 at android.support.v7.widget.ActionMenuView.invokeItem(ActionMenuView.java:618) 
 at android.support.v7.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:139) 
 at android.view.View.performClick(View.java:4209) 
 at android.view.View$PerformClick.run(View.java:17431) 
 at android.os.Handler.handleCallback(Handler.java:725) 
 at android.os.Handler.dispatchMessage(Handler.java:92) 
 at android.os.Looper.loop(Looper.java:153) 
 at android.app.ActivityThread.main(ActivityThread.java:5297) 
 at java.lang.reflect.Method.invokeNative(Native Method) 
 at java.lang.reflect.Method.invoke(Method.java:511) 
 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833) 
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600) 
 at dalvik.system.NativeStart.main(Native Method)

Błąd sugeruje, że encja ta nie posiada pola id. Sama encja wyglądała tak:

@DatabaseTable(tableName = "documentFiles")
class DocumentFile {
  @DatabaseField(generatedId = true)
  var id: Int = _
  @DatabaseField(canBeNull = false)
  var createDate: Date = _
  @DatabaseField(canBeNull = false)
  var filename: String = _
  @DatabaseField(canBeNull = false)
  var extension: String = _
  @DatabaseField(canBeNull = false)
  var mime: String = _
  @DatabaseField(canBeNull = false)
  var description: String = _
  @DatabaseField(foreign = true, canBeNull = false)
  var document: Document = _
}

Encja oczywiście zawierała pole id, a sam zapis encji do bazy danych odbył się bez problemu.

Zanim przejdę do mojego rozwiązania to chciałbym odnieść się do znaku _, który występował przy kazdym polu, np:

@DatabaseField(generatedId = true)
var id: Int = _

Idąc za dokumentacją języka:

A variable definition var x: T = _ can appear only as a member of a template. It introduces a mutable field with type T and a default initial value. The default value depends on the type T as follows: 
0 if T is Int or one of its subrange types,
0L if T is Long,
0.0f if T is Float,
0.0d if T is Double,
false if T is Boolean,
() if T is Unit,
null for all other types T .

Czyli w przypadku pola id powinno do niego zostać przypisane domyślnie 0.

Wracając do mojego rozwiązania problemu. Jedyną zmianą, którą zrobiłem w moim kodzie to poniższa modyfikacja.

@DatabaseField(generatedId = true)
var id: Int = 0

Jak widać bezpośrednie przypisanie 0 do pola id pomogło z rozwiązaniem problemu.