can you join two tables and result with an obj (from first table) containing a list of obj(from the second table) - sql

First of all my code:
Table 1:
object Company : Table() {
val name = varchar("pk_name", 250)
override val primaryKey = PrimaryKey(name, name = "pk_company_constraint")
}
Table 2&3:
object Sector : IntIdTable() {
val name = varchar("fk_name", 50).references(MainSector.name)
val alias = varchar("alias", 50).nullable()
val companyName = varchar("fk_company_name", 250).references(Company.name, onDelete = ReferenceOption.CASCADE)
}
object MainSector : Table() {
val name = varchar("pk_name", 50)
override val primaryKey = PrimaryKey(name, name = "pk_main_sector_constraint")
}
My Problem:
I need to parse the result into a DTO that looks like this:
data class CompanyDTO (
val companyName: String,
val sectorList: List<SectorDTO>
)
data class SectorDTO (
val mainSectorName: String,
val sectorAlias: String
)
I am able to get a Company with the first Sector from the database, but i have no idea how to get a list of them.
My try:
override fun retrieveCompanies(vararg names: String): List<CompanyDTO> {
var retlist: List<CompanyDTO> = emptyList()
if (names.isEmpty()){
retlist = transaction {
(Company innerJoin Sector)
.select{Company.name eq Sector.companyName}
.map { CompanyDTO(it[Company.name], listOf(
SectorDTO(it[Sector.name], it[Sector.alias]?: "")
)) }
}
} else {
//return specific
}
return retlist
}
If no arguments are given i want to return all companies from the database, if arguments are given i want to return only companies with given name.
I canĀ“t find anything about this topic in the official documentation, please send help

If Company could not have any Sector you need to use leftJoin and then your code could be like:
Company.leftJoin.Sector.selectAll().map {
val companyName = it[Company.name]
val sector = it.tryGet(Sector.name)?.let { name ->
SectorDTO(name, it[Sector.alias].orEmpty())
}
companyName to sector
}.groupBy({ it.first }, { it.second }).map { (companyName, sectors) ->
CompanyDTO(companyName, sectors.filterNotNull())
}

Related

Returning one of different object types from single function in kotlin

I have the following structure at present:
#Entity
#Table(name = "table_app_settings")
data class AppSetting(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "app_setting_id")
val id: Long? = null,
#Column(name = "app_setting_name")
val name: String = "",
#Column(name = "app_setting_value")
var value: String = "",
#Column(name = "app_setting_type")
val type: AppSettingType,
)
enum class AppSettingType {
CHAR,
STRING,
BYTE,
SHORT,
INT,
LONG,
DOUBLE,
FLOAT,
BOOLEAN,
}
This is then saved to the database with the following:
override fun saveAppSetting(setting: AppSetting): DatabaseResult<AppSetting> {
log.info("Saving App Setting ${setting.name} to database.")
return try {
// Attempt to save the entity to the database. If we do not throw an exception, return success.
val savedSetting = appSettingsRepository.save(setting)
DatabaseResult(
code = ResultCode.CREATION_SUCCESS,
entity = savedSetting
)
} catch(exception: DataAccessException) {
log.error("Unable to save App Setting ${setting.name} to database. Reason: ${exception.message}")
DatabaseResult(
code = ResultCode.CREATION_FAILURE
)
}
}
Now, let's say that I wish to save a Char type to database, I figure I would use the following:
override fun saveAppSetting(name: String, value: Char): DatabaseResult<Char> {
val appSettingResult = saveAppSetting(AppSetting(
name = name,
value = value.toString(),
type = AppSettingType.CHAR,
))
return if(appSettingResult.code != ResultCode.CREATION_FAILURE) {
val entity = getAppSetting<Char>(appSettingResult.entity?.name!!).entity.toString().first()
DatabaseResult(
code = appSettingResult.code,
entity = entity
)
} else {
DatabaseResult(
code = ResultCode.CREATION_FAILURE,
)
}
}
I also figured that I would need to do the following in order to retrieve the correct object type:
override fun getAppSetting(name: String): DatabaseResult<Any?> {
log.info("Getting App Setting $name from database.")
val appSetting = appSettingsRepository.findAppSettingByName(name)
return if(appSetting != null) {
log.info("App Setting $name has ID of ${appSetting.id} within the database")
when(appSetting.type) {
AppSettingType.CHAR -> {
DatabaseResult<Char>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.first(),
)
}
AppSettingType.STRING -> {
DatabaseResult<String>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value,
)
}
AppSettingType.BYTE -> {
DatabaseResult<Byte>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toByte(),
)
}
AppSettingType.SHORT -> {
DatabaseResult<Short>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toShort(),
)
}
AppSettingType.INT -> {
DatabaseResult<Int>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toInt(),
)
}
AppSettingType.LONG -> {
DatabaseResult<Long>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toLong(),
)
}
AppSettingType.DOUBLE -> {
DatabaseResult<Double>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toDouble(),
)
}
AppSettingType.FLOAT -> {
DatabaseResult<Float>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toFloat()
)
}
AppSettingType.BOOLEAN -> {
DatabaseResult<Boolean>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toBoolean()
)
}
}
} else {
log.error("App Setting $name does not seem to exist within the database.")
DatabaseResult(
code = ResultCode.FETCH_FAILURE
)
}
However, when I then wish to use said object, I still have to write something like the following:
val newBarcode = getAppSetting("barcode_value").entity.toString().toInt()
Assuming I've "initialised" barcode_value with a value of 177 (for example).
How can I get the function to return what I need without having to do .toString.to...()?
Yes this all possible, here is a simplified demo, firstly
import kotlin.reflect.KClass
data class AppSetting(
val id: Long? = null,
val name: String = "",
var value: String = "",
val type: AppSettingType,
)
enum class AppSettingType(val clazz: KClass<out Any>) {
CHAR(Char::class),
STRING(String::class),
INT(Int::class),
}
So I added a clazz so from the enum we know the Kotlin type
and now a function to simulate your repository fetch
fun findAppSettingByName(name: String): AppSetting? {
return when(name) {
"Char thing" -> AppSetting(value= "C", type = AppSettingType.CHAR)
"String thing" -> AppSetting(value= "Str", type = AppSettingType.STRING)
"Int thing" -> AppSetting(value= "42", type = AppSettingType.INT)
else -> throw IllegalArgumentException()
}
}
Next in the function declaration I have made it generic with T and for the purposes of the demo removed the DatabaseResult container. Then I added a clazz parameter which is the typical Java way of carrying the required class information into the function:
fun <T : Any> getAppSetting(name: String, clazz: KClass<T>): T? {
val appSetting: AppSetting? = findAppSettingByName(name)
return appSetting?.let {
require(clazz == appSetting.type.clazz) {
"appSetting.type=${appSetting.type.clazz} mismatched with requested class=${clazz}"
}
when (appSetting.type) {
AppSettingType.CHAR -> appSetting.value.first()
AppSettingType.STRING -> appSetting.value
AppSettingType.INT -> appSetting.value.toInt()
} as T
}
}
the as T is important to cast the values into the required return type - this is unchecked but the when() clause should be creating the correct types.
Now let's test it:
val c1: Char? = getAppSetting("Char thing", Char::class)
val s1: String? = getAppSetting("String thing", String::class)
val i1: Int? = getAppSetting("Int thing", Int::class)
println("c1=$c1 s1=$s1 i1=$i1")
val c2: Char? = getAppSetting("Char thing")
val s2: String? = getAppSetting("String thing")
val i2: Int? = getAppSetting("Int thing")
println("c2=$c2 s2=$s2 i2=$i2")
}
The output is
c1=C s1=Str i1=42
c2=C s2=Str i2=42
But how do c2/s2/i2 work, the final part is this function
inline fun <reified T : Any> getAppSetting(name: String) = getAppSetting(name, T::class)
This is reified generic parameters... there is no need to pass the clazz because this can be found from the data type of the receiving variable.
There are many articles about this advanced topic, e.g.
https://typealias.com/guides/getting-real-with-reified-type-parameters/
https://medium.com/kotlin-thursdays/introduction-to-kotlin-generics-reified-generic-parameters-7643f53ba513
Now, I didn't completely answer what you wanted because you wanted to receive a DatabaseResult<T> wrapper. What might be possible, is to have a function that returns DatabaseResult<T> and you can obtain the T from it as the "clazz" parameter, but I'll leave that for someone else to improve on :-) but I think that gets you pretty close.

Select where id not in list for IntIdTable in Kotlin Exposed

In Kotlin exposed, I have a IntIdTable like this:
object UserTable : IntIdTable("user", "id") {
override val id: Column<EntityID<Int>>
get() = super.id
val name = varchar("name", 128)
val activeFlg = integer("active_flg")
...
}
So, I call the transaction that get all record from test table with exclude ids like this:
fun getAllWithExcludeIds(excludeIds: List<Int>): List<User> {
return transaction {
UserTable
.select { UserTable.id notInList excludeIds }
}
}
Then I got a type miss match error:
Type mismatch.
Required:
EntityID<Int>
Found:
Int
How can I select list of users which has id not in specified excludeIds list? Thank you!

Cannot 'find' on mutableListOf

The mytable object contains the columns variable (the CollectionColumns class) and the list variable is declared in this class, which contains objects of the Column class.
I try to search in this list by the variable name and don't find it (returns null).
val myTable = Table()
myTable.columns.add("Date")
myTable.columns.add("Name")
myTable.columns.add("Summ")
val find = myTable.columns.find("Summ")
println(find)
result: null
Does anyone know why ?
class Table() {
var columns: CollectionColumns
var rows: CollectionRows
init {
columns = CollectionColumns()
rows = CollectionRows()
}
inner class CollectionColumns() {
var list = mutableListOf<Column>()
fun add(_name: String): Table.Column{
val newColumn = Column(_name)
columns.list.add(newColumn)
for (row in rows.list) {
row.data.put(newColumn.name, null)
}
return newColumn
}
fun find(_name: String): Column?{
val findColumn = columns.list.find { it.name == _name }
return findColumn
}
}
class Column(_name: String) {
val name = _name
}
inner class CollectionRows() {
var list = mutableListOf<Row>()
fun add() : Table.Row {
val newRow = Row()
for (column in columns.list) {
newRow.data.put(column.name, null)
}
rows.list.add(newRow)
return newRow
}
}
class Row() {
var data = mutableMapOf<String, Any?>()
}
}

how to get count of "collection set" from list having specific property

I want to get a count value of set from list having specific property
Thanks in advance
class User(val age = 0, val group = "school")
fun main(
val list = listof(User(1, "school"), User(2, "school"), User(3, "company"))
fun getCount() {
//how to get 2 (the count of set such as ("school", "company").size)
}
}
If I understand your question correct, you want to know how many groups you have (in your example 2: "school" and "company"
You could make a Grouping, eg:
data class User(val age: Int = 0, val group: String = "school")
fun main() {
val list = listOf(User(1, "school"), User(2, "school"), User(3, "company"), User(16, "banana"))
println(getCount(list))
println(getCountOf(list,"banana"))
}
// returns the number of groups
fun getCount(list: List<User>): Int {
val grouping = list.groupingBy { it.group }.eachCount()
return grouping.keys.size
}
//returns the size of a given group (0 if group not found)
fun getCountOf(list: List<User>, group: String): Int {
val grouping = list.groupingBy { it.group }.eachCount()
return grouping[group] ?: 0
}
You can filter the list to those with the group "school" and then count, maybe?
list .filter { p -> group == "school" }

type-safe builder example kotlin

I want to have following person object in Kotlin :
var p = person {
age = 22
gender = "male"
name {
first = "Ali"
last = "Rezaei"
}
}
I have following code to build it :
data class Person(var age: Int? = null, var gender: String? = null
, var name : Name? = null) {
}
fun name(init: Name.() -> Unit): Name {
val n = Name()
n.init()
return n
}
data class Name(var first: String? = null, var last : String? = null)
fun person(init: Person.() -> Unit): Person {
val p = Person()
p.init()
return p
}
But when I print it, the result is following :
Person(age=22, gender="male", name=null)
What is wrong with my code?
You could make name an extension function on Person that assigns the Name to the Person instead of returning it:
fun Person.name(init: Name.() -> Unit) {
val n = Name()
n.init()
this.name = n
}
You could even consider a more concise syntax for the same, like this:
fun Person.name(init: Name.() -> Unit) {
this.name = Name().apply(init)
}
Shameless plug for my repository discussing DSL design and containing examples.
You need to assign to name. This ended up working for me...
var p = person {
age = 22
gender = "male"
name = name {
first = "Ali"
last = "Rezaei"
}
}