Bind a list of objects to JDBI SQL. Kotlin - sql

I have a list of data object that looks as follows:
data class Detail (
val type1: Type1
val type2: Type2
)
val list: List<Detail> = arrayListOf(Detail,Detail,Detail)
How do I bind the the list of details into a JDBI SQL query. I know how to do it with a single data type but just not sure how this works when you are getting properties from the "Detail" data class.
eg:
it.createQuery(
"""
SELECT
id
FROM
tbl_of_something
WHERE
type1
AND
type2
IN (<detail>)
"""
).bindList("detail", list)
.mapTo(String::class.java)
.toSet()
.toList()
.sorted()

I figured it out, this can be solved by using bindBeanList() as follows:
it.createQuery(
"""
SELECT
id
FROM
tbl_of_something
WHERE
(type1, type2)
IN (<detail>)
"""
).bindBeanList("detail", list, listOf("type1", "type2"))
.mapTo(String::class.java)
.toSet()
.toList()
.sorted()```

Related

Aliasing count() for several columns in a group by query in Exposed

I'm trying to create a query like the presented one in the ORM Exposed.
select t.a, t.b, count(*)
from table as t
group by t.a, t.b;
But it seems that .count() supports aliases only for one column, but here I need two columns:
val count = Table.Table.<somehow I need to push both fields a and b here>.count()
Table.Table
.slice(Table.Table.a, Table.Table.b, count)
.selectAll()
.groupBy(Table.Table.a, Table.Table.b)
.map { row ->
Result(
state = row[Table.Table.a],
trigger = row[Table.Table.b],
count = row[count]
)
}
Are you have any ideas on how to do it?
val count = Count(stringLiteral("*"))
This will generate COUNT('*') in your query (which is a valid SQL expression, giving the same result as COUNT(*)).
If you want to get rid of these annoying quotes, you may do the following:
val asterisk = object : Expression<String>() {
override fun toQueryBuilder(queryBuilder: QueryBuilder) {
queryBuilder { +"*" }
}
}
val count = Count(asterisk)

How to sort list of objects by two properties and collator

I have to sort list of object by lastName and firstName (if case there is the same lastName for multiple objects). I must also apply Collator to those.
Suppose I can do that for one property:
val collator = Collator.getInstance(context.getResources().getConfiguration().locale)
myList.sortedWith(compareBy(collator, { it.lastName.toLowerCase() }))
But is it possible to apply also to that another restriction to also sort by firstName?
You can add another sort criteria with thenBy:
val comparator =
compareBy(collator) { p: Person -> p.lastName.toLowerCase() }
.thenBy(collator) { p: Person -> p.firstName.toLowerCase() }
val result = myList.sortedWith(comparator)
Simplest would just be to concatenate the two properties in the selector lambda:
myList.sortedWith(
compareBy(collator) { "${it.lastName} ${it.firstName}".toLowerCase() }
)

Slick query for one to optional one (zero or one) relationship

Given tables of:
case class Person(id: Int, name: String)
case class Dead(personId: Int)
and populated with:
Person(1, "George")
Person(2, "Barack")
Dead(1)
is it possible to have a single query that would produce a list of (Person, Option[Dead]) like so?
(Person(1, "George"), Some(Dead(1)))
(Person(2, "Barack"), None)
For slick 3.0 it should be something like this:
val query = for {
(p, d) <- persons joinLeft deads on (_.id === _.personId)
} yield (p, d)
val results: Future[Seq[(Person, Option[Dead])]] = db.run(query.result)
In slick, outer joins are automatically wrapped in an Option type. You can read more about joining here: http://slick.typesafe.com/doc/3.0.0/queries.html#joining-and-zipping

groovy oracle result capital column name issue

In Groovy to set a bean you just need to give the GroovyRowResult while creating object.
Consider below People.groovy bean:
class People {
String name
int age
}
My sql query:
select * from People -- returns name and age of people
the GroovyRowResult is returned with column names (keys) in capitals like it:[NAME:"Alex", AGE: 21].
So when I try to set the bean like below:
le.rows(sqlQuery).each {
People p = new People(it)
}
I receive the Exception:
groovy.lang.MissingPropertyException: No such property: NAME for class: People. Possible solutions: name
I guess I can modify sql query to include double quotes on the alias, but have you guys handled it any different?
The rows() method returns a List<GroovyRowResult> where GroovyRowResult implements Map and then you can apply collectEntries to transform it, so that the keys are lowercase and you can use the resulting map:
sql.rows('select * from people').each {
People p = new People(it.collectEntries { k,v -> [k.toLowerCase(), v] })
println p.name
println p.age
}

scala: how to model a basic parent-child relation

I have a Brand class that has several products
And in the product class I want to have a reference to the brand, like this:
case class Brand(val name:String, val products: List[Product])
case class Product(val name: String, val brand: Brand)
How can I poulate these classes???
I mean, I can't create a product unless I have a brand
And I can't create the brand unless I have a list of Products (because Brand.products is a val)
What would be the best way to model this kind of relation?
I would question why you are repeating the information, by saying which products relate to which brand in both the List and in each Product.
Still, you can do it:
class Brand(val name: String, ps: => List[Product]) {
lazy val products = ps
override def toString = "Brand("+name+", "+products+")"
}
class Product(val name: String, b: => Brand) {
lazy val brand = b
override def toString = "Product("+name+", "+brand.name+")"
}
lazy val p1: Product = new Product("fish", birdseye)
lazy val p2: Product = new Product("peas", birdseye)
lazy val birdseye = new Brand("BirdsEye", List(p1, p2))
println(birdseye)
//Brand(BirdsEye, List(Product(fish, BirdsEye), Product(peas, BirdsEye)))
By-name params don't seem to be allowed for case classes unfortunately.
See also this similar question: Instantiating immutable paired objects
Since your question is about model to this relationship, I will say why not just model them like what we do in database? Separate the entity and the relationship.
val productsOfBrand: Map[Brand, List[Product]] = {
// Initial your brand to products mapping here, using var
// or mutable map to construct the relation is fine, since
// it is limit to this scope, and transparent to the outside
// world
}
case class Brand(val name:String){
def products = productsOfBrand.get(this).getOrElse(Nil)
}
case class Product(val name: String, val brand: Brand) // If you really need that brand reference