JDBI: querying a string column with kotlin - kotlin

I have a column title in a SQL table myTable.
Using Kotlin and JDBI, how would I go about getting all the distinct entries in this table?
This is what I have tried so far:
val jdbi = Jdbi.create("...url", "...user", "...password")
fun getTitles(): List<String> = jdbi.withHandleUnchecked { handle ->
handle.createQuery("select distinct(title) from myTable;")
.mapTo(String.javaClass)
.list()
However, this gives me the following exception:
A bean, Companion was mapped which was not instantiable (cannot find appropriate constructor)
What's going wrong here?

Apparently, String.javaClass is not what I want here (as it is a different type than is required). It is String::class.java.

Related

Jooq: JsonConverter not converting jsonb into list of class when fetching data

This is a continuation of a first question I asked here: Jooq: How can I map a JSONB column to a Kotlin data class field?
Although I'm able to create new records just fine with the changes mentioned there, I'm not being able to fetch data like so:
fun findAllTrackedEvents(): List<TrackedEvent> {
return dslContext.select(*TRACKED_EVENT.fields())
.from(TRACKED_EVENT)
.fetchInto(TrackedEvent::class.java)
}
It seems that jackson is mapping the rows into LinkedHashMaps instead of mapping them into the fields of the Metadata data class. This is the error I'm getting:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON:
object is not an instance of declaring class; nested exception is com.fasterxml.jackson.databind.JsonMappingException:
object is not an instance of declaring class (through reference chain: java.util.ArrayList[0]->com.my.project.tracked_event.TrackedEvent["metadata"]->java.util.ArrayList[0]->java.util.LinkedHashMap["tableRef"])]
data class TrackedEvent(
val id: UUID,
// other fields
val metadata: List<Metadata> // this metadata field in the database is of type jsonb
)
data class Metadata(
val tableRef: String,
val value: UUID
)
So it can convert the field properly when inserting but not when fetching?
In my previous answer, I suggested you use arrays instead of lists. This had a reason. Consider this:
fun main() {
val a: Array<Int?> = arrayOf(1)
println(a::class.java)
val b: List<Int?> = listOf(1)
println(b::class.java)
}
It prints:
class [Ljava.lang.Integer;
class java.util.Collections$SingletonList
As you can see, while arrays are reified on the JVM, other generic types are not, and the T type variable of List<T> is erased. It is possible that Jackson cannot figure out the correct type to unmarshal at runtime using reflection, despite all the type information being available at compile time.
I would just use Array<Metadata> instead. Alternatively, of course, you can attach a custom converter to the column, instead of using the out of the box <jsonConverter>. That way, you're in full control of the mapping.

Kotlin: get members of a data class by reflection in the order they have been defined

Assume the following simple example data class:
data class SomeDataClass(
var id: String,
var name: String,
var deleted: String
)
With the following code it is possible to get the properties (and set or get their values):
import kotlin.reflect.full.memberProperties
val properties = SomeDataClass::class.memberProperties
print(properties.map { it.name }) // prints: [deleted, id, name]
The map within the print statement will return a List with the name of the properties in alphabetical order. I need the list in the order they have been defined in the source code, in this case: [id, name, deleted].
It doesn't seem achievable purely through reflection. The only solution I could come up with is to use a helper class defining the order:
val SomeDataClass_Order = listOf("id", "name", "deleted")
This wouldn't be a problem for one or two classes, but it is for hundreds of data classes with the largest one having up to almost one hundred properties.
Any idea would be welcome. I do not need detailed code, rather hints (like parsing the source code, annotations, etc).
If all the properties are declared in the primary constructor, you could "cheat":
val propertyNames = SomeDataClass::class.primaryConstructor!!.parameters.map { it.name }
If you want the KPropertys:
val properties = propertyNames.map { name ->
SomeDataClass::class.memberProperties.find { it.name == name }
}
This unfortunately doesn't find the properties that are declared in the class body.
I don't know about other platforms, but on Kotlin/JVM, the order in which the backing fields for the properties are generated in the class file is not specified, and a quick experiment finds that the order (at least for the version of kotlinc that I'm using right now), the order is the same as the declaration order. So in theory, you could read the class file of the data class, and find the fields. See this related answer for getting the methods in order. Alternatively, you can use Java reflection, which also doesn't guarantee any order to the returned fields, but "just so happens" to return them in declaration order:
// not guaranteed, might break in the future
val fields = SomeDataClass::class.java.declaredFields.toList()
If you do want to get the properties declared inside the class body in order too, I would suggest that you don't depend on the order at all.

Corda Custom Query with field name taken as input

I am using Corda 4.0 and I want to run a custom query with dynamic column name (colN) and column value (colC). Here is my code for the query building
builder {
val index: CriteriaExpression.ColumnPredicateExpression<Any,String> = getField(colN,CarSchemaV1.PersistentCar::class.java).equal(colC)
val customCriteria = QueryCriteria.VaultCustomQueryCriteria(index)
}
But I am getting a error in compile time
Type parameter bound for L in constructor VaultCustomQueryCriteria<L: PersistableState>(expression: CriteriaExpression<L,Boolean>,...) is not satisfied: inferred typed Any is not a subtype of StatePersistable
the issue here has to do with your use of Any in the generics of CriteriaExpression.ColumnPredicaeExpression<Any, String>. As indicated by the error message, instead of using Any you should be using a subtype of StatePersistable
According to the docs: net.corda.core.schemas.StatePersistable
Marker interface to denote a persistable Corda state entity that will always have a transaction id and index
Your schema CarSchemaV1.PersistentCar::class.java likely implements PersistentState. Try adding that into your generics.
val index: CriteriaExpression.ColumnPredicateExpression<CarSchemaV1.PersistentCar,String> = getField(colN,CarSchemaV1.PersistentCar::class.java).equal(colC)

Hibernate SQL transformation fails for Enum field type

I am using a SQL query and then transforming the result using Hibernates's Transformers.aliasToBean().
One of the columns in my query is an enum. The transformation somehow fails for the enum. What should I do? Which datatype should I use? I want more than 1 character to transform the result into my enum type.
This is how the simplified version of my query/code looks like (b is an enum in the table profiles):
session.createSQLQuery("select a, b from profiles").setResultTransformer(Transformers.aliasToBean(Profile.class))
.list();
Exception : expected type: Foo.ProfileStateEnum, actual value: java.lang.Character
Assuming that the java enum type that corresponds to column b is Foo.ProfileStateEnum, the following code snippet should work for you. (I tested with Hibernate 4.1.6)
import java.util.Properties;
import org.hibernate.type.Type;
import org.hibernate.type.IntegerType;
import org.hibernate.internal.TypeLocatorImpl.TypeLocatorImpl;
import org.hibernate.type.TypeResolver.TypeResolver;
import org.hibernate.type.EnumType;
Properties params = new Properties();
params.put("enumClass", "Foo.ProfileStateEnum");
params.put("type", "12"); /*type 12 instructs to use the String representation of enum value*/
/*If you are using Hibernate 5.x then try:
params.put("useNamed", true);*/
Type myEnumType = new TypeLocatorImpl(new TypeResolver()).custom(EnumType.class, params);
List<Profile> profileList= getSession().createSQLQuery("select a as ID, b from profiles")
.addScalar("ID", IntegerType.INSTANCE)
.addScalar("b", myEnumType )
.setResultTransformer(Transformers.aliasToBean(Profile.class))
.list();
I found two ways to achieve it.
Use org.hibernate.type.CustomType with org.hibernate.type.EnumType(put either EnumType.NAMED or EnumType.TYPE, see EnumType#interpretParameters). Like below:
Properties parameters = new Properties();
parameters.put(EnumType.ENUM, MyEnum.class.getName());
// boolean or string type of true/false; declare database type
parameters.put(EnumType.NAMED, true);
// string only; declare database type
parameters.put(EnumType.TYPE, String.valueOf(Types.VARCHAR));
EnumType<MyEnum> enumType = new EnumType<>();
enumType.setTypeConfiguration(new TypeConfiguration());
enumType.setParameterValues(parameters);
CustomType customEnumType = new CustomType(enumType);
Another simple way. Use org.hibernate.type.StandardBasicTypeTemplate with org.hibernate.type.descriptor.sql.*TypeDescriptor. Like below:
StandardBasicTypeTemplate<MyEnum> enumType =
new StandardBasicTypeTemplate<>(VarcharTypeDescriptor.INSTANCE,
new EnumJavaTypeDescriptor<>(MyEnum.class));
Let's see why you are getting this exception.
From the question it is obvious that you have used #Enumerated(EnumType.STRING) annotation for the field 'b' in you model class. So the field is an enum for your model class and a varchar for your database. Native SQL is not concerned about you model class and returns what ever is there in the database table as it is. So in your case, the SQLQuery you are using will return a String for 'b' instead of a ProfileStateEnum type. But your setter method for 'b' in the Profile class takes a ProfileStateEnum type argument.
Thus you get the exception "expected type: Foo.ProfileStateEnum, actual value: java.lang.Character"
You can use Aliasing to solve the problem.
What I suggest is, alias your column with any name you want and create a setter method for that alias in your model/dto.
For example, lets alias your column as 'enumStr'.
Then your query will look like this : "select a, b as enumStr from profiles"
Now, create a setter method for that alias in the your Profile class.
(Assuming that the enum ProfileStateEnum can have any of the two values STATE1 and STATE2)
public void setEnumStr(String str){
/*Convert the string to enum and set the field 'b'*/
if(str.equals(ProfileStateEnum.STATE1.toString())){
b = ProfileStateEnum.STATE1;
} else {
b = ProfileStateEnum.STATE2;
}
}
Now on transforming, the setter for the alias setEnumStr(String) will be invoked instead of setter for the field setB(ProfileStateEnum) and the string will be converted and saved to the type you want without any exceptions.
I am a beginner in Hibernate and the solution worked for me. I am using PostgreSQL. But I believe it works for other databases too.

How to make a return type for a result set in LINQ

I am having a problem determining how c# and LINQ solve the common problem of handling a data structure that does not necessarily return a table structure, but instead a resultset.
I have a stored procedure that works, and have included it in my DBML
[Function(Name="dbo.p_GetObject")]
public int p_GetObject([Parameter(Name="ObjectType", DbType="NVarChar(200)")] string objectType, [Parameter(Name="ItemState", DbType="Bit")] System.Nullable<bool> itemState, [Parameter(Name="IsPublished", DbType="Bit")] System.Nullable<bool> isPublished)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), objectType, itemState, isPublished);
return ((int)(result.ReturnValue));
}
The dbml says that the return type is (None) and this could be the crux issue. However I don't have a DBML object that matches the resultset.
The SP takes three parameters, and returns a result set with three columns (ID, Name, Value) with multple rows. I can create a data object for this, and call it resultSet
When I write a function call for this, I get stuck:
public List<resultset> GetObject(string objectType, bool itemState, bool isPublished)
{
MyDataContext.p_GetObject(objectType, itemState, isPublished);
}
My questions are:
how do I have the data context call to the stored procedure populate my resultSet object? Is there a better approach? What should the return type be? A SQL view? Looking for good suggestions...
If it simply isn't understanding your SP, that could be the SET FMT_ONLY issue... try generating the data from a simplified version of the SP?
Normally, SPs / UDFs that don't map 1:1 with an existing entity would expose themselves in a generated type. You can rename this in the DBML file (not in the designer), but personally I wouldn't; I tend to mark the SP as private, and write my own method that projects into my own POCO type (defined for the repository):
var typed = from row in cxt.SomeFunction(123)
select new MyType {Id = row.Id, Name = row.Name, ...}
The reason for this is partly for repository purity, and partly to guard against the designer's habit of re-writing the DBML in unexpected ways ;-p See here for more.