-
Notifications
You must be signed in to change notification settings - Fork 3
[2] Entity MapWrapper
- Add it in your root build.gradle at the end of repositories:
buildscript {
repositories {
maven { url 'https://jitpack.io' }
}
}
...
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
- Add gradle dependency
implementation 'com.github.Kaufland.andcouchbaseentity:couchbase-entity-api:3.1.0'
kapt 'com.github.Kaufland.andcouchbaseentity:couchbase-entity:3.1.0'
- Optionally, if you use Couchbase 2.x.x (provides already implemented connector)
implementation 'com.github.Kaufland.andcouchbaseentity:couchbase-entity-connector:3.1.0@aar'
NOTE: For custom connectors see
- Configure library
- Add the following code in your Application.kt
@Override
public void onCreate() {
super.onCreate();
PersistenceConfig.configure(object : Couchbase2Connector() {
override fun getDatabase(name: String): Database {
if (DB == name) {
//for better performance when using queries may consider create some Indexes
return database!!
}
throw RuntimeException("wrong db name defined!!")
}
})
}
NOTE: For other databases implement your own connector
- Annotate classes to generate entities (all generated classes have the suffix Entity or Wrapper (used for child entities or map wrapping)
@Entity(database = Application.DB)
@Fields(
Field(name = "type", type = String::class, defaultValue = "product", readonly = true),
Field(name = "name", type = String::class),
Field(name = "comments", type = UserComment::class, list = true),
Field(name = "image", type = Blob::class),
Field(name = "identifiers", type = String::class, list = true)
)
@Queries(
Query(fields = ["type"])
)
open class Product{
companion object{
@GenerateAccessor
fun someComplexQuery(param1 : String){
//do some heavy logic here
}
}
}
- Use generated classes and be happy :-)
- Example 1: use fluent API to modify multiple properties.
ProductEntity.create().builder()
.setName("Wodka")
.setComments(listOf(UserCommentWrapper
.create().builder()
.setComment("feeling like touch the sky")
.exit())).setImage(Blob("image/jpeg", resources.openRawResource(R.raw.ic_kaufland_placeholder)))
.exit().save()
- Example 2: get / set data via kotlin property syntax
var data = ProductEntity.create()
data.name = "Tomatoes"
data.comments = listOf(UserCommentWrapper.create().builder().setComment("don't like there color").exit(),
UserCommentWrapper.create().builder().setComment("worst experience ever!!").exit())
data.image = Blob("image/jpeg", resources.openRawResource(R.raw.ic_kaufland_placeholder))
data.save()
NOTE : To modify child entities it's neccessary to invoke the setter before saving the parent entity
final List<UserCommentWrapper> data = getParentEntity().getComments();
data.remove(position);
try {
ProductEntity entity = getParentEntity();
entity.setComments(mComments);
entity.save();
} catch (PersistenceException e) {
Log.e(TAG, "failed to save Entity", e);
}
By using the @Query annotation the Framework automatically generates static methods to execute this queries and returns the resulted entities
@Fields(
Field(name = "type", type = String::class, defaultValue = "product", readonly = true),
Field(name = "name", type = String::class)
)
@Queries(
Query(fields = ["type"]),
Query(fields = ["type", "name"])
)
Above Code generates Methods which can be used like
val resultByType = MyEntity.findByType()
val resultByTypeAndName = MyEntity.findbyTypeAndName("beer")
The Query generation may not fit all needs for this cases you can define a query by yourself and use the @GenerateAccessor annotation to make it available in the GeneratedEntity
@Entity(database = "mydb_db")
//Fields and Queries add them here
open class Product{
companion object{
@GenerateAccessor
fun someComplexQuery(param1 : String){
//do some heavy logic here
}
}
}
ProductEntity.someComplexQuery("Foo")
By using the @SchemaClass
annotation the Framework automatically generates a class that contains all the fields as properties.
The fields are mapped to one of four types, determined by the parameters provided for each field, along with a generic derived from the "type" parameter.
The type is determined based on the following criteria:
- CMList: When the "list" parameter is marked as true.
-
CMObject: If the "type" parameter is assigned to another class that also has the
@SchemaClass
annotation. -
CMObjectList: When both the "list" parameter is true and the "type" parameter is set to a class that is annotated with
@SchemaClass
. - CMField: When none of the above apply.
If there is a defaultValue given to a Field, it also creates a default property for this field.
@SchemaClass
@Fields(
Field(name = "type", type = String::class, defaultValue = "product", readonly = true),
Field(name = "name", type = String::class)
)
class Product
The Code above generates a class like this:
open class ProductSchema(
path: String = ""
) : Schema {
val DEFAULT_TYPE: String = "product"
val type: CMField<String> = CMField("type", path)
val name: CMField<String> = CMField("name", path)
}
This class can then be used like this:
val productSchema = ProductSchema()
val productNameField = productSchema.name
Crystal-Map stores data in maps as one of the following types:
String
Boolean
Number
Map
Any
Any other type that is used in a Field
annotation requires a TypeConverter to be defined. Crystal-Map ensures this at annotation processing time.
A TypeConverter can be defined using the annotation @TypeConverter
. This annotation should be placed on an abstract or open class that implements the ITypeConverter
interface. The generic type parameters of the interface specify the type we want to convert to (i.e. the one that is used in the Field
annotation) and the type that should be used in the map (i.e. one of the types described above). The following is an example that converts between LocalDate
and String
.
@TypeConverter
abstract class LocalDateConverter : ITypeConverter<LocalDate, String> {
override fun write(value: LocalDate?): String? =
value?.toString()
override fun read(value: String?): LocalDate? = value?.let { LocalDate.parse(it) }
}
This will generate a Kotlin object that will be used in the generated Entities and Wrappers
public object LongConverterInstance : LongConverter()
In order to facilitate the usage of TypeConverters across modules and projects, they can be exported in the source module and imported in the target module.
A TypeConverter export can be defined with the @TypeConverterExporter
annotation. The annotation should be placed on an interface.
@TypeConverterExporter
interface DummyTypeConverters
This will generate a Kotlin class that includes all specified TypeConverters in a format that can be used by the annotation processor:
public class DummyTypeConvertersInstance : DummyTypeConverters, ITypeConverterExporter {
override val typeConverters: Map<KClass<*>, ITypeConverter<*, *>>
get() = mapOf(
Date::class to DummyDateConverterInstance,
)
override val typeConverterImportables: List<TypeConverterImportable>
get() = listOf(
TypeConverterImportable(
ClassNameDefinition("com.schwarz.crystaldummy.customtypes", "DummyDateConverterInstance"),
ClassNameDefinition("java.util", "Date"),
ClassNameDefinition("kotlin", "Number")
),
)
}
The @TypeConverterImporter
can then be used to import the exported TypeConverters. For this, the aforementioned class is passed as a parameter to the annotation.
@TypeConverterImporter(DummyTypeConvertersInstance::class)
interface DummyTypeConverterImporter
The exported TypeConverters will then be added to the list of available converters and used by the annotation processor.