How to split database schema in Realm? - realm

I need to have two different schema in one app using realm db.
It seems that should work below solution:
open class AModel : RealmObject() {
var a: Int = 0
}
open class BModel : RealmObject() {
var b: Int = 0
}
open class XModel : RealmObject() {
var x: Int = 0
}
open class YModel : RealmObject() {
var y: Int = 0
}
#RealmModule(classes = [AModel::class,BModel::class])
open class Schema1
val conf1 = new RealmConfiguration.Builder()
.name("db1.realm")
.schemaVersion(1)
.modules(Schema1())
.build();
#RealmModule(classes = [XModel::class,YModel::class])
open class Schema2
val conf2 = new RealmConfiguration.Builder()
.name("db2.realm")
.schemaVersion(2)
.modules(Schema2())
.build();
but when app starts and calls Realm.setDefaultConfiguration(conf1), it prints below error:
com.example.XModel is not part of the schema for this Realm
So it seems that I miss something in configuration but looking at docs I cannot figure out what. So what I miss?

The problem is that you set the schema version of conf2 as a second version, but the first one was never created. Change it for this:
...
val conf2 = new RealmConfiguration.Builder()
.name("db2.realm")
.schemaVersion(1)
.modules(Schema2())
.build();

Related

When using Kotlin Exposed, how do I handle missing values for numbers in an sqlite database?

I have an sqlite data base that entries similar this example:
0|some_text0|123.456||some_other_text0
1|some_text1||456.789|some_other_text1
So as you can see the 3rd and 4th column are numbers, but in some records the data in the column is empty.
I defined the interface using something similar to this example.
object Foos : IntIdTable("foos") {
val sequelId = integer("id").primaryKey()
val text0 = text("text0")
val num0 = float("num0").nullable()
val num1 = float("num1").nullable()
val text1 = text("text1")
}
class Foo(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Foo>(Foos)
var sequelId by Foos.sequelId
var text0 by Foos.text0
var num0 by Foos.num0
var num1 by Foos.num1
var text1 by Foos.text
}
If I query a record that contains a missing value and try to access the field I throw an exception such as java.lang.String cannot be cast to java.math.Float?
for example:
val x = queryResult0.num1 //fails
I can get passed this via:
val x = try { queryResult0.num1 } catch(e: Exception) { null } // works I get a value or null
My current "hacky" solution is to add a second set of getters for the fields such this:
class Foo(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Foo>(Foos)
var sequelId by PinTimings.sequelId
var text0 by Foos.text0
var num0 by Foos.num0
var num1 by Foos.num1
var text1 by Foos.text
val num0x : Float?
get() = try { num0 } catch(e: Exception) { null }
val num1x : Float?
get() = try { num1 } catch(e: Exception) { null }
}
Then queryResut0.num1x provides what I need.
What's the right way to handle this kind of situation?
FYI: I am using the following setup
Kotlin: 1.2.61
Exposed: org.jetbrains.exposed:exposed:0.17.7
JDBC: org.xerial:sqlite-jdbc:3.32.3.2

Opening SQLite database in Android Studio Kotlin

I'm very new to Kotlin so please bear with me. I want to open a SQLite database or create it if it doesn't exist yet. Afterwards I'd run the appropriate statements to make a table, etc. I'm trying to use SQLiteDatabase. The line of code I'm trying is:
val db = SQLiteDatabase.openOrCreateDatabase("database.db",null, SQLiteDatabase.OPEN_READWRITE)
I get this runtime error when I try the app:
android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 1294 SQLITE_CANTOPEN_ENOENT[1294]): Could not open database
I'm not picky about the database, I just want an easy way to read and write to one from within my function.
This is the surrounding code:
package com.google.firebase.codelab.barcode_scanning
// Date/time formatting functions
import android.database.sqlite.SQLiteDatabase
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_row.view.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
class QrCodeAdapter(private val qrList: ArrayList<QrCode>) : RecyclerView.Adapter<QrCodeAdapter.QrHolder>() {
//
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QrHolder {
return QrHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_row, parent, false))
}
override fun getItemCount() = qrList.size
override fun onBindViewHolder(holder: QrHolder, position: Int) {
//internal var dbHelper = DatabaseHelper(this)
with(qrList[position]) {
var objname = "Barcode Not Found"
var statustext = ""
// status: 0 = no record, 1 = dirty, 2 = clean, 3 = dwell, 4: dwell done
var status = 0
var lastcleaned = ""
// objcategory: 0 = no record, 1 = contact object, 2 = dwell item
var objcategory = 0
var objcategorytext = ""
// initial setup of current date/time
var unixtime = System.currentTimeMillis()
var currenttime = LocalDateTime.now()
var timeformat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")
var testtime = currenttime.format(timeformat).toString()
//val db = SQLiteDatabase.openDatabase("//app/maindb.db", null, 0);
val db = SQLiteDatabase.openOrCreateDatabase("database.db",null, null)
//val sql = "SELECT COUNT(*) FROM main"
//val statement = db.compileStatement(sql)
//val result = statement.simpleQueryForLong()
//Convert barcode output to string
var barcodevalue = this.value.toString()
// hide settings
holder.itemView.textSettingsTitle.visibility=View.GONE
holder.itemView.textboxName.visibility=View.GONE
}

How to setup H2 for Corda

I am re-writing and running a IssueFlow for an example cordapp here.
I can see the flow is successful and can find a number of UTXOs on the vault query for the node.
run vaultQuery contractStateType: com.example.state.IOUStat
I would like to view the data in the persistent store (H2).
I've added the following to my node's config (similar for party A node with different port).
devMode=true
myLegalName="O=PartyB,L=New York,C=US"
p2pAddress="localhost:10008"
rpcSettings {
address="localhost:10009"
adminAddress="localhost:10049"
}
security {
authService {
dataSource {
type=INMEMORY
users=[
{
password=test
permissions=[
ALL
]
user=user1
}
]
}
}
}
h2Settings {
address: "localhost:12344"
}
I can see the DB url on run-nodes
jdbc:h2:tcp://localhost:12344/node
I can successfully connect to this db url. However , I do not see any tables for my Persistent state
DB Query
object IOUSchema
object IOUSchemaV1 : MappedSchema(
schemaFamily = IOUSchema.javaClass,
version = 1,
mappedTypes = listOf(PersistentIOU::class.java)){
#Entity
#Table(name = "iou_states")
class PersistentIOU(
#Column(name = "lender")
var lenderName : String,
#Column (name = "borrower")
var borrowerName : String,
#Column(name = "value")
var value : Int,
#Column(name = "linear_id")
var linearId : UUID
) : PersistentState(){
constructor() : this("","",0, UUID.randomUUID())
}
}
#BelongsToContract(IOUContract::class)
data class IOUState (val value : Int,
val lender: Party,
val borrower : Party,
override val linearId: UniqueIdentifier = UniqueIdentifier()): LinearState, QueryableState {
override val participants : List<AbstractParty> get() = listOf(lender,borrower)
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema){
is IOUSchemaV1 -> IOUSchemaV1.PersistentIOU(
this.lender.name.toString(),
this.borrower.name.toString(),
this.value,
this.linearId.id
)
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(IOUSchemaV1)
}
EDIT: This certainly works by connecting directly to the file. See here, However this looks like it is connecting to a different db when using a external client (dbeaver) to connect to the JDBC url emmited on node startup.
I was unable to find tables in H2 instance as my connection string was wrong & H2 appears is creating a blank db when trying to connect to a non-existant DB.
In DBeaver , you can add the JDBC url, but it automatically pre-fixes 'jdbc:h2:'
Connection details

Migration is required due to Property id has been made required

it works fine if i change lateinit var id: String in the Payment.kt and CartPayment.kt to var id: String? = "", but the problem is i want the id to be required, how can i achieve that ?
The Error:
java.lang.RuntimeException: Unable to create application: io.realm.exceptions.RealmMigrationNeededException: Migration is required due to the following errors:
- Property 'CartPayment.id' has been made required.
- Property 'Payment.id' has been made required.
Model :
open class Payment() : RealmObject() {
#PrimaryKey
lateinit var id: String
var typeValue: Int = 0
var statusValue: Int = 0
var value: Double = 0.0
var referenceNumber: String? = null
Note: Payment and CartPayment models are identical except for the class name
Migration.kt
class Migration : RealmMigration {
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
var oldVersion = oldVersion
val schema = realm.schema
if (oldVersion == 0L) {
schema.create("Payment")
.addField("id", String::class.java, FieldAttribute.PRIMARY_KEY)
.addField("typeValue", Int::class.java)
.addField("statusValue", Int::class.java)
.addField("value", Double::class.java)
.addField("referenceNumber", String::class.java)
schema.get("Order")!!
.addRealmListField("payments", schema.get("Payment")!!)
oldVersion++
}
if (oldVersion == 1L) {
schema.create("CartPayment")
.addField("id", String::class.java, FieldAttribute.PRIMARY_KEY)
.addField("typeValue", Int::class.java)
.addField("statusValue", Int::class.java)
.addField("value", Double::class.java)
.addField("referenceNumber", String::class.java)
schema.get("Order")!!
.addField("cashPaymentAmount", Float::class.java)
.addField("change", Float::class.java)
oldVersion++
}
}
}
App.kt
class App: Application() {
override fun onCreate() {
super.onCreate()
Realm.init(this)
val realmConfig = RealmConfiguration.Builder()
.schemaVersion(2)
.migration(Migration())
.build()
Realm.getInstance(realmConfig)
Realm.setDefaultConfiguration(realmConfig)
Fresco.initialize(this)
}
}
.addField("id", String::class.java, FieldAttribute.PRIMARY_KEY, FieldAttribute.REQUIRED)
did the trick.
if you declare the variable to be lateinit, make sure to add FieldAttribute.REQUIRED.
Basically you are adding a new field "id" which is primary key (hence required key).
If you do not specify any value while initialisation (lateinit), how will realm migrate all the earlier records, which doesn't have an id, but is required after migration ? Hence the error.
Below solutions might work
Either pre-populate the id's (without using lateinit)
Transform your earlier records to have id's if they don't have
Check the official examples here
For me, it happened after I have done the Migration.
I have made a non-nullable object in Kotlin and on migration I was creating a nullable wrapper type like Double, Int, etc.
Just use
Double::class.java
instead of
Double::class.javaObjectType

AnKo SQLite : populate listview asynchronously from database?

I'm trying to translate my app from Java to Kotlin.
I'm managing database with AnKo SQLite
All is OK except listviews with CursorLoaders : I can't find how to replace CursorLoader while using AnKo SQLite.
(and same problem with expandableListViews)
Can somebody help me please?
OK, here is my solution... I don't know if it is the best :
create a new kotlin class "MyCursorLoader" that extends CursorLoader
set the primary constructor like this :
class MyCursorLoader(
mContext: Context,
val mTableName: String,
var mProjection: Array<String>? = null,
var mSelection: String = "1",
var mSelectionArgs: Array<String> = emptyArray(),
var mGroupBy: String = MySqlHelper.ID,
var mHaving: String = "",
var mSortOrder: String = "${MySqlHelper.ID} ASC",
var mLimit: String = "",
var mDistinct: Boolean = true
): CursorLoader(mContext) {
val mObserver: Loader<Cursor>.ForceLoadContentObserver = Loader<Cursor>(mContext).ForceLoadContentObserver()
var mCancellationSignal: CancellationSignal? = null
override the OnLoadInBackground method with te same code than built-in one, just replacing the val cursor = ContentResolverCompat.query(... line with :
val cursor = MySqlHelper.instance.readableDatabase.query(
mDistinct, mTableName, mProjection, mSelection, mSelectionArgs, mGroupBy, mHaving, mSortOrder, mLimit, mCancellationSignal)
So no need to recreate a dataprovider in manifest, no need to deal with Uri's... I can use MyCursorLoader exactly like built-in CursorLoader, calling it like this :
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
when (id) {
DAY_HEADER_LOADER ->
return MyCursorLoader(mContext, TABLE_EVENTS, arrayOf(ID, DAY), mGroupBy = DAY, mSortOrder = "$DAY DESC")
...
}
}
Let me know if ther is a better solution.
Hope that can help.

Resources