Spring MVC instantiate inner class form bean property - ajax

I have post data like:
cond[startData]:2015-09-22
cond[endDate]:2015-09-23
cond[orderId]:
And my ajax is:
cond={
startDate: "2015-09-22",
endDate: "2015-09-23",
orderId: ""
}
queryData = {cond:cond};
$.ajax({
url: "orderDetail",
type: "post",
dataType:"json",
data: queryData,
success: function(data){
}
});
And my spring form bean is
public class OrderFormBean {
private Cond cond;
private Result result;
// query condition
public class Cond {
private String startDate;
private String endDate;
private String orderId;
}
// query result
public class Result {
private String orderId;
private String orderDate;
private String operator;
...
}
...
}
So, it will cause error:
org.springframework.beans.NullValueInNestedPathException: Invalid property 'cond' of bean class [*.OrderFormBean]: Could not instantiate property type [*.OrderFormBean$Cond] to auto-grow nested property path: java.lang.InstantiationException: *.OrderFormBean$Cond
Edit
I found that even if I post data this way:
queryData = {
"cond.startDate": startDate,
"cond.endDate": endDate,
"cond.balanceType": balanceType,
"cond.orderId": orderId
};
Spring still produces the error Could not instantiate property type [*.OrderFormBean$Cond].
If I move inner class Cond outside, the Spring MVC works.
So It's the inner class reason, that Spring can't instantiate it.
According to this
How to create a Spring Bean of a Inner class?, there is a xml configure. But I don't want to configure it, as OrderFormBean is not configured.

Your error is caused by the field result being null.
Try the following:
cond={
startDate: "2015-09-22",
endDate: "2015-09-23",
orderId: ""
};
result={
/*whatever you are going to write here*/
};
queryData = {
cond: cond,
result: result
};
reference: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/NullValueInNestedPathException.html
EDIT:
Also, your inner classes cannot be instantiated because non-static inner classes don't have a default (no-arguments) constructor.
Solutions are: either mark your inner classes as static or move them outside your "containing" class (to their own separate .java files)
For more details you can read:
http://thecodersbreakfast.net/index.php?post/2011/09/26/Inner-classes-and-the-myth-of-the-default-constructor

Related

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

Kotlin & Spring (Data): custom setters

I currently am working on a project that uses Spring Boot, written in Kotlin. It has got to be mentioned that I am still relatively new to Kotlin, coming from Java. I have a small database consisting of a single table that is used to look up files. In this database, I store the path of the file (that for this testing purpose is simply stored in the the resources of the project).
The object in question looks as follows:
#Entity
#Table(name = "NOTE_FILE")
class NoteFile {
#Id
#GeneratedValue
#Column(name = "id")
var id: Int
#Column(name = "note")
#Enumerated(EnumType.STRING)
var note: Note
#Column(name = "instrument")
var instrument: String
#Column(name = "file_name")
var fileName: String
set(fileName) {
field = fileName
try {
file = ClassPathResource(fileName).file
} catch (ignored: Exception) {
}
}
#Transient
var file: File? = null
private set
constructor(id: Int, instrument: String, note: Note, fileName: String) {
this.id = id
this.instrument = instrument
this.note = note
this.fileName = fileName
}
}
I have created the following repository for retrieving this object from the database:
#Repository
interface NoteFileRepository : CrudRepository<NoteFile, Int>
And the following service:
#Service
class NoteFileService #Autowired constructor(private val noteFileRepository: NoteFileRepository) {
fun getNoteFile(id: Int): NoteFile? {
return noteFileRepository.findById(id).orElse(null)
}
}
The problem that I have is when I call the getNoteFile function, neither the constructor nor the setter of the constructed NoteFile object are called. As a result of this, the file field stays null, whereas I expect it to contain a value. A workaround this problem is to set the fileName field with the value of that field, but this looks weird and is bound to cause problems:
val noteFile: NoteFile? = noteFileService.getNoteFile(id)
noteFile.fileName = noteFile.fileName
This way, the setter is called and the file field gets the correct value. But this is not the way to go, as mentioned above. The cause here could be that with the Spring Data framework, a default constructor is necessary. I am using the necessary Maven plugins described here to get Kotlin and JPA to work together to begin with.
Is there some way that the constructor and/or the setter does get called when the object is constructed by the Spring (Data) / JPA framework? Or maybe should I explicitly call the setter of fileName in the service that retrieves the object? Or is the best course of action here to remove the file field as a whole and simply turn it into a function that fetches the file and returns it like that?

Jackson java.util.Optional serialization does not include type ID

I got the following classes:
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class,
property = "oid"
)
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "clazz")
#JsonSubTypes({
#JsonSubTypes.Type(value = MySubEntity.class, name = "MySubEntity"),
})
public abstract class Entity {
...
}
public class MySubEntity extends Entity {
...
}
Now when I serialize that MySubEntity wrapped in an Optional then JSON does not contain the clazz attribute containing the type ID. Bug? When I serialize to List<MySubEntity> or just to MySubEntity it works fine.
Setup: jackson-databind 2.9.4, jackson-datatype-jdk8 2.9.4, serialization is done in Spring Boot application providing a RESTful web service.
EDIT: Here is the Spring REST method that returns the Optional:
#RequestMapping(method = RequestMethod.GET, value = "/{uuid}", produces = "application/json")
public Optional<MySubEntity> findByUuid(#PathVariable("uuid") String uuid) {
...
}
EDIT:
I made a SSCCE with a simple Spring REST controller and two tests. The first test is using ObjectMapper directly which is successful in deserialization although the clazz is missing. The second test calls the REST controller and fails with an error because clazz is missing:
Error while extracting response for type [class com.example.demo.MySubEntity] and content type [application/json;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Missing type id when trying to resolve subtype of [simple type, class com.example.demo.MySubEntity]: missing type id property 'clazz'; nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class com.example.demo.MySubEntity]: missing type id property 'clazz'
This, indeed, looks like a bug. There is one workaround that I can suggest for this case, is to use JsonTypeInfo.As.EXISTING_PROPERTY and add field clazz to your Entity. There only one case with this approach is that the clazz must be set in java code manually. However this is easy to overcome.
Here is the full code for suggested workaround:
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class,
property = "oid"
)
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY, //field must be present in the POJO
property = "clazz")
#JsonSubTypes({
#JsonSubTypes.Type(value = MySubEntity.class, name = "MySubEntity"),
})
public abstract class Entity {
#JsonProperty
private String uuid;
//Here we have to initialize this field manually.
//Here is the simple workaround to initialize in automatically
#JsonProperty
private String clazz = this.getClass().getSimpleName();
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
}

Passing an object to a controller with Ajax - which contains an Enum

I'm trying to pass an object Consumer to my Controller. A Consumer has a one-to-many relationship with Policy. This is what I tried:
function changeConsumer(){
var newConsumer = {
"id" : /*[[${consumer.id}]]*/,
"name" : document.getElementById('0').value,
"endpoint" : document.getElementById('1').value,
"policies" : /*[[${policies}]]*/
}
$.ajax({
type: "PUT",
url: "/rest/consumer/update",
data: JSON.stringify(newConsumer),
contentType: "application/json; charset=utf-8",
success : function(response) {
...
});
}
If I pass the Consumer like this, with an empty array of Policies - it works:
function changeConsumer(){
var newConsumer = {
"id" : /*[[${consumer.id}]]*/,
"name" : document.getElementById('0').value,
"endpoint" : document.getElementById('1').value,
"policies" : []
}
$.ajax({
type: "PUT",
url: "/rest/consumer/update",
data: JSON.stringify(newConsumer),
contentType: "application/json; charset=utf-8",
success : function(response) {
...
});
}
This is the stacktrace I'm receiving, which leads me to think there a problem with the Enum "Effect" in the Policy object:
2017-11-18 16:28:22.255 WARN 7884 --- [nio-8080-exec-6] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Can not deserialize instance of com.policyMgmt.policy.Effect out of START_OBJECT token
at [Source: java.io.PushbackInputStream#56bd85f4; line: 1, column: 93] (through reference chain: com.policyMgmt.consumer.Consumer["policies"]->java.util.ArrayList[0]->com.policyMgmt.policy.Policy["effect"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.policyMgmt.policy.Effect out of START_OBJECT token
at [Source: java.io.PushbackInputStream#56bd85f4; line: 1, column: 93] (through reference chain: com.policyMgmt.consumer.Consumer["policies"]->java.util.ArrayList[0]->com.policyMgmt.policy.Policy["effect"])
2017-11-18 16:28:22.255 WARN 7884 --- [nio-8080-exec-6] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Can not deserialize instance of com.policyMgmt.policy.Effect out of START_OBJECT token
at [Source: java.io.PushbackInputStream#56bd85f4; line: 1, column: 93] (through reference chain: com.policyMgmt.consumer.Consumer["policies"]->java.util.ArrayList[0]->com.policyMgmt.policy.Policy["effect"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.policyMgmt.policy.Effekt out of START_OBJECT token
at [Source: java.io.PushbackInputStream#56bd85f4; line: 1, column: 93] (through reference chain: com.policyMgmt.consumer.Consumer["policies"]->java.util.ArrayList[0]->com.policyMgmt.policy.Policy["effect"])
The Policy object which looks like this:
#Entity
public class Policy {
#Id
#GeneratedValue
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#Column (name = "consumer_id")
private Integer consumer;
#Enumerated(EnumType.STRING)
#Column(name="effect")
private Effect effect;
public Policy() {}
....
}
Maybe it helps, this is what a Consumer looks like:
#Entity
public class Consumer {
#Id
#GeneratedValue
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "endpoint")
private String endpoint;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name ="consumer_id")
private List<Policy> policies;
public Consumer() {
}
}
Rest API
#Controller
#RequestMapping(value = "/rest/consumer")
public class ConsumerController {
...
#PutMapping(value = "/update")
#ResponseBody
public List<Consumer> updateConsumer(#RequestBody Consumer consumer) {}
...
}
Does anyone have experience passing enums within Ajax to a Controller?
I can't say for certain why #RequestBody is having difficulty deserializing JSON like { "$type": "Effekt", "$name": "deny" } into an instance of the Effekt enum, but I think you should try writing a custom JsonDeserializer for your enum.
Here's another Stackoverflow question that is similar, but not identical to the issue you're facing. Take a look at the second answer -- the one that includes a code example of a JsonDeserializer.
With a bit of trail-and-error, you can probably come up with something that successfully processes JSON like you're already getting into instances of Effekt.
The other option would be to change the JSON representation of Effekt in that gets sent to the server. Perhaps if it looked like this...
"effekt": "deny"
it would work directly with #RequestBody. You could try hard-coding it in the JavaScript to test.
If you want to go this route, you will probably need to implement a custom JsonSerializer -- the reverse of the earlier option.

Spring Boot Controller endpoint and ModelAttribute deep access

I would like to know how to access a deep collection class attribute in a GET request. My endpoint maps my query strings through #ModelAttribute annotation:
Given that:
public class MyEntity
{
Set<Item> items;
Integer status;
// getters setters
}
public class Item
{
String name;
// getters setters
}
And my GET request: localhost/entities/?status=0&items[0].name=Garry
Produces bellow behavior?
#RequestMapping(path = "/entities", method = RequestMethod.GET)
public List<MyEntity> findBy(#ModelAttribute MyEntity entity) {
// entity.getItems() is empty and an error is thrown: "Property referenced in indexed property path 'items[0]' is neither an array nor a List nor a Map."
}
Should my "items" be an array, List or Map? If so, thereĀ“s alternatives to keep using as Set?
Looks like there is some problem with the Set<Item>.
If you want to use Set for the items collection you have to initialize it and add some items:
e.g. like this:
public class MyEntity {
private Integer status;
private Set<Item> items;
public MyEntity() {
this.status = 0;
this.items = new HashSet<>();
this.items.add(new Item());
this.items.add(new Item());
}
//getters setters
}
but then you will be able to set only the values of this 2 items:
This will work: http://localhost:8081/map?status=1&items[0].name=asd&items[1].name=aaa
This will not work: http://localhost:8081/map?status=1&items[0].name=asd&items[1].name=aaa&items[2].name=aaa
it will say: Invalid property 'items[2]' of bean class MyEntity.
However if you switch to List:
public class MyEntity {
private Integer status;
private List<Item> items;
}
both urls map without the need to initialize anything and for various number of items.
note that I didn't use #ModelAttribute, just set the class as paramter
#GetMapping("map")//GetMapping is just a shortcut for RequestMapping
public MyEntity map(MyEntity myEntity) {
return myEntity;
}
Offtopic
Mapping a complex object in Get request sounds like a code smell to me.
Usually Get methods are used to get/read data and the url parameters are used to specify the values that should be used to filter the data that has to be read.
if you want to insert or update some data use POST, PATCH or PUT and put the complex object that you want to insert/update in the request body as JSON(you can map that in the Spring Controller with #RequestBody).

Resources