@Persistent
and @Entity
, each of which allows you to specify in an explicit form the version of the described entity. Within the framework of this article, we will use only the @Entity
annotation, in the following ones - light will be shed on @Persitent
@Entity(version = SampleDBO.schema) class SampleDBO private constructor() { companion object { const val schema = 1 } @PrimaryKey lateinit var id: String private set @SecondaryKey(relate = Relationship.MANY_TO_ONE) lateinit var name: String private set constructor(id: String, name: String): this() { this.id = id this.name = name } }
@PrimaryKey
annotation of the java.lang.Long
type, you can also specify the sequence parameter, which will create a separate sequence for generating the identifiers of your entities. Alas, in Kotlin does not work.
@SecondaryKey
annotation @SecondaryKey
be present in each entity field that we want to index later on. In this case, this is the name field.
@Entity(version = SampleDBO.schema) class SampleDBO private constructor() { companion object { const val schema = 1 } @PrimaryKey lateinit var id: String private set @SecondaryKey(relate = Relationship.MANY_TO_ONE) var name: String? = null private set(value) { if(value == null) { throw IllegalArgumentException("Illegal name passed: ${value}. Non-null constraint failed") } if(value.length < 4 || value.length > 16) { throw IllegalArgumentException("Illegal name passed: ${value}. Expected length in 4..16, but was: ${value.length}") } } constructor(id: String, name: String): this() { this.id = id this.name = name } }
Relationship.ONE_TO_ONE
Relationship.ONE_TO_MANY
Relationship.MANY_TO_ONE
Relationship.MANY_TO_MANY
@SecondaryKey
with three additional parameters:
relatedEntity
- class of entity, the relation to which is describedonRelatedEntityDelete
- behavior when deleting an entity (interrupting a transaction, zeroing references, cascading deletion)name
- the entity field that acts as foreign key @Entity(version = CustomerDBO.schema) class CustomerDBO private constructor() { companion object { const val schema = 1 } @PrimaryKey() var id: String? = null private set @SecondaryKey(relate = Relationship.ONE_TO_ONE) lateinit var email: String private set var balance: Long = 0L constructor(email: String, balance: Long): this() { this.email = email this.balance = balance } constructor(id: String, email: String, balance: Long): this(email, balance) { this.id = id } override fun toString(): String { return "CustomerDBO(id=$id, email=$email, balance=$balance)" } }
@Entity(version = ProductDBO.schema) class ProductDBO { companion object { const val schema = 1 } @PrimaryKey() var id: String? = null private set @SecondaryKey(relate = Relationship.MANY_TO_ONE) lateinit var name: String private set var price: Long = 0L var amount: Long = 0L private constructor(): super() constructor(name: String, price: Long, amount: Long): this() { this.name = name this.price = price this.amount = amount } constructor(id: String, name: String, price: Long, amount: Long): this(name, price, amount) { this.id = id } override fun toString(): String { return "ProductDBO(id=$id, name=$name, price=$price, amount=$amount)" } }
@Entity(version = ProductChunkDBO.schema) class ProductChunkDBO { companion object { const val schema = 1 } @PrimaryKey() var id: String? = null private set @SecondaryKey(relate = Relationship.MANY_TO_ONE, relatedEntity = OrderDBO::class, onRelatedEntityDelete = DeleteAction.CASCADE) var orderId: String? = null private set @SecondaryKey(relate = Relationship.MANY_TO_ONE, relatedEntity = ProductDBO::class, onRelatedEntityDelete = DeleteAction.CASCADE) var itemId: String? = null private set var amount: Long = 0L private constructor() constructor(orderId: String, itemId: String, amount: Long): this() { this.orderId = orderId this.itemId = itemId this.amount = amount } constructor(id: String, orderId: String, itemId: String, amount: Long): this(orderId, itemId, amount) { this.id = id } override fun toString(): String { return "ProductChunkDBO(id=$id, orderId=$orderId, itemId=$itemId, amount=$amount)" } }
@Entity(version = OrderDBO.schema) class OrderDBO { companion object { const val schema = 1 } @PrimaryKey() var id: String? = null private set @SecondaryKey(relate = Relationship.MANY_TO_ONE, relatedEntity = CustomerDBO::class, onRelatedEntityDelete = DeleteAction.CASCADE) var customerId: String? = null private set @SecondaryKey(relate = Relationship.ONE_TO_MANY, relatedEntity = ProductChunkDBO::class, onRelatedEntityDelete = DeleteAction.NULLIFY) var itemChunkIds: MutableSet<String> = HashSet() private set var isExecuted: Boolean = false private set private constructor() constructor(customerId: String, itemChunkIds: List<String> = emptyList()): this() { this.customerId = customerId this.itemChunkIds.addAll(itemChunkIds) } constructor(id: String, customerId: String, itemChunkIds: List<String> = emptyList()): this(customerId, itemChunkIds) { this.id = id } fun setExecuted() { this.isExecuted = true } override fun toString(): String { return "OrderDBO(id=$id, customerId=$customerId, itemChunkIds=$itemChunkIds, isExecuted=$isExecuted)" } }
Environment
and EntityStore
. Each of them provides an impressive list of different options.
val environment by lazy { Environment(dir, EnvironmentConfig().apply { transactional = true allowCreate = true nodeName = "SampleNode_1" cacheSize = Runtime.getRuntime().maxMemory() / 8 offHeapCacheSize = dir.freeSpace / 8 }) }
transactional
- set to true
if you want to use transactionsallowCreate
- set to true
if the environment is to be created, if it is not found in the specified directorynodeName
- set the name for a configurable Environment; A very nice option, if the application will use several Environment, and you cacheSize
- the amount of memory that will be allocated under the in-memory cacheoffHeapCacheSize
- the amount of memory that will be allocated for disk cache val store by lazy { EntityStore(environment, name, StoreConfig().apply { transactional = true allowCreate = true }) }
SELECT * FROM customers ORDER BY email;
@PrimaryKey
our entity. The second is secondary, corresponding to the field, the ordering of which is performed ( note — the field should, as was said above, be annotated as @SecondaryKey
).
val primaryIndex: PrimaryIndex<String, CustomerDBO> by lazy { entityStore.getPrimaryIndex(String::class.java, CustomerDBO::class.java) } val emailIndex: SecondaryIndex<String, String, CustomerDBO> by lazy { entityStore.getSecondaryIndex(primaryIndex, String::class.java, "email") }
EntityCursor
)
fun read(): List<CustomerDBO> = emailIndex.entities().use { cursor -> mutableListOf<CustomerDBO>().apply { var currentPosition = 0 val count = cursor.count() add(cursor.first() ?: return@apply) currentPosition++ while(currentPosition < count) { add(cursor.next() ?: return@apply) currentPosition++ } } }
SELECT * FROM orders WHERE customer_id = ?;
fun readByCustomerId(customerId: String): List<OrderDBO> = customerIdIndex.subIndex(customerId).entities().use { cursor -> mutableListOf<OrderDBO>().apply { var currentPosition = 0 val count = cursor.count() add(cursor.first() ?: return@apply) currentPosition++ while(currentPosition < count) { add(cursor.next() ?: return@apply) currentPosition++ } } }
@Entity(version = CustomerDBO.schema) class CustomerDBO private constructor() { companion object { const val schema = 1 } @PrimaryKey() var id: String? = null private set @SecondaryKey(relate = Relationship.ONE_TO_ONE) lateinit var email: String private set @SecondaryKey(relate = Relationship.MANY_TO_ONE) lateinit var country: String private set @SecondaryKey(relate = Relationship.MANY_TO_ONE) lateinit var city: String private set var balance: Long = 0L constructor(email: String, country: String, city: String, balance: Long): this() { this.email = email this.country = country this.city = city this.balance = balance } constructor(id: String, email: String, country: String, city: String, balance: Long): this(email, country, city, balance) { this.id = id } }
val countryIndex: SecondaryIndex<String, String, CustomerDBO> by lazy { entityStore.getSecondaryIndex(primaryIndex, String::class.java, "country") } val cityIndex: SecondaryIndex<String, String, CustomerDBO> by lazy { entityStore.getSecondaryIndex(primaryIndex, String::class.java, "city") }
SELECT * FROM customers WHERE country = ? AND city = ?;
fun readByCountryAndCity(country: String, city: String): List<CustomerDBO> { val join = EntityJoin<String, CustomerDBO>(primaryIndex) join.addCondition(countryIndex, country) join.addCondition(cityIndex, city) return join.entities().use { cursor -> mutableListOf<CustomerDBO>().apply { var currentPosition = 0 val count = cursor.count() add(cursor.first() ?: return@apply) currentPosition++ while(currentPosition < count) { add(cursor.next() ?: return@apply) currentPosition++ } } } }
fun <E> entities(fromKey: K, fromInclusive: Boolean, toKey: K, toInclusive: Boolean):
EntityCursor<E>
fun <E> entities(fromKey: K, fromInclusive: Boolean, toKey: K, toInclusive: Boolean):
EntityCursor<E>
fun <E> entities(fromKey: K, fromInclusive: Boolean, toKey: K, toInclusive: Boolean):
EntityCursor<E>
which provides the ability to use the cursor, iterating over the desired data sample. This method works quite quickly, since indexes are used, is relatively convenient, and, in my opinion, does not require separate comments.
Source: https://habr.com/ru/post/336098/