#Value on R2dbc table fields - spring-boot

Given the following entity/table defined in a Spring/KotlinCoroutines project.
#Table("workers")
data class Worker(
#Id
val id: UUID? = null,
#Column(value = "photo")
var photo: String? = null,
// see: https://github.com/spring-projects/spring-data-r2dbc/issues/449
#Transient
#Value("#{root.photo!=null}")
val hasPhoto: Boolean = false
)
The hasPhoto field does not map to a table field. I follow the R2dbc official reference doc and use a Spring EL to evaluate the EL result as value of this field.
But when I test this hasPhoto, it always returns false even I set the photo to a nonnull string.

Got answer from the Spring guys, #Value here only can access the projection.

Related

Dealing with nullable UUID in a controller - SpringBoot and Postgres

I have an entity that includes a nullable UUID (but it is not an ID), as follows:
#Column(nullable = true)
var myField: UUID? = null
I also have a dto converter as follows:
myField = entity.myField.let { it }
I can correctly store a null value for the UUID field, but when inspecting the GET response from my controller whenever I pass a null value for such field the body does not contain the myField at all. What is the reason for that? How does SpringBoot deal with null value for UUID.
Thanks

Spring's findByColumnName returning empty list

I need to retrieve a list of Category from the DB on the basis of value of column called owner. Here is my Category -
#Entity
#Table(name = "categories")
class Category(#Column(name = "category_id", nullable = false)
#Id #GeneratedValue(strategyGenerationType.AUTO)
var id: Long = 0,
#Column(name = "category_owner", nullable = false)
#field:NotNull(message = "Please assign an owner")
var owner: Long?,
#Column(name = "category_name", nullable = false)
#field:NotEmpty(message = "Please assign a name")
var name: String?)
Here is my interface which defines the function findByOwner -
interface CategoryRepository: JpaRepository<Category, Long> {
fun findByOwner(categoryOwner: Long): List<Category>
}
However, when I call the method, I get no response. I have made sure that the DB has correct data and I'm providing the correct owner Id. Have even invalidated the cache etc. What could be going wrong?
EDIT:
After spring.jpa.show-sql=true -
findAll()
Hibernate: select category0_.category_id as category1_0_, category0_.category_name as category2_0_, category0_.category_owner as category3_0_ from categories category0_
findByOwner()
Hibernate: select category0_.category_id as category1_0_, category0_.category_name as category2_0_, category0_.category_owner as category3_0_ from categories category0_ where category0_.category_owner=?
EDIT 2:
Turns out that my implementation was fine all along. The bug was in my service.
Create your named method according with the name of the column.
fun findByCategoryOwner(categoryOwner: Long): List<Category>
Or use #Query
#Query("SELECT * FROM categories WHERE category_owner = ?1", nativeQuery = true)
fun findByOwner(cateogryOwner: Long): List<Category
Can you put a breakpoint in org.springframework.data.jpa.repository.query.JpaQueryExecution class and when you execute findByOwner, it will come here.
When it reaches this breakpoint, select the query.createQuery(accessor).getResultList() and evaluate to see what value is returned by hibernate for spring-data-jpa to use
This post should help you. It appears to be happeing because of the parameter name mismatch.
Use camelCase to name your variables in Entity class then jpa will auto recognise the column name
findByCategoryOwner(String categoryOwner)
If you still wish to have underscore in your column names then try this
findByCategory_Owner(String categoryOwner)
I haven't tried the second option though
At least in java you need to provide the id in the method name:
**fun findByOwner_Id(categoryOwner: Long): List<Category>**
So change it from findByOwner -> findByOwnerId.

findByPropertyAndReleation not giving me the expected Entity

I'm importing historical football (or soccer, if you're from the US) data into a Neo4j database using a spring boot application (2.1.6.RELEASE) with the spring-boot-starter-data-neo4j dependency and a standalone, locally running 3.5.6 Neo4j database server.
But for some reason searching for an entity by a simple property and an attached, referenced entity, does not work, althought the relation is present in the database.
This is the part of the model, that is currently giving me a headache:
#NodeEntity(label = "Season")
open class Season(
#Id
#GeneratedValue
var id: Long? = null,
#Index(unique = true)
var name: String,
var seasonNumber: Long,
#Relationship(type = "IN_LEAGUE", direction = Relationship.OUTGOING)
var league: League?,
var start: LocalDate,
var end: LocalDate
)
#NodeEntity(label = "League")
open class League(
#Id
#GeneratedValue
var id: Long? = null,
#Index(unique = true)
var name: String,
#Relationship(type = "BELONGS_TO", direction = Relationship.OUTGOING)
var country: Country?
)
(I left out the Country class, as I'm pretty sure that it is not part of the problem)
To allow running the import more than once, I want to check if the corresponding entity is already present in the database and only import newer ones. So I added the following method SeasonRepository:
open class SeasonRepository : CrudRepository<Season, Long> {
fun findBySeasonNumberAndLeague(number: Long, league: League): Season?
}
But it is giving me a null result instead of the existing entity on consecutive runs, hence I get duplicates in my database.
I would have expected spring-data-neo4j to reduce the passed League to its Id and then have a generated query that looks somewhat like this:
MATCH (s:Season)-[:IN_LEAGUE]->(l:League) WHERE id(l) = {leagueId} AND s.seasonNumber = {seasonNumber} WITH s MATCH (s)-[r]->(o) RETURN s,r,o
but when I turn on finer logging on the neo4j package I see this output in the log file:
MATCH (n:`Season`) WHERE n.`seasonNumber` = { `seasonNumber_0` } AND n.`league` = { `league_1` } WITH n RETURN n,[ [ (n)-[r_i1:`IN_LEAGUE`]->(l1:`League`) | [ r_i1, l1 ] ] ], ID(n) with params {league_1={id=30228, name=1. Bundesliga, country={id=29773, name=Deutschland}}, seasonNumber_0=1}
So for some reason, spring-data seems to think, that the league property is a simple / primitive property and not a full releation, that needs to be resolved by the id (n.league= {league_1}).
I only got it to work, by passing the id of the league, and providing a custom query using the #Query annotation but I actually thought, that it would work with spring-data-neo4j out of the box.
Any help appreciated. Let me know if you need more details.
Spring Data Neo4j does not support objects as parameters at the moment. It is possible to query for properties on related entities/nodes e.g. findBySeasonNumberAndLeagueName if this is a suitable solution.

Kotlin not nullable value can be null?

I have backend that return me some json.
I parse it to my class:
class SomeData(
#SerializedName("user_name") val name: String,
#SerializedName("user_city") val city: String,
var notNullableValue: String
)
Use gson converter factory:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ENDPOINT)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
And in my interface:
interface MyAPI {
#GET("get_data")
Observable<List<SomeData>> getSomeData();
}
Then I retrieve data from the server (with rxJava) without any error. But I expected an error because I thought I should do something like this (to prevent GSON converter error, because notNullableValue is not present in my JSON response):
class SomeData #JvmOverloads constructor(
#SerializedName("user_name") val name: String,
#SerializedName("user_city") val city: String,
var notNullableValue: String = ""
)
After the data is received from backend and parsed to my SomeData class with constructor without def value, the value of the notNullableValue == null.
As I understand not nullable value can be null in Kotlin?
Yes, that is because you're giving it a default value. Ofcourse it will never be null. That's the whole point of a default value.
Remove ="" from constructor and you will get an error.
Edit: Found the issue. GSON uses the magic sun.misc.Unsafe class which has an allocateInstance method which is obviously considered very unsafe because what it does is skip initialization (constructors/field initializers and the like) and security checks. So there is your answer why a Kotlin non-nullable field can be null. Offending code is in com/google/gson/internal/ConstructorConstructor.java:223
Some interesting details about the Unsafe class: http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/
Try to override constructor like this:
class SomeData(
#SerializedName("user_name") val name: String,
#SerializedName("user_city") val city: String,
var notNullableValue: String = "") {
constructor() : this("","","")
}
Now after server response you can check the notNullableValue is not null - its empty

Spring Annotation not working in Kotlin

Any have problem of annotation doesnt work in Kotlin?
#Column(unique=true, nullable = false)
#Size(min = 1, max = 50)
var name: String = "",
#Size(max = 100)
var description: String = ""
I can save into database with empty string and the unique constraint also not working via the annotation. Anyone have similar issue before ? Kindly advise :)
When using Kotlin, you need to apply the constraint validators on the getter methods. This should work:
#Column(unique=true, nullable = false)
#get: Size(min = 1, max = 50)
var name: String = "",
#get: Size(max = 100)
var description: String = ""
I do not think your issue is with Kotlin exactly.
#Size and #Pattern annotation are not working in Spring MVC
It is possible that you have missed the org.hibernate:hibernate-validator package in your dependency, but without more information I cannot be certain.
You can change to
#field: Size(max = 100)
var description: String = ""

Resources