Method signature not resolving for kotlin in Java - spring

IN my project I am using graphql-spring-java to implement a graphql endpoint. I have the graphql schema defined and all the entities put together in kotlin. The problem is that one of my method signatures in my kotlin is not resolving correctly while all the other ones do. Here's the error verbatim:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: No method or field found with any of the following signatures (with or without one of [interface graphql.schema.DataFetchingEnvironment] as the last argument), in priority order:
ninja.familyhomestay.domain.Host.reservations()
ninja.familyhomestay.domain.Host.getReservations()
ninja.familyhomestay.domain.Host.reservations
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:582)
... 105 common frames omitted
Caused by: com.coxautodev.graphql.tools.FieldResolverError: No method or field found with any of the following signatures (with or without one of [interface graphql.schema.DataFetchingEnvironment] as the last argument), in priority order:
ninja.familyhomestay.domain.Host.reservations()
ninja.familyhomestay.domain.Host.getReservations()
ninja.familyhomestay.domain.Host.reservations
at com.coxautodev.graphql.tools.FieldResolverScanner.missingFieldResolver(FieldResolverScanner.kt:51)
at com.coxautodev.graphql.tools.FieldResolverScanner.findFieldResolver(FieldResolverScanner.kt:42)
and here is the code in question:
#Entity
#Table(name = "host")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "host")
class Host(
...
#OneToMany(mappedBy = "host")
var reservations: MutableSet<Reservation> = HashSet()
) : Serializable
#Entity
#Table(name = "reservation")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "reservation")
data class Reservation(
....
#ManyToOne
#JoinColumn(name = "host_id")
var host: Host? = null,
...) : Serializable {
companion object {
private const val serialVersionUID = 1L
}
}
Obviously there's nothing wrong with code so I am thinking that it's an issue with kotlin. What do you think? I am using kotlin version 1.2.51 and idea 2018.2.eap.

Related

Spring Boot - Entity With Kotlin List<Enum> Not Booting

I have been helping my team upgrade our Maven/SpringBoot/Kotlin project from Spring-Boot 2.7.5 to Spring-Boot 3.0.0. However, an issue on startup is preventing us from progressing. This has not been an issue before Spring-Boot 3.0.0.
Upon booting the application, I receive the following stack trace:
org.springframework.context.ApplicationContextException: Unable to start web server
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaSharedEM_entityManagerFactory': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: class sun.reflect.generics.reflectiveObjects.WildcardTypeImpl cannot be cast to class java.lang.reflect.ParameterizedType (sun.reflect.generics.reflectiveObjects.WildcardTypeImpl and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')
After spending a day checking configurations and isolating the problem, we were left with one entity in our application, where we were still having the issue. We then started removing fields from the entity, until the application was able to run. The field we removed was a kotlin.collections.List of type Interaction, an enum class that we had defined for the application.
In order to ensure privacy, here is an isolated slice of the application MVC that will replicate this issue:
package com.example.adminapp.adminauth.persistence
import com.fasterxml.jackson.databind.ObjectMapper
import jakarta.persistence.*
import org.springframework.data.repository.CrudRepository
import org.springframework.stereotype.Repository
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
#Entity
#Table(name = "a_test_entity")
class AdminTestEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Int? = null
var name: String? = null
#Column
#Convert(converter = StrListConverter::class)
var interactions: List<Interaction> = emptyList()
}
enum class Interaction { HI, BYE }
class StrListConverter : AttributeConverter<List<Interaction>, String?> {
override fun convertToDatabaseColumn(p0: List<Interaction>): String? = ObjectMapper().writeValueAsString(p0)
override fun convertToEntityAttribute(p0: String?): List<Interaction> =
p0?.let { listOf(*ObjectMapper().readValue(it, Array<Interaction>::class.java)) } ?: emptyList()
}
#Repository
interface AdminTestEntityRepository : CrudRepository<AdminTestEntity, Int?>
#RestController
#RequestMapping("/api/v1/admin/test")
class AdminTestController(private val adminTestEntityRepository: AdminTestEntityRepository) {
#GetMapping("all")
fun getAllTest() = adminTestEntityRepository.findAll()
}
The kicker for this whole issue is that it only seems to be List<Enum> that causes this issue. For example, the following three re-definitions do not cause an instance of this issue:
var interactions: ArrayList<Interaction> = emptyList()
var interactions: List<String> = emptyList()
var interactions: List<DataClass> = emptyList()
What could be the cause of this? Why is it only List<Enum>?
It seems that https://hibernate.atlassian.net/browse/HHH-15624 fixed this issue.

Spring boot Data JDBC + Querydsl error " Reason: No property count found for type"

I'm trying to use Spring data JDBC with Query DSL in Kotlin.
This is my repository
#Repository
interface UserRepository : PagingAndSortingRepository<User, Int>, QuerydslPredicateExecutor<User> {}
My entity is a simple data object
#Entity
#Table(name = "user")
data class User(
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_id_seq")
#SequenceGenerator(name = "user_id_seq", sequenceName = "user_id_seq", allocationSize = 1)
val id: Int? = null,
val name: String,
val surname: String)
I've added these lines to my build.gradle
implementation("com.querydsl:querydsl-sql-spring:4.4.0") {
exclude group: 'joda-time', module: 'joda-time'
}
kapt "com.querydsl:querydsl-apt:4.4.0:jpa"
I've added the #EnableJdbcrepositories annotation in my Spring application
But when I try to start my application I get this error:
Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository' defined in my.tests.querydsl.repositories.UserRepository defined in #EnableJdbcRepositories declared on
JdbcRepositoriesRegistrar.EnableJdbcRepositoriesConfiguration: Invocation of init method failed; nested exception is org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract long org.springframework.data.querydsl.QuerydslPredicateExecutor.count(com.querydsl.core.types.Predicate)! Reason: No property count found for type User!
I tried also to remove #EnableJdbcrepositories annotation but the error persists.
What I'm doing wrong? I'm missing some dependency?
Thanks
Infobip Spring Data Querydsl provides QuerydslPredicateExecutor fragment support for Spring Data JDBC (among other things).
With it, UserRepository definition should work as is defined, check out the module README.md JDBC section for details.
If you need SQLQuery, SQLUpdateClause or delete with Predicate support don't miss the QuerydslJdbcFragment.
The Spring-Data book says that only Spring Data JPA and MongoDB support QuerydslPredicateExecutor

"Parameter specified as non-null is null" is thrown when trying to reference one JDL entity from another entity in JHipster Kotlin

I'm trying to build a website in JDL Kotlin, but am having a problem with the JDL generated entities and edit forms. I have a JDL entity called Provider, an entity called Plan that needs to reference which provider it belongs to, and a third entity called PerDayPricing that references both Provider and Plan, all as per the following JDL configuration:
entity Provider {
// [...]
}
entity Plan {
// [...]
}
entity PerDayPricing {
// [...]
}
relationship OneToMany {
Provider to Plan
}
relationship OneToMany {
Provider to PerDayPricing
Plan to PerDayPricing
}
service all with serviceImpl
However, when I try to create or set the Provider field on a Plan item, it spits out the following error:
WARN [PID] --- [ XNIO-1 task-10] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageConversionException: JSON conversion problem: Parameter specified as non-null is null: method com.example.project.domain.Plan.setPerDayPricings, parameter <set-?>; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Parameter specified as non-null is null: method com.example.project.domain.Plan.setPerDayPricings, parameter <set-?> (through reference chain: com.example.project.domain.Plan["perDayPricings"])]
PerDayPricing is referenced here even though I didn't change any of its items. When I do try to set the Provider field on a PerDayPricing item, it gives the following error:
WARN [PID] --- [ XNIO-1 task-32] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageConversionException: JSON conversion problem: Parameter specified as non-null is null: method com.example.project.domain.Provider.setPlans, parameter <set-?>; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Parameter specified as non-null is null: method com.example.project.domain.Provider.setPlans, parameter <set-?> (through reference chain: com.example.project.domain.PerDayPricing["provider"]->com.example.project.domain.Provider["plans"])]
I actually have no idea what's going on here as I don't have a lot of experience with JHipster. I simply imported the JDL source file and let JHipster create files based on its configuration, without changing any of them, and this is already happening. References to the method names setPlans and setPerDayPricings don't even exist in the codebase, so I'm assuming they are being generated by Kotlin in the background?
Does anyone know what's going on and how I can fix it?
Edit: The following are the signatures of the entity classes (regular fields were removed for brevity):
// src/main/kotlin/domain/Provider.kt
#Entity
#Table(name = "plan")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
data class Plan(
// [...]
#OneToMany(mappedBy = "plan")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
var perDayPricings: MutableSet<PerDayPricing> = mutableSetOf(),
#OneToMany(mappedBy = "plan")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
var fixedDurationPricings: MutableSet<FixedDurationPricing> = mutableSetOf(),
#ManyToOne #JsonIgnoreProperties("plans")
var provider: Provider? = null
// jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
) : Serializable
// src/main/kotlin/domain/Plan.kt
#Entity
#Table(name = "provider")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
data class Provider(
// [...]
#OneToMany(mappedBy = "provider")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
var plans: MutableSet<Plan> = mutableSetOf(),
#OneToMany(mappedBy = "provider")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
var perDayPricings: MutableSet<PerDayPricing> = mutableSetOf(),
#OneToMany(mappedBy = "provider")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
var fixedDurationPricings: MutableSet<FixedDurationPricing> = mutableSetOf()
// jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
) : Serializable
// src/main/kotlin/domain/PerDayPricing.kt
/**
* A PerDayPricing.
*/
#Entity
#Table(name = "per_day_pricing")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
data class PerDayPricing(
// [...]
#ManyToOne #JsonIgnoreProperties("perDayPricings")
var provider: Provider? = null,
#ManyToOne #JsonIgnoreProperties("perDayPricings")
var plan: Plan? = null
// jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
) : Serializable
If it generates data classes, then compiler will generate you the setters and getters. I suppose, when you want to set the entity to one another, you did not provide those fields from the exception. Kotlin not support null values by default. So either you have to provide those fields or mark the type as nullable with ? (question mark) at the and of the type. For example
var perDayPricings:String?
You should update the question with the given code, so we can see what is the problem.

SqlResultSetMapping to POJO class from a NamedNativeQuery throwing 'could not locate appropriate constructor'

I made a #NamedNativeQuery and attached it to an entity 'Doctor', on the same entity I attached a #SqlResultSetMapping which takes the columns of the query's result and maps them to a constructor of a specifically made POJO class. This query is also connected to a JPA method, which resides in the repository of the same entity.
However I keep getting an error that the appropriate constructor could not be located, as if the #SqlResultSetMapping or the POJO constructors are not in sync. (stack trace is at the bottom)
My entity, #NamedNativeQuery and #SqlResultSetMapping:
I tried the query directly on the DB and it gave the expected result, so I am just writing the select clause
#Entity
#NamedNativeQuery(
name =
"Doctor.findFreeExaminationTimes", // name of the JPA method in entity's repository (definition below)
query =
"SELECT on_date AS onDate, LAG(to_time, 1, '00:00') OVER mojWindow AS fromTime, from_time AS toTime " +
"...",
resultSetMapping = "freeTimesByDoctorId" // name of the mapping below
)
#SqlResultSetMapping(
name = "freeTimesByDoctorId", // result set mapping name
classes = #ConstructorResult(
targetClass = DoctorAvailabilityResponse.class, // my POJO class (definition below)
columns = { // order and types are the same as in the select clause above and the POJO constructor below
#ColumnResult(name = "onDate", type = java.sql.Date.class),
#ColumnResult(name = "fromTime", type = java.sql.Time.class),
#ColumnResult(name = "toTime",type = java.sql.Time.class)
}
)
)
public class Doctor extends User {...}
The POJO class which I mention in the #ConstructorResult under 'targetClass' has a constructor with the exact order, number and type, of arguments, specified under 'columns'
My POJO class which should be mapped to the query's result:
public class DoctorAvailabilityResponse {
final private java.sql.Date onDate;
final private java.sql.Time fromTime;
final private java.sql.Time toTime;
public DoctorAvailabilityResponse(java.sql.Date onDate, java.sql.Time fromTime, java.sql.Time toTime) {
this.onDate = onDate;
this.fromTime = fromTime;
this.toTime = toTime;
}
// getters
}
My repository:
#RepositoryRestResource
public interface DoctorRepository extends UserRepository<Doctor> {
// JPA method mapped to the named native query above
List<DoctorAvailabilityResponse> findFreeExaminationTimes(#Param("doctorId") Long doctorId);
}
However when testing this JPA method I get an exception with the message 'could not locate appropriate constructor'.
My test:
#SpringBootTest
public class DoctorTests {
#Autowired
private DoctorRepository doctorRepository;
private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
#Test
public void shouldReturnDoctorAvailability() {
// Exception thrown here
List<DoctorAvailabilityResponse> freeTimes = doctorRepository.findFreeExaminationTimes(4L);
LOGGER.info(freeTimes.toString());
}
}
I cannot understand why is this happening. Is there a way to manually map this result set to the POJO while maintaining the JPA repository method?
Stack trace:
org.springframework.dao.InvalidDataAccessApiUsageException: Could not locate appropriate constructor on class : com.example.isaproj.isa_projekat_2019.Model.DTO.DoctorAvailabilityResponse; nested exception is java.lang.IllegalArgumentException: Could not locate appropriate constructor on class : com.example.isaproj.isa_projekat_2019.Model.DTO.DoctorAvailabilityResponse
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:374)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:256)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528)
...
...
Caused by: java.lang.IllegalArgumentException: Could not locate appropriate constructor on class : com.example.isaproj.isa_projekat_2019.Model.DTO.DoctorAvailabilityResponse
at org.hibernate.loader.custom.ConstructorResultColumnProcessor.resolveConstructor(ConstructorResultColumnProcessor.java:92)
at org.hibernate.loader.custom.ConstructorResultColumnProcessor.performDiscovery(ConstructorResultColumnProcessor.java:45)
at org.hibernate.loader.custom.CustomLoader.autoDiscoverTypes(CustomLoader.java:494)
at org.hibernate.loader.Loader.processResultSet(Loader.java:2333)
at org.hibernate.loader.Loader.getResultSet(Loader.java:2289)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2045)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2007)
at org.hibernate.loader.Loader.doQuery(Loader.java:953)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:354)
at org.hibernate.loader.Loader.doList(Loader.java:2810)
at org.hibernate.loader.Loader.doList(Loader.java:2792)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2624)
at org.hibernate.loader.Loader.list(Loader.java:2619)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:338)
at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:2137)
at org.hibernate.internal.AbstractSharedSessionContract.list(AbstractSharedSessionContract.java:1134)
at org.hibernate.query.internal.NativeQueryImpl.doList(NativeQueryImpl.java:173)
at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1526)
at org.hibernate.query.Query.getResultList(Query.java:165)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:409)
at com.sun.proxy.$Proxy212.getResultList(Unknown Source)
at org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:126)
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:88)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:154)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:142)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:618)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:605)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:353)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
... 73 more
Sanity check and alternative approach
To make a sanity check I deleted the #SqlResultSetMapping, in which case the query is supposed to return a list of 'Object[]' values, and then I tested each individual value in that array to check its type, it showed me that the types are what I assumed them to be 'java.sql.Date' and 'java.sql.Time' twice, and all three of them were in the expected order, (Date, Time, Time), which matches the order of the constructor parameters of my POJO class.
My entity and namedNativeQuery:
#Entity
#NamedNativeQuery(
name =
"Doctor.findFreeExaminationTimes",
query =
"SELECT on_date AS onDate, LAG(to_time, 1, '00:00') OVER mojWindow AS fromTime, from_time AS toTime " +
"..."
)
public class Doctor extends User {...}
My repository with a new return type:
#RepositoryRestResource
public interface DoctorRepository extends UserRepository<Doctor> {
List<Object[]> findFreeExaminationTimes(#Param("doctorId") Long doctorId);
}
My test:
#SpringBootTest
public class DoctorTests {
#Autowired
private DoctorRepository doctorRepository;
private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
#Test
public void shouldReturnDoctorAvailability() {
// Exception thrown here
List<DoctorAvailabilityResponse> freeTimes = doctorRepository.findFreeExaminationTimes(4L);
freeTimes.stream().forEach((ft) -> {
// Values are in expected order and of expected types
String classNameOnDate = ft[0].getClass().toString(); // java.sql.Date
String classNameFromTime = ft[1].getClass().toString(); // java.sql.Time
String classNameToTime = ft[1].getClass().toString(); // java.sql.Time
// I suppose the mapping mechanism is supposed to do something like this, but fails for some reason
DoctorAvailabilityResponse dar = new DoctorAvailabilityResponse((Date)ft[0], (Time)ft[1], (Time)ft[2]);
});
LOGGER.info(freeTimes.toString());
}
}
Running this test works perfectly which supposedly shows that the problem is in the #SqlResultSetMapping or in POJO class.
I would appreciate any feedback. Thanks!
SOLUTION
I had to change the types in the #SqlResultSetMapping and in the constructor of my POJO class.
Changed #SqlResultSetMapping
#SqlResultSetMapping(
name = "freeTimesByDoctorId",
classes = #ConstructorResult(
targetClass = DoctorAvailabilityResponse.class,
columns = {
#ColumnResult(name = "onDate", type = String.class),
#ColumnResult(name = "fromTime", type = String.class),
#ColumnResult(name = "toTime",type = String.class)
}
)
)
Changed POJO class constructor
public DoctorAvailabilityResponse(String onDate, String fromTime, String toTime) {
this.onDate = Date.valueOf(onDate);
this.fromTime = Time.valueOf(fromTime);
this.toTime = Time.valueOf(toTime);
}
This alone did not solve my problem however as I got a hibernate exception as mentioned and solved in this SO question. According to this answer I also changed my repository and added an additional annotation.
Changed repository
#RepositoryRestResource
public interface DoctorRepository extends UserRepository<Doctor> {
#Query(nativeQuery = true) // This is added
List<DoctorAvailabilityResponse> findFreeExaminationTimes(#Param("doctorId") Long doctorId);
}
Now everything works, though the question remains why didn't #SqlResultSetMapping map java.sql.* types to the constructor in the first place.
#ConstructorResult does not work very well with java.sql.Date.class or java.sql.Time.class type. A way to fix your issue is to use String.class instead and then convert the String values to Date/Time in DoctorAvailabilityResponse's constructor

Spring Data Rest & Lombok - Exception while adding adding relation

In my project I have 2 entities. Survey and entries to survey. They are in relation one to many (thare can be many entries to one survey).
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "survey_entries")
#TypeDef(name = "SurveyEntry", typeClass = SurveyEntry.class)
public class SurveyEntryEntity extends AbstractEntity {
#ManyToOne
#JoinColumn(name = "survey_id")
private SurveyEntity survey;
#NonNull
#Type(type = "SurveyEntry")
#Column(name = "responses")
// JSON db column type mapped to custom type
private SurveyEntry responses;
}
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "surveys")
#TypeDef(name = "Survey", typeClass = Survey.class)
public class SurveyEntity extends AbstractEntity {
#NonNull
#Type(type = "Survey")
#Column(name = "template")
// JSON db column type mapped to custom type
private Survey survey;
#OneToMany(mappedBy = "survey")
private List<SurveyEntryEntity> entries;
}
I have also created 2 rest repositories using Spring Data Rest:
#RepositoryRestResource(collectionResourceRel = "survey_entries", path = "survey-entries")
public interface SurveyEntryRepository extends PagingAndSortingRepository<SurveyEntryEntity, Long> {
}
#RepositoryRestResource(collectionResourceRel = "surveys", path = "surveys")
public interface SurveyRepository extends PagingAndSortingRepository<SurveyEntity,Long> {
}
I have successfully added survey by rest POST request and I can access it entries (currently empty) by sending GET to /api/surveys/1/entries.Now I want to add entry to exisiting survey. And while I can add it by sending POST (content below) to /api/survey-entries I have troubles adding it directly as a reference to survey. I'm using POST method with the same content and url /api/surveys/1/entries. What is interesting, I'm getting NullPointerException in logs and entry is not inserted but audit modify timestamp in survey is changed. What am I doing wrong? Did I miss same configuration? Or should I use different content?
Content of POST with entry:
{
"responses": {
"question1": "response1",
"question2": "response2",
"question3": "response3"
}
}
Content of POST with survey:
{
"survey": {
//survey structure
}
}
Exception:
08:41:14.730 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod - Failed to resolve argument 1 of type 'org.springframework.data.rest.webmvc.PersistentEntityResource'
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: No content to map due to end-of-input; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
#EDIT
I have tried adding entry by POST to /api/survey-entries with 'application/hal+json' Content-Type header and content as below, but now I'm getting other exception:
Content:
{
"survey" : "http://localhost:8080/api/surveys/1",
"responses": {
"question1": "response1",
"question2": "response2",
"question3": "response3"
}
}
Exception:
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.domain.SurveyEntity` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/api/surveys/1')
at [Source: (org.apache.catalina.connector.CoyoteInputStream); line: 1, column: 41] (through reference chain: com.domain.SurveyEntryEntity["survey"])
#Edit 2
Added Lombok annotations present on Entity classess
Unfortunatelly problem lied in Lombok annotations which weren't included in sample code. I added them now so any one can see where the problem lies.
I managed to solve it by downgrading Lombok to version (1.16.14) and changing annotation #AllArgsConstructor to #AllArgsConstructor(suppressConstructorProperties = true). It's immposible to achieve in later Lombok versions as this property is currently removed.
I have found solution on Spring Data Rest JIRA. There is already issue DATAREST-884 mentioning problem and presenting solution/workaround.
Sorry for wasted time while it was impossible to see solution without all the code.

Resources