I have a table that has two fields of type Datetime and int.
object DummyTable : IntIdTable("db.table", columnName = "id") {
val createdAt = datetime("created_at")
val mins = integer("mins")
override val primaryKey = PrimaryKey(id)
}
I have a function where i want to check the condition that createdAt plus mins is less than the Datetime now.
fun isValid(id: String): String? {
return DummyTable.select{
COND??
}.single()[DummyTable.age]
}
Something like this:
DummyTable.createdAt plus minutes lessEq DateTime.now()??
What would be the query condition?
I thought that answer on github was enought.
class DateAddMins(val dateExp: Expression<DateTime>, val addMins: Expression<Int?>) : Expression<DateTime>() {
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
append("DATE_ADD(", dateExp, ", INTERVAL IFNULL(", addMins, ", 0) MINUTE)")
}
}
DummyTable.select {
DateAddMins(DummyTable.createdAt, DummyTable.mins) lessEq DateTime.now)
}
Related
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.
Using Kotlin and spring data mongodb, I'm trying to find the most idiomatic method that can receive nullable parameters (through user defined filters) and create a MongoTemplate Query with the non-null ones (which, in Java, would be a bunch of ifs). This is what I came up with, so far, but I wonder if there's a better way:
// extending Query
open class Filter {
// case insensitive 'like' criteria
fun Query.like(field: String, value: String?) = value?.let {
this.addCriteria(Criteria.where(field).regex(it,"i"))
}
// queries from a date, to a date or between two dates
fun <T: Comparable<T>> Query.between(field: String, from: T?, to: T?) {
if (from != null || to != null) {
val criteria = where(field)
from?.let { criteria.gte(it) }
to?.let { criteria.lte(it) }
this.addCriteria(criteria)
}
}
fun Query.onlyActive(active: Boolean?) = when (active) {
true -> this.addCriteria(Criteria.where("active").`is`(true))
else -> null
}
}
// data class extending Filter()
data class myFilter(val: name: String, val type: String?, val content:String?,
val fromNum: Int?, val toNum: Int?, val fromDate: LocalDateTime?,
val toDate: LocalDateTime?, val active? = false): Filter() {
fun toQuery(): Query {
// name is a mandatory param
val query = Query.query(Criteria.where("name").`is`(name))
with(query) {
like("type", type)
like("content", content)
between("num", fromNum, toNum)
between("date", fromDate, toDate)
onlyActive(active)
}
return query
}
}
I'd like to iterate all dates within a given Joda interval:
val interval = Interval(DateTime.now().minusDays(42), DateTime.now())
How to do that in Kotlin?
Heavily inspired by your current solution:
fun Interval.toDateTimes() = generateSequence(start) { it.plusDays(1) }
.takeWhile(::contains)
Usage:
interval.toDateTimes()
.forEach { println(it) }
If you need the LocalDate you could still do the following instead:
interval.toDateTimes()
.map(DateTime::toLocalDate)
.forEach { println(it) }
or as an extension function to Interval again:
fun Interval.toLocalDates() = toDateTimes().map(DateTime::toLocalDate)
If you want the end date to be inclusive instead, use takeWhile { it <= end } instead.
The following extension function gives a Sequence of LocalDate objects from the given Interval, which can be used to iterate those dates.
fun Interval.toLocalDates(): Sequence<LocalDate> = generateSequence(start) { d ->
d.plusDays(1).takeIf { it < end }
}.map(DateTime::toLocalDate)
Usage:
val interval = Interval(DateTime.now().minusDays(42), DateTime.now())
interval.toLocalDates().forEach {
println(it)
}
In this solution, the last day, DateTime.now() is not included in the Sequence since that's how Interval is implemented as well:
"A time interval represents a period of time between two instants. Intervals are inclusive of the start instant and exclusive of the end."
If, for any reason, you want to make it include the last day, just change the takeIf condition to it <= end.
I guess if you need it more than once, it would be better to overload rangeTo operator to allow this syntax
for (i in LocalDate.now() .. LocalDate.now().plusWeeks(1)) {
System.out.print(i) // 2018-08-30 2018-08-31 2018-09-01
}
Here is the code for operator extension:
operator fun LocalDate.rangeTo(other: LocalDate): LocalDateRange {
return LocalDateRange(this, other)
}
And necessary classes:
class LocalDateRange(override val start: LocalDate, override val endInclusive: LocalDate)
: ClosedRange<LocalDate>, Iterable<LocalDate> {
override fun iterator(): Iterator<LocalDate> {
return DateIterator(start, endInclusive)
}
}
class DateIterator(start: LocalDate, private val endInclusive: LocalDate)
: Iterator<LocalDate> {
private var current = start
override fun hasNext(): Boolean {
return current <= endInclusive
}
override fun next(): LocalDate {
current = current.plusDays(1)
return current
}
}
LocalDate is preferred nowadays, so we can simply iterate with day as number:
for (day in minDate.toEpochDay()..maxDate.toEpochDay()) {
// ...
}
or:
(minDate.toEpochDay()..maxDate.toEpochDay()).forEach {
// ...
}
Iterate with day as date:
generateSequence(minDate) { it.plusDays(1) }.takeWhile { it < maxDate }.forEach {
// it ...
}
or:
var day = minDate;
while (day < maxDate) {
day = day.plusDays(1);
// ...
}
I am working on a console application in Kotlin where I accept multiple arguments in main() function
fun main(args: Array<String>) {
// validation & String to Integer conversion
}
I want to check whether the String is a valid integer and convert the same or else I have to throw some exception.
How can I resolve this?
You could call toInt() on your String instances:
fun main(args: Array<String>) {
for (str in args) {
try {
val parsedInt = str.toInt()
println("The parsed int is $parsedInt")
} catch (nfe: NumberFormatException) {
// not a valid int
}
}
}
Or toIntOrNull() as an alternative:
for (str in args) {
val parsedInt = str.toIntOrNull()
if (parsedInt != null) {
println("The parsed int is $parsedInt")
} else {
// not a valid int
}
}
If you don't care about the invalid values, then you could combine toIntOrNull() with the safe call operator and a scope function, for example:
for (str in args) {
str.toIntOrNull()?.let {
println("The parsed int is $it")
}
}
Actually, there are several ways:
Given:
// aString is the string that we want to convert to number
// defaultValue is the backup value (integer) we'll have in case of conversion failed
var aString: String = "aString"
var defaultValue : Int = defaultValue
Then we have:
Operation
Successful operation
Unsuccessful Operation
aString.toInt()
Numeric value
NumberFormatException
aString.toIntOrNull()
Numeric value
null
aString.toIntOrNull() ?: defaultValue
Numeric value
defaultValue
If aString is a valid integer, then we will get is numeric value, else, based on the function used, see a result in column Unsuccessful Operation.
val i = "42".toIntOrNull()
Keep in mind that the result is nullable as the name suggests.
As suggested above, use toIntOrNull().
Parses the string as an [Int] number and returns the result
or null if the string is not a valid representation of a number.
val a = "11".toIntOrNull() // 11
val b = "-11".toIntOrNull() // -11
val c = "11.7".toIntOrNull() // null
val d = "11.0".toIntOrNull() // null
val e = "abc".toIntOrNull() // null
val f = null?.toIntOrNull() // null
I use this util function:
fun safeInt(text: String, fallback: Int): Int {
return text.toIntOrNull() ?: fallback
}
In Kotlin:
Simply do that
val abc = try {stringNumber.toInt()}catch (e:Exception){0}
In catch block you can set default value for any case string is not converted to "Int".
string_name.toString().toInt()
converts string_name to String and then the resulting String is converted to int.
i would go with something like this.
import java.util.*
fun String?.asOptionalInt() = Optional.ofNullable(this).map { it.toIntOrNull() }
fun main(args: Array<String>) {
val intArgs = args.map {
it.asOptionalInt().orElseThrow {
IllegalArgumentException("cannot parse to int $it")
}
}
println(intArgs)
}
this is quite a nice way to do this, without introducing unsafe nullable values.
add (?) before fun toInt()
val number_int = str?.toInt()
You can Direct Change by using readLine()!!.toInt()
Example:
fun main(){
print("Enter the radius = ")
var r1 = readLine()!!.toInt()
var area = (3.14*r1*r1)
println("Area is $area")
}
fun getIntValueFromString(value : String): Int {
var returnValue = ""
value.forEach {
val item = it.toString().toIntOrNull()
if(item is Int){
returnValue += item.toString()
}
}
return returnValue.toInt()
}
I'm trying to create a query that returns the number of rows from a table based on the passed in ID and an array of boolean values that acts as an extra filter. As for now I add the filter using a vararg boolean, but this isn't optimal. Is there some other way to add this conditional functionality, that would make better sense?
Here's my query-method:
#Query("SELECT COUNT(id) FROM tasks WHERE id = :id AND deleted IN (:filterDeleted)")
abstract fun countTasksById(id: String, vararg filterDeleted: Boolean = booleanArrayOf(false, true)): Int
And here's how it's used:
#Test
fun shouldCountOnlyDeleted() {
insertTestData(appDatabase)
val deleted = TEST_TASKS.take(5)
repository.softDeleteAll(deleted)
val nonDeleted = TEST_TASKS.filterNot { it in deleted }
deletedStudies.forEach {
assertThat(repository.countTasks(it.id, true), `is`(1))
}
nonDeleted.forEach {
assertThat(repository.countTasks(it.id, true), `is`(0))
}
}
#Test
fun shouldCountBothDeletedAndNonDeleted() {
insertTestData(appDatabase)
val deleted = TEST_TASKS.take(5)
repository.softDeleteAll(deleted)
val nonDeleted = TEST_TASKS.filterNot { it in deleted }
deletedStudies.forEach {
assertThat(repository.countTasks(it.id, false, true), `is`(1))
}
nonDeleted.forEach {
assertThat(repository.countTasks(it.id, false, true), `is`(1))
}
}
You can try this:
#Delete
public void deleteTasks(Task... tasks)
See: enter link description here