Getting “Couldn't find PersistentEntity for type X” when setting r2dbcCustomConversions - spring-data-r2dbc

I'm getting "Couldn't find PersistentEntity" mapping exception when trying to set r2dbcCustomConversions.
I followed some code snippets from r2dbc reference docs
Here is my code:
#Configuration
class ServiceConfiguration : AbstractR2dbcConfiguration() {
#Bean
override fun connectionFactory() =
ConnectionFactories.get("CONNECTION_STRING")
override fun r2dbcCustomConversions() = R2dbcCustomConversions(
storeConversions,
listOf(PersonReadConverter(), PersonWriteConverter())
)
}
#ReadingConverter
class PersonReadConverter : Converter<Row, Person> {
override fun convert(source: Row): Person {
return Person(
source.get("id", String::class.java),
source.get("name", String::class.java),
source.get("age", Int::class.java)
)
}
}
#WritingConverter
class PersonWriteConverter : Converter<Person, OutboundRow> {
override fun convert(source: Person): OutboundRow? {
val row = OutboundRow()
row["id"] = SettableValue.from(source.id!!)
row["name"] = SettableValue.from(source.name!!)
row["age"] = SettableValue.from(source.age!!)
return row
}
}
#Table
data class Person(#Id val id: String?, val name: String?, val age: Int?)
#Service
class PersonService(private val databaseClient: DatabaseClient) : InitializingBean {
override fun afterPropertiesSet() {
selectAll()
.subscribe(
{ println("Data: $it") },
{ println("Error: $it") },
{ println("Done") }
)
}
fun selectAll() = databaseClient
.select()
.from(Person::class.java)
.fetch()
.all()
}
Here is the output I'm getting:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'personService' defined in file [\demo\reactive-web\target\classes\com\example\demo\PersonService.class]: Invocation of init method failed; nested exception is org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class com.example.demo.Person!
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1783) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:868) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.2.0.M2.jar:5.2.0.M2]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.2.0.M2.jar:5.2.0.M2]
at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:67) ~[spring-boot-2.2.0.M3.jar:2.2.0.M3]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:782) [spring-boot-2.2.0.M3.jar:2.2.0.M3]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:404) [spring-boot-2.2.0.M3.jar:2.2.0.M3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:319) [spring-boot-2.2.0.M3.jar:2.2.0.M3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1275) [spring-boot-2.2.0.M3.jar:2.2.0.M3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1263) [spring-boot-2.2.0.M3.jar:2.2.0.M3]
at com.example.demo.DemoApplicationKt.main(DemoApplication.kt:13) [classes/:na]
I noticed the docs a bit out of date. For example, the PersonWriteConverter sample required using of SettableValue. I'm not sure if this is a bug or I'm missing something

Registering custom converters for a domain type considers the domain type to be a simple type. Simple types are not inspected for mapping metadata. We already have a ticket to address that issue.
Switch to execute.sql("SELECT …").asType<Person>() to consume results.

Related

mockk, get error when using mockkstatic for stub static function

using io.mockk 1.11.0
having some class with #JvmStatic function
class LogUtil {
#JvmStatic
fun logData(jsonStr: String) {
val jsonObj = getDataJson(jsonStr)
if (jsonObj == null) {
Log.e("+++", "+++ wrong json")
}
// ......
}
}
data util
class DataUtil {
#JvmStatic
fun getDataJson(json: String): JSONObject? {
return try {
JSONObject(json)
} catch (e: Exception) {
null
}
}
}
The test is to verify the Log.e(...) is called when a null is returned from getDataJson().
#Test
fun test_() {
io.mockk.mockkStatic(android.utils.Log::class)
io.mockk.mockkStatic(DataUtil::class)
every { DataUtil.getDataJson(any()) } returns null //<== error points to this line
LogUtil.logData("{key: value}")
verify(exactly = 1) { android.utils.Log.e("+++", any()) }
}
got error
io.mockk.MockKException: Failed matching mocking signature for
left matchers: [any()]
if change to every { DataUtil.getDataJson("test string") } returns null, it will get error
MockKException: Missing mocked calls inside every { ... } block: make sure the object inside the block is a mock
How to use mockkStatic for a #JvmStatic unction?
The use of mockkStatic is correct, but DataUtil is not static.
If your code is kotlin, must use object instead class:
object DataUtil { .. }
object LogUtil { .. }
PD: Use unmockkStatic in #After method for avoid side effects that may affect other test.

Save in ReactiveCrudRepository not inserting or updating records

As stated in the title I'm not been able to insert or update records in my Postgres database.
I just started working with spring and kotlin so there might be some pretty basic configuration that it's missing. Thanks in advance
Here is my code base
UserRepository
#Repository
interface UserRepository : ReactiveCrudRepository<User, Int> {}
User model
#Table("user_app")
data class User (
#Id
#Column("id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
val id : Int? = null,
#Column("username")
val username : String?,
#Column("email")
val email : String?,
#Column("name")
val name : String?,
#Column("password")
val password : String?
)
UserController
#Component
class UserController{
#Autowired
private lateinit var userRepository : UserRepository
fun getAllUsers() : Flux<User> = userRepository.findAll()
fun getUserById( userId: Int) : Mono<User> = userRepository.findById(userId)
fun createUser(user: User): Mono<User> = userRepository.save(user)
fun updateUser(user: User) : Mono<User> = userRepository.save(user)
}
UserConfiguration
#Configuration
class UserConfiguration {
#FlowPreview
#Bean
fun userRouter(controller: UserController): RouterFunction<ServerResponse> = router{
("/users").nest{
GET("") { _ ->
ServerResponse.ok().body(controller.getAllUsers())
}
GET("/{id}") { req ->
ServerResponse.ok().body(controller.getUserById(req.pathVariable("id").toInt()))
}
accept(MediaType.APPLICATION_JSON)
POST("") {req ->
ServerResponse.ok().body(req.bodyToMono(User::class.java).doOnNext { user ->
controller.createUser(user) })
}
accept(MediaType.APPLICATION_JSON)
PUT("") {req ->
ServerResponse.ok().body(req.bodyToMono(User::class.java).doOnNext { user ->
run {
controller.updateUser(user)
}
})
}
}
}
}
R2dbcConfiguration
#Configuration
#EnableR2dbcRepositories
class R2dbcConfiguration : AbstractR2dbcConfiguration() {
#Bean
override fun connectionFactory(): PostgresqlConnectionFactory {
val config = PostgresqlConnectionConfiguration.builder()
.host("localhost")
.port(5432)
.database("postgres")
.username("postgres")
.password("pass123")
.build()
return PostgresqlConnectionFactory(config)
}
}
Found out why this wasnt working.
Save does not persist the changes in the database, to do that you need to call some methods. I called subscribe but because this is a blocking method I ran it either in a let block or a doOnNext
fun createUser(request: ServerRequest): Mono<ServerResponse> {
println("createUser")
val badRequest = ServerResponse.badRequest().build()
val user = request.bodyToMono(User::class.java)
return user
.doOnNext { u -> userRepository.save(u).subscribe() }
.flatMap {
ServerResponse.ok().contentType(APPLICATION_JSON).body(fromValue(it))
}
}

Kotlin use SpringData Jpa custom repository

This is my code. I customized my repository.
interface ChapterDao {
fun test(novelId:String):List<Chapter>
}
class ChapterDaoImpl constructor(#PersistenceContext var entityManager: EntityManager){
fun test(novelId: String): List<Chapter> {
val query = entityManager.createNativeQuery("select c.name, c.number from chapter c where c.novel.id = $novelId")
val resultList = query.resultList as Array<Array<Any>>
var chapterList:ArrayList<Chapter> = ArrayList<Chapter>()
for (item in resultList){
chapterList.add(Chapter(item.get(0) as String,item.get(1) as Int))
}
return chapterList
}
}
interface ChapterRepository : CrudRepository<Chapter, String>, ChapterDao {
}
Chapter code is:
package com.em248.entity;
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonInclude
import java.util.*
import javax.persistence.*
#Entity
#Table(name = "chapter")
#com.fasterxml.jackson.annotation.JsonInclude(JsonInclude.Include.NON_EMPTY)
class Chapter {
#Id
var id: String = UUID.randomUUID().toString()
var number: Int = -1
var name: String = ""
#Column(columnDefinition = "text")
var content: String? = ""
#Temporal(TemporalType.TIMESTAMP)
var createDate: Date = Date()
#ManyToOne
#JoinColumn(name = "novel_id")
#JsonIgnore
var novel: Novel = Novel();
constructor()
constructor(name: String, number: Int)
constructor(number: Int, name: String, content: String?, createDate: Date, novel: Novel) {
this.number = number
this.name = name
if (content != null) this.content = content
this.createDate = createDate
this.novel = novel
}
}
But when using the test function, it throws an error:
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property test found for type Chapter!
at org.springframework.data.mapping.PropertyPath.lambda$new$0(PropertyPath.java:82) ~[spring-data-commons-2.0.0.M3.jar:na]
at java.util.Optional.orElseThrow(Optional.java:290) ~[na:1.8.0_111]
at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:82) ~[spring-data-commons-2.0.0.M3.jar:na]
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:304) ~[spring-data-commons-2.0.0.M3.jar:na]
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:284) ~[spring-data-commons-2.0.0.M3.jar:na]
at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:243) ~[spring-data-commons-2.0.0.M3.jar:na]
I search how to implement a custom repository, but I don't see the difference to my code?
Rename ChapterDaoImpl to ChapterRepositoryImpl.
Spring Data looks for custom implementations named after the Repository Interface + Impl.
You named the implementation based on the custom interface.

Swift Realm Object and Mappable

I'm using "Realm" and "ObjectMapper" in swift
"Realm" does not support inheritance, so I have to make model like this:
class Model1 : Object, Mappable
class Model2 : Object, Mappable
And I wanted to make a function that find local data with model name in string and primary key.
func fetchLocal(name : String, key : String)->Object{
switch(name){
case "Model1":
~~~~
return Model1
case "Model2":
~~~~
return Model2
}
}
And when I using this function, cast object to Mappable
if let mappable = fetchLocal(name : "Model1", key: "~~~~") as? Mappable{
let json = mappable.toJSON()
}
Then run time error comes out without message like :
Thread 1: EXC_BAD_ACCESS (code = 1, address = 0x0)
I tracked this error but app just crash in Mappable.swift at:
public func toJSON() -> [String: Any] {
return Mapper().toJSON(self) <------here
}
I think the reason is that function "fetchLocal" just return "Object" not "Mappable", but returning class is obviously implements "Mappable", so it passes the "if let as" clause, but error comes out when calling "toJSON()".
As "Realm Object" cannot be implemented, I cannot make a class like "ObjectMappable : Object, Mappable" and let the "fetchLocal" function to return "ObjectMappable"
So, I think only option is making "fetchLocal" function to return a class that implements "Object and Mappable", but I don't know how.
Please, help me.
public class Model: Object, Mappable {
public required init(map: Map) {
super.init()
}
required public init() {
super.init()
}
required public init(realm: RLMRealm, schema: RLMObjectSchema) {
super.init(realm: realm, schema: schema)
}
required public init(value: Any, schema: RLMSchema) {
super.init(value: value, schema: schema)
}
public override init(value: Any) {
super.init(value: value)
}
public func mapping(map: Map) {
}
}
realm query
func fetchLocal<AnyModel: Model>( type: AnyModel.Type, key: String ) -> AnyModel {
yourRealm.object(ofType: type, forPrimaryKey: key)
}
Any model
class Model1 : Model {
}
select
let m:Model1? = fetchLocal(type: Model1.self, key: "1")

Implement your own object binder for Route parameter of some object type in Play scala

Well, I want to replace my String param from the following Play scala Route into my own object, say "MyObject"
From GET /api/:id controllers.MyController.get(id: String)
To GET /api/:id controllers.MyController.get(id: MyOwnObject)
Any idea on how to do this would be appreciated.
Well, I have written up my own "MyOwnObject" binder now. Another way of implementing PathBindable to bind an object.
object Binders {
implicit def pathBinder(implicit intBinder: PathBindable[String]) = new PathBindable[MyOwnObject] {
override def bind(key: String, value: String): Either[String, MyOwnObject] = {
for {
id <- intBinder.bind(key, value).right
} yield UniqueId(id)
}
override def unbind(key: String, id: UniqueId): String = {
intBinder.unbind(key, id.value)
}
}
}
Use PathBindable to bind parameters from path rather than from query. Sample implementation for binding ids from path separated by comma (no error handling):
public class CommaSeparatedIds implements PathBindable<CommaSeparatedIds> {
private List<Long> id;
#Override
public IdBinder bind(String key, String txt) {
if ("id".equals(key)) {
String[] split = txt.split(",");
id = new ArrayList<>(split.length + 1);
for (String s : split) {
long parseLong = Long.parseLong(s);
id.add(Long.valueOf(parseLong));
}
return this;
}
return null;
}
...
}
Sample path:
/data/entity/1,2,3,4
Sample routes entry:
GET /data/entity/:id controllers.EntityController.process(id: CommaSeparatedIds)
I'm not sure if it works for binding data in the path part of a URL, but you may want to read the docs on QueryStringBindable if you're able to accept your data as query params.