I'm trying to use retrofit2 with Koltin in Android Studio as part of jetpack compose application. I'm sending a POST and keep getting error 500. I don't have access to the server code so I'm trying to figure out what am I doing wrong.
This is the interface I have declared for building the retrofit object:
I tried three different ways of declaring the POST endpoint.
#Singleton
interface IsrPayService {
#Headers ("Content-Type: application/json")
#POST("v3/driver/new-credit-driver")
suspend fun signUp(#Body user: UserDriver): Call\<WsError?\>
#Headers (
"Content-Type: application/json",
"Accept: application/json")
#POST("v3/driver/new-credit-driver")
suspend fun signup2(#Body user: UserDriver): retrofit2.Response<WsError>
#FormUrlEncoded
#POST("v3/driver/new-credit-driver")
suspend fun signupUrlEncoded(
#Field("firstName") firstName: String,
#Field("lastName") lastName: String): retrofit2.Response<WsError>
}
The data I am trying to send is UserDriver and I declared all the internal data classes below:
data class UserDriver(
#SerializedName("firstName") val firstName: String = "default",
#SerializedName("lastName") val lastName: String = "default",
#SerializedName("civilId") val civilId: String = "default",
#SerializedName("vehicleLicensingNumber") val vehicleLicensingNumber: String = "default",
#SerializedName("vehicleManufacturer") val vehicleManufacturer: String = "default",
#SerializedName("vehicleModel") val vehicleModel: String = "default",
#SerializedName("vehicleManufactureYear") val vehicleManufactureYear: String = "1973",
#SerializedName("counterModel") val counterModel: String = "default",
#SerializedName("authorizedEmployerNumber") val authorizedEmployerNumber: String = "default",
#SerializedName("bankAccountId") val bankAccountId: String = "default",
#SerializedName("bankAccountBranch") val bankAccountBranch: Int = 0,
#SerializedName("bankId") val bankId:Int = 123456,
#SerializedName("email") val email: String = "default",
#SerializedName("dob") val dob: DateIndicator = DateIndicator(date = 3, month = 4, year = 2023),
#SerializedName("address") val address: Address = Address (Coordinates(0,0),"מודיעין","טשרניחובסקי","12"),
#SerializedName("phoneNumber") val phoneNumber: String = "default",
#SerializedName("driverLicenseId") val driverLicenseId: Int = 0,
#SerializedName("civilIdPhoto") val civilIdPhoto: String = "default",
#SerializedName("driverLicensePhoto") val driverLicensePhoto: String = "default",
#SerializedName("signaturePhoto") val signaturePhoto: String = "default"
)
data class DateIndicator(
#SerializedName("date")
#Expose
val date: Int,
#SerializedName("month")
#Expose
val month: Int,
#SerializedName("year")
#Expose
val year: Int
)
I think #Expose is not required but tried it just in case..
data class Address (
#SerializedName("coordinates")
#Expose
val coordinates: Coordinates,
#SerializedName("city")
#Expose
val city: String,
#SerializedName("street")
#Expose
val street: String,
#SerializedName("number")
#Expose
val number: String
)
data class Coordinates (
#SerializedName("latitude")
#Expose
val latitude: Int,
#SerializedName("longitude")
#Expose
val longitude: Int
)
I tried to methods of getting the response:
suspend fun driverSignUp(user: UserDriver, onResult: (WsError?) -> Unit) {
try {
ws.signUp(user = user). enqueue (
object: Callback<WsError?> {
override fun onResponse(call: Call<WsError?>, response: Response<WsError?>) {
Log.d("driverSignUp",
"onResponse: response.isSuccessful = ${response.isSuccessful}")
var wsError: WsError? = null
wsError = if(!response.isSuccessful){
WsError(
body = "",
isError = true,
error = yz.learning.isrpaytest.model.Error(
errorCode = response.code(),
errorMessage = ErrorMessage(
enUs = response.message(),
heIl = response.message())))
} else {
response.body()
}
onResult(wsError)
}
override fun onFailure(call: Call<WsError?>, t: Throwable) {
Log.d("driverSignUp", "onFailure: ")
onResult(null)
}
}
)
} catch (exception: Exception) {
Log.d("driverSignUp", "driverSignUp exception: ${exception.message}")
onResult(
WsError(
body = "",
isError = true,
error = yz.learning.isrpaytest.model.Error(
errorCode = 0,
errorMessage = ErrorMessage(
enUs = exception.message!!,
heIl = exception.message!!)))
)
}
}
suspend fun driverSignUp2(user: UserDriver): retrofit2.Response<WsError>{
return ws.signup2(user)
}
I don't understand why I keep getting Internal server error. I have a feeling I have to send the data as a JSON string and not as an Object but as far as I understand this is supposed to be automatically using the gson converter, no?
I can try a simpler endpoint, but I think I will end up with the same problem.
I will appreciate any help since I'm stuck with this issue for a couple of days.
Thanks,
Yariv
I' ve this data classe
#Parcelize
data class IdTotalTotal(
val idEnvioMarrocos: Int,
val clifor: String,
val nomeClifor: String,
val nomeUser: String,
val termina: Int,
var dados: List<IdTotal>
): Parcelable {
#Parcelize
data class IdTotal(
val produto: String,
val modelo: String,
var idPartePeso: List<IdPartePeso>,
var idOpPedido: List<IdOpPedido>
) : Parcelable {
#Parcelize
data class IdPartePeso(
val parteID: Int,
val nomeParte: String,
var pesoTamanho: List<PesoTamanho>
) : Parcelable {
#Parcelize
data class PesoTamanho(
val nomeT: String,
var pesoT: Int
) : Parcelable
}
#Parcelize
data class IdOpPedido(
val op: String,
var pedidos: List<Pedido>
) : Parcelable {
#Parcelize
data class Pedido(
val pedido: String,
val pedidoCliente: String,
var pedidoEntra: Boolean
) : Parcelable
}
}
}
Now I'm trying to achieve the same result with a room entity for persistence and ease.
This is my code:
#Entity
data class EnviosPesosDestinosBd(
#PrimaryKey(autoGenerate = false)
val idEnvioMarrocos: Int,
val clifor: String,
val nomeClifor: String,
val nomeUser: String,
val termina: Int,
val updatedAt: Long = System.currentTimeMillis()
)
#Entity(primaryKeys = ["idEnvioMarrocos", "produto"])
data class EnvioProduto(
val idEnvioMarrocos: Int,
val produto: String,
val modelo: String,
)
#Entity(primaryKeys = ["idEnvioMarrocos", "produto", "parteID"],
foreignKeys = [
ForeignKey(
entity = TamanhoPeso::class,
parentColumns = ["idEnvioMarrocos", "produto", "parteID"],
childColumns = ["idEnvioMarrocos", "produto", "parteID"]
)
]
)
data class PesoParte(
val idEnvioMarrocos: Int,
val produto: String,
val parteID: Int,
val nomeParte: String,
)
#Entity(primaryKeys = ["idEnvioMarrocos", "produto", "parteID", "nomeT"])
data class TamanhoPeso(
val idEnvioMarrocos: Int,
val produto: String,
val parteID: Int,
val nomeT: String,
var pesoT: Int
)
#Entity(primaryKeys = ["idEnvioMarrocos", "op", "produto"],
foreignKeys = [
ForeignKey(
entity = OpPedido::class,
parentColumns = ["idEnvioMarrocos", "op"],
childColumns = ["idEnvioMarrocos", "op"]
)
])
data class EnvioDestinoComOp(
val idEnvioMarrocos: Int,
val produto: String,
val op: String,
)
#Entity(primaryKeys = ["idEnvioMarrocos", "op", "pedido"])
data class OpPedido(
val idEnvioMarrocos: Int,
val op: String,
val pedido: String,
val pedidoCliente: String,
var pedidoEntra: Boolean
)
Thanks
It would appear that you are stuck with how to create appropriate Entities to represent your original classes and trying to make complex primary keys to create the relationships between the tables.
I would suggest utilising unique id's (single columns) as the basis for the relationships.
The following code, based upon a hierarchical inspection of your original classes and using the original class names as the basis for the Database Table/Entity names is a quick interpretation of what you may wish to consider as the basis.
So the topmost class directly or indirectly referenced is the IdTotalTotal class, and it appears that idEnvioMarrocos would be a unique value.
This what was coded (basically little different to what you coded) :-
#Entity
data class DBIdTotalTotal(
#PrimaryKey
val idEnvioMarrocos: Long,
val clifor: String,
val nomeClifor: String,
val nomeUser: String,
val termina: Int,
val updatedAt: Long = System.currentTimeMillis()
)
Long rather Int has been used as I prefer to use Long as at least for Java the value can be Long rather than int.
No need for autoGenerate = false as just #PrimaryKey will default to false.
Note that I have prefixed the original class name with DB to indicate that it's a DB related class (and I've used this throughout).
Now according to your original classes the IdTotalToal class has sub classes (aka a relationship) with a list of IdTotal's. So DBIdTotal looks like :-
#Entity(
foreignKeys = [
ForeignKey(
entity = DBIdTotalTotal::class,
parentColumns = ["idEnvioMarrocos"],
childColumns = ["ref_DBIdTotalTotal"]
)
]
)
data class DBIdTotal(
#PrimaryKey
val id_DBIdTotal: Long,
val ref_DBIdTotalTotal: Long,
val produto: String,
val modelo: String
)
i.e. A DBIdTotal is a child of a DBIdTotalTotal and the parent is stored in the ref_DBIdTotalTotal column that has been added.
you will see a pattern where ref_ prefixes the new column added to reference the parent column.
note (I have not included indexes on the Foreign keys Room will warn you to do this)
As the IdTotal class has two lists then it is a parent to the IdPartPeso(s) and the IdOpPedido(s) these two DB classes are as follows:-
#Entity(
foreignKeys = [
ForeignKey(
entity = DBIdTotal::class,
parentColumns = ["id_DBIdTotal"],
childColumns = ["ref_DBIdTotal"]
)
]
)
data class DBIdPartePeso(
#PrimaryKey
val id_DBIdPartePeso: Long,
val ref_DBIdTotal: Long,
val parteID: Int,
val nomeParte: String
)
and
#Entity(
foreignKeys = [
ForeignKey(entity = DBIdTotal::class,
parentColumns = ["id_DBIdTotal"],
childColumns = ["ref_DBIdTotal"]
)
]
)
data class DBIdOpPedido(
#PrimaryKey
val id_DBIdOpPedido: Long,
val ref_DBIdTotal: Long,
val op: String
)
IdPartePeso has a list of PesoTamanho's so DBPesoTamanho is :-
#Entity(
foreignKeys = [
ForeignKey(
entity = DBIdPartePeso::class,
parentColumns = ["id_DBIdPartePeso"],
childColumns = ["ref_DBIdPartePeso"]
)
]
)
data class DBPesoTamanho(
#PrimaryKey
val id_DBPesoTamanho: Long,
val ref_DBIdPartePeso: Long,
val nomeT: String,
var pesoT: Int
)
Likewise IdOpPedido has a list of Pedido's SO DBPedido is :-
#Entity(
foreignKeys = [
ForeignKey(
entity = DBIdOpPedido::class,
parentColumns = ["id_DBIdOpPedido"],
childColumns = ["ref_DBIdOpPedido"]
)
]
)
data class DBPedido(
#PrimaryKey
val id_DBPedido: Long,
val ref_DBIdOpPedido: Long,
val pedido: String,
val pedidoCliente: String,
var pedidoEntra: Boolean
)
That deals with the tables. However, it is very likely that you want to get a DBIdTotalTotal with all the underlying objects (The DBIdTotal's .... the DBPedido's and DBPesoTamanho's).
So we work back up the hierachy this time creating POJO's for combining children with the parent into a List of the children.
So for a DBIdOpPedido we want the DBIdOpPedido object with a List so the following POJO would suit:-
data class PojoDBIdOpPedidoWithDbPedido (
#Embedded
val dbIdOpPedido: DBIdOpPedido,
#Relation(
entity = DBPedido::class,
parentColumn = "id_DBIdOpPedido",
entityColumn = "ref_DBIdOpPedido"
)
val dbPedidoList: List<DBPedido>
)
The parent is Embedded, the children use and #Relation
Similar for DBPartePpeso with the List :-
data class PojoDBIdPartePesoWithDBPesoTamanho(
#Embedded
val dbIdPartePeso: DBIdPartePeso,
#Relation(
entity = DBPesoTamanho::class,
parentColumn = "id_DBIdPartePeso",
entityColumn = "ref_DBIdPartePeso"
)
val dbPesoTamanhoList: List<DBPesoTamanho>
)
Next up the Hierarchy is the DBIdTotal, this has 2 lists which sublists. However, it's not complicated because the previously created POJO's can be utilised. As such there is:-
data class PojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedido(
#Embedded
val dbIdTotal: DBIdTotal,
#Relation(
entity = DBIdPartePeso::class,
parentColumn = "id_DBIdTotal",
entityColumn = "ref_DBIdTotal"
)
val pojoDBIdPartePesoWithDBPesoTamanhoList: List<PojoDBIdPartePesoWithDBPesoTamanho>,
#Relation(
entity = DBIdOpPedido::class,
parentColumn = "id_DBIdTotal",
entityColumn = "ref_DBIdTotal"
)
val pojoDBIdOpPedidoList: List<PojoDBIdOpPedidoWithDbPedido>
)
NOTE the entities referred to are the entities NOT the POJO's as it is the columns of the tables that form the relationships.
So last but not least the top level DBIdTotalTotal aka everything all together (as such a bit of a different name for this) :-
data class PojoDBIdTotalTotalWithDBIdTotalEtcetera(
#Embedded
val dbIdTotalTotal: DBIdTotalTotal,
#Relation(
entity = DBIdTotal::class,
parentColumn = "idEnvioMarrocos",
entityColumn = "ref_DBIdTotalTotal"
)
val pojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedidoList: List<PojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedido>
)
The above code can be used with appropriate Dao's nothing that Room builds the underlying queries to obtain ALL relations when using #Relation. This makes the queries very simple as you only have to specfify the higher level table. The following are suitable Dao's (in a single class named DBDao) as per :-
#Dao
interface DBDao {
#Insert
fun insertDBIdTotalTotal(dbIdTotalTotal: DBIdTotalTotal): Long
#Insert
fun insertDBIdTotal(dbIdTotal: DBIdTotal): Long
#Insert
fun insertDBIdPartePeso(dbIdPartePeso: DBIdPartePeso): Long
#Insert
fun insertDBPesoTamanho(dbPesoTamanho: DBPesoTamanho): Long
#Insert
fun insertDBIdOpPedido(dbIdOpPedido: DBIdOpPedido): Long
#Insert
fun insertDBPedido(dbPedido: DBPedido): Long
#Transaction
#Query("SELECT * FROM dbidoppedido")
fun getIdOpPedidoWithAllPedidos(): List<PojoDBIdOpPedidoWithDbPedido>
#Transaction
#Query("SELECT * FROM dbidpartepeso")
fun getIdPartePesoWithAllPesoTamanhod(): List<PojoDBIdPartePesoWithDBPesoTamanho>
#Transaction
#Query("SELECT * FROM dbidtotal")
fun getIdTotalWithAllIdPartePesosAndAllIdOpPedidos(): List<PojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedido>
#Transaction
#Query("SELECT * FROM dbidtotaltotal")
fun getIdTotalTotalWithEverything(): List<PojoDBIdTotalTotalWithDBIdTotalEtcetera>
}
not that the queries that have an underlying #Relation all have #Transaction (Room will issue warnings if not (best to use #Transaction as child objects are built by using additional queries)).
Testing/Demo/Example
An #Database named *TheDatabase was created :-
#Database(entities = [
DBIdTotalTotal::class,
DBIdTotal::class,
DBIdOpPedido::class,
DBIdPartePeso::class,
DBPesoTamanho::class,
DBPedido::class
],
version = 1
)
abstract class TheDatabase: RoomDatabase() {
abstract fun getDao(): DBDao
}
The the following was then used for testing/demonstrating the code above (note allowMainThreadQueries used for brevity and convenience) :-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: DBDao
val TAG = "MYDBINFO"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = Room.databaseBuilder(this,TheDatabase::class.java,"mydb.db")
.allowMainThreadQueries().build()
dao = db.getDao()
var firstTotalTotalId = dao.insertDBIdTotalTotal(DBIdTotalTotal(1L,"CLIFOR1","CLIFORNAME1","USERNAME1",10))
var firstTotal = dao.insertDBIdTotal(DBIdTotal(2L,firstTotalTotalId,"PRODUTO1","MODELO1"))
var secondTotal = dao.insertDBIdTotal(DBIdTotal(3L,firstTotalTotalId,"PRODUTO2","MODELO2"))
var firstPartePeso = dao.insertDBIdPartePeso(DBIdPartePeso(4L,firstTotal,100,"Parte100"))
var secondPartePeso = dao.insertDBIdPartePeso(DBIdPartePeso(5L,secondTotal,200,"PARTE200"))
var thirdPartePeso = dao.insertDBIdPartePeso( DBIdPartePeso(6,firstTotal,300,"PARTE300"))
var fourthPartePes = dao.insertDBIdPartePeso(DBIdPartePeso(7,secondTotal,400,"PARTE400"))
var firstPesoTamanho = dao.insertDBPesoTamanho(DBPesoTamanho(8,firstPartePeso,"PESONTAMANHO1000",1000))
var secondPesoTamanho = dao.insertDBPesoTamanho(DBPesoTamanho(9,thirdPartePeso,"PARTEPESO2000",2000))
var firstIdOpPedido = dao.insertDBIdOpPedido(DBIdOpPedido(10,firstTotal,"OPPEDIDO10000"))
var sedcondIdOpPedido = dao.insertDBIdOpPedido(DBIdOpPedido(11,firstTotal,"OPPEDIDO2000"))
var firstPedido = dao.insertDBPedido(DBPedido(12,firstIdOpPedido,"PEDIDO100000","CLIENTE1",true))
var getall = dao.getIdTotalTotalWithEverything()
for(all: PojoDBIdTotalTotalWithDBIdTotalEtcetera in getall) {
logIdTotalTotal(all.dbIdTotalTotal,"")
for(allTotals: PojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedido in all.pojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedidoList) {
logIdTotal(allTotals.dbIdTotal,"\t")
for(allIdPartePeso: PojoDBIdPartePesoWithDBPesoTamanho in allTotals.pojoDBIdPartePesoWithDBPesoTamanhoList) {
logIdPartePeso(allIdPartePeso.dbIdPartePeso,"\t\t")
for(pesoTamanho: DBPesoTamanho in allIdPartePeso.dbPesoTamanhoList) {
logPesoTamanho(pesoTamanho,"\t\t\t")
}
}
for(allIdOpPedido: PojoDBIdOpPedidoWithDbPedido in allTotals.pojoDBIdOpPedidoList) {
logIdOpPedido(allIdOpPedido.dbIdOpPedido,"\t\t")
for(pedido: DBPedido in allIdOpPedido.dbPedidoList) {
logPedido(pedido,"\t\t\t")
}
}
}
}
}
private fun logPedido(p: DBPedido,prefix: String) {
Log.d(TAG,"$prefix PEDIDIO = ${p.pedido} CLIENTE = ${p.pedidoCliente} ID=${p.id_DBPedido} ENTRA=${p.pedidoEntra} REFERENCES IDOPPedido=${p.ref_DBIdOpPedido}")
}
private fun logIdOpPedido(iop: DBIdOpPedido, prefix: String) {
Log.d(TAG,"$prefix OP=${iop.op} ID=${iop.id_DBIdOpPedido} REFERNCES TOTAL=${iop.ref_DBIdTotal}" )
}
private fun logPesoTamanho(pt: DBPesoTamanho, prefix: String) {
Log.d(TAG,"$prefix NOME=${pt.nomeT} PESO=${pt.pesoT} ID=${pt.id_DBPesoTamanho} REFRENCES IDPARTEPESO=${pt.ref_DBIdPartePeso}")
}
private fun logIdPartePeso(ipp: DBIdPartePeso, prefix: String) {
Log.d(TAG,"$prefix NOMEPARTE=${ipp.nomeParte} PARTEID=${ipp.parteID} ID=${ipp.id_DBIdPartePeso} REFERNCES TOTAL=${ipp.ref_DBIdTotal}")
}
private fun logIdTotal(it: DBIdTotal, prefix: String) {
Log.d(TAG,"$prefix MODELO=${it.modelo} PRODUTO=${it.produto} ID=${it.id_DBIdTotal} REFERENCES TOTALTOTAL= ${it.ref_DBIdTotalTotal}")
}
private fun logIdTotalTotal(itt: DBIdTotalTotal, prefix: String) {
Log.d(TAG,"$prefix NOMUSER=${itt.nomeUser} CLIFOR=${itt.clifor} NOMCLIFOR=${itt.nomeClifor} TERMINA=${itt.termina} UPDATED= ${itt.updatedAt} ID=${itt.idEnvioMarrocos}")
}
}
The above has been designed to run and work just the once purely for testing/demonstrating.
Result
D/MYDBINFO: NOMUSER=USERNAME1 CLIFOR=CLIFOR1 NOMCLIFOR=CLIFORNAME1 TERMINA=10 UPDATED= 1620189450079 ID=1
D/MYDBINFO: MODELO=MODELO1 PRODUTO=PRODUTO1 ID=2 REFERENCES TOTALTOTAL= 1
D/MYDBINFO: NOMEPARTE=Parte100 PARTEID=100 ID=4 REFERNCES TOTAL=2
D/MYDBINFO: NOME=PESONTAMANHO1000 PESO=1000 ID=8 REFRENCES IDPARTEPESO=4
D/MYDBINFO: NOMEPARTE=PARTE300 PARTEID=300 ID=6 REFERNCES TOTAL=2
D/MYDBINFO: NOME=PARTEPESO2000 PESO=2000 ID=9 REFRENCES IDPARTEPESO=6
D/MYDBINFO: OP=OPPEDIDO10000 ID=10 REFERNCES TOTAL=2
D/MYDBINFO: PEDIDIO = PEDIDO100000 CLIENTE = CLIENTE1 ID=12 ENTRA=true REFERENCES IDOPPedido=10
D/MYDBINFO: OP=OPPEDIDO2000 ID=11 REFERNCES TOTAL=2
D/MYDBINFO: MODELO=MODELO2 PRODUTO=PRODUTO2 ID=3 REFERENCES TOTALTOTAL= 1
D/MYDBINFO: NOMEPARTE=PARTE200 PARTEID=200 ID=5 REFERNCES TOTAL=3
D/MYDBINFO: NOMEPARTE=PARTE400 PARTEID=400 ID=7 REFERNCES TOTAL=3
That is the Single IdTotalTotal is extracted with the underlying related objects
The above is intended to show the principle, there may be some issues as the output hasn't been rigorously checked that it conforms to expectations.
I need to summarize a column of a state. I created a mappedSchema and defined the field as Double.
If I list the states, the values for that field are correct. But if I use builder::sum(), the value returns with rounding problems and more decimal places than it should.
Here are excerpts from the code:
STATE
data class ConsumerMeteringState(val metering : ConsumerMetering,
val meteringParticipants : List<AbstractParty> = listOf(),
override val linearId: UniqueIdentifier = UniqueIdentifier()) :
LinearState, QueryableState {
override val participants: List<AbstractParty> = meteringParticipants
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) {
is ConsumerMeteringSchemaV1 -> ConsumerMeteringSchemaV1.PersistentConsumerMetering(
this.metering.dateTimeIni,
this.metering.dateTimeEnd,
this.metering.quantityKwh,
this.linearId.id
)
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(ConsumerMeteringSchemaV1)
SCHEMA
object ConsumerMeteringSchemaV1 : MappedSchema(
schemaFamily = ConsumerMeteringSchema.javaClass,
version = 1,
mappedTypes = listOf(PersistentConsumerMetering::class.java)) {
#Entity
#Table(name = "consumer_metering_states")
class PersistentConsumerMetering(
#Column(name = "date_time_ini")
var dateTimeIni: Instant,
#Column(name = "date_time_end")
var dateTimeEnd: Instant,
#Column(name = "quantity_kwh")
var quantityKwh: Double,
#Column(name = "linear_id")
var linearId: UUID
) : PersistentState() {
// Default constructor required by hibernate.
constructor(): this(Instant.now(), Instant.now(), 0.0, UUID.randomUUID())
}
}
VAULTQUERY CRITERIA
val criteriaAccount = QueryCriteria.VaultQueryCriteria(externalIds = listOf(accountId))
val sumQuantityKwh = builder { ConsumerMeteringSchemaV1
.PersistentConsumerMetering::quantityKwh.sum() }
val sumQuantityKwhCriteria = QueryCriteria.VaultCustomQueryCriteria(sumQuantityKwh)
serviceHub.vaultService.queryBy(contractStateType = ConsumerMeteringState::class.java,
criteria = criteriaAccount.and(sumQuantityKwhCriteria)).otherResults.singleOrNull()
States only (the values are OK):
[ConsumerMeteringState(metering=ConsumerMetering(dateTimeIni=2020-06-03T09:46:00Z, dateTimeEnd=2020-06-03T09:59:00Z, quantityKwh=10.55), meteringParticipants=[Anonymous(DL624i3ieTdLkPRBUvUgZnzn5jeG3Md2cvANt6sZNJiXwy), O=Distributor, L=Curitiba, C=BR], linearId=2e5009ad-56c3-4fed-ba36-deb0d48e668c), ConsumerMeteringState(metering=ConsumerMetering(dateTimeIni=2020-06-03T09:46:00Z, dateTimeEnd=2020-06-03T09:59:00Z, quantityKwh=50.18), meteringParticipants=[Anonymous(DLBep6kdDduaMKVrszQWa7N8i6YNnJLtA4WXsp4QmZiEjC), O=Distributor, L=Curitiba, C=BR], linearId=3b012984-676d-4e62-9b9f-1bb8158aaf4b)]
With builder sum:
I get the value 60.730000000000004
Why sum doesn't return 60.73 ?
It worked by changing the column type from Double to BigDecimal. It seems to be some question of precision of the Double type.
I did a test just by retrieving the states and making a simple sum of the quantityKwh (Double) field and the precision was already strange.
I didn't understand the reason for this behavior, but with BigDecimal it worked ok.
I have a one-to-many relationship where I am trying to add a list of object/class in my state. i.e
I have a contract state that has a list of attachments List<Attachment>, where Attachmentis just a class with fields like
attachmentHash, uploadedDate, fileType
I wanted to query with something in the child but I get syntax error "AttachmentEntity is not a subtype of PersistentState"
QueryCriteria.VaultCustomQueryCriteria(
builder { (ContractSchemaV1.AttachmentEntity::uploadDate).equal(givenDate) }))
I let AttachmentEntity be a subclass of PersistentState and the node started up with the error
org.hibernate.AnnotationException: net.corda.core.schemas.PersistentStateRef
must not have #Id properties when used as an #EmbeddedId: project.schemas.ContractSchemaV1$AttachmentEntity.stateRef
Seems like I'm doing something wrong, whats the best way to represent a collection of data classes in the state and translate that in a schema? Or is this already the correct way, but there's no way to query the nested collection using VaultCustomQuery?
The example entity below.
object ContractSchema
object ContractSchemaV1 : MappedSchema(schemaFamily = ContractSchema.javaClass, version = 1,
mappedTypes = listOf(ContractEntity::class.java, AttachmentEntity:class.java)) {
#Entity
#Table(name = "contracts")
class ContractEntity(
#Column(name = "issued_date")
var issuedDate: Instant,
#Column(name = "linear_id")
var linearId: String,
#OneToMany(fetch = FetchType.LAZY, cascade = arrayOf(CascadeType.PERSIST))
#JoinColumns(
JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"),
JoinColumn(name = "output_index", referencedColumnName = "output_index"))
var attachments: MutableSet<AttachmentEntity> = emptyList(),
) : PersistentState()
#Entity
#Table(name = "attachments")
class AttachmentEntity (
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
var id: Long? = null,
#Column(name = "attachment_hash", nullable = false)
var attachmentHash: String? = null,
#Column(name = "attachment_name", nullable = false)
var attachmentName: String? = null,
#Column(name = "upload_date", nullable = true)
var uploadDate: Instant? = null)
}
Your schema definition is correct (and you can see another example here: Querying nested collections in LinearState states).
However, querying nested collections is not supported by VaultCustomQueryCriteria. You have to do direct JDBC queries to query attributes of the nested collections.
Here is an example of a direct JDBC query in Corda:
#Test
fun `test calling an arbitrary JDBC native query`() {
val nativeQuery = "SELECT v.transaction_id, v.output_index FROM vault_states v WHERE v.state_status = 0"
database.transaction {
val jdbcSession = services.jdbcSession()
val prepStatement = jdbcSession.prepareStatement(nativeQuery)
val rs = prepStatement.executeQuery()
var count = 0
while (rs.next()) {
val stateRef = StateRef(SecureHash.parse(rs.getString(1)), rs.getInt(2))
Assert.assertTrue(cashStates.map { it.ref }.contains(stateRef))
count++
}
Assert.assertEquals(cashStates.count(), count)
}
}
I have this data class in Kotlin (example):
import com.google.firebase.database.Exclude
data class User(val name: String = "", #Exclude val age: Int = 0)
And I don't want to save the age property in firebase. #Exclude should do this but it does not work, age is still saved.
Are there any workarounds?
Placing #Exclude on a property targets its generated field and not its generated get accesor method. To do the latter you'll need to prefix "Exclude" with "get:". e.g.:
data class User(val name: String = "", #get:Exclude val age: Int = 0)
See Annotation Use-site Targets for more details.
Actually you don't need to add only #get:Exclude but you need all 3 Exclude,
#Exclude #set:Exclude #get:Exclude.
I did it for imageUrl and providerId
data class FirebaseChatModel(
#get:PropertyName("message")
#set:PropertyName("message")
var message: String = "",
#get:PropertyName("type")
#set:PropertyName("type")
var type: Int = 1,
#get:PropertyName("senderId")
#set:PropertyName("senderId")
var senderId: Int = 0,
#get:PropertyName("receiverId")
#set:PropertyName("receiverId")
var receiverId: Int = 0,
#Exclude #set:Exclude #get:Exclude var imageUrl: String? = "",
#Exclude #set:Exclude #get:Exclude var providerId: Int = 0
)