On the client and server side, I have:
@Data
public class Car {
private Long id;
}
@Data
public class GroupMember {
private Long id;
private Group group; // has only single field, id
private Car car;
private Car groupCar;
}
Note: On the server side these are entities. (I'm using Spring Boot Data Rest to save.)
The client creates a car and then tries to create a group member for that car as follows:
Car car = new Car();
car = client.save(car); // this works fine
GroupMember gm = new GroupMember();
gm.setGroup(new Group(1L));
gm.setGroupCar(car);
gm.setCar(car);
client.save(gm); // this fails
client
is a instance of CarClient
:
@FeignClient(name = "car")
public interface CarClient {
@PostMapping("/api/cars")
public Car save(Car car);
@PostMapping("/api/groupMembers")
public GroupMember save(GroupMember groupMember);
}
client.save(GroupMember)
fails with
[409] during [POST] to [http://car/api/groupMembers] [CarClient#save(GroupMember)]: [{"cause":{"cause":{"cause":null,"message":"Cannot insert the value NULL into column 'groupCarId', table 'cars.dbo.GroupMember'; column does not allow nulls. INSERT fails."}
After turning on trace on the server side, I can see that groupCar
(and car
) is null:
2022-06-30 16:25:47.333 TRACE 16840 --- [http-nio-9009-exec-1] .w.s.m.m.a.ServletInvocableHandlerMethod : Arguments: [org.springframework.data.rest.webmvc.RootResourceInformation@5ca2b05f, Resource { content: GroupMember(id=10640, group=Group(id=1), car=null, groupCar=null), links: [] }, org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler@47f7b8a2, */*]
Also, the GroupMember
id was set to the car id, 10640!!
Why did Spring Data Rest incorrectly bind the properties of GroupMember?
Update
Because Spring Data Rest appears to be broken in its binding, I created a controller as follows:
@RestController
@RequiredArgsConstructor
@RequestMapping("/groupMember")
@Slf4j
public class GroupMemberController {
private final GroupMemberRepository repo;
@PostMapping
public GroupMember save(@RequestBody GroupMember groupMember) {
log.info("GROUP MEMBER is {}", groupMember);
return repo.save(groupMember);
}
}
and then revised my FeignClient to point to that for saving a GroupMember:
@FeignClient(name = "car")
public interface CarClient {
@PostMapping("/api/cars")
public Car save(Car car);
@PostMapping("/groupMember")
public GroupMember save(GroupMember groupMember);
}
This works as expected. I can see from the log that:
2022-07-01 13:02:04.637 INFO 764 --- [nio-9009-exec-1] com.example.GroupMemberController : GROUP MEMBER is GroupMember(id=null, group=Group(id=1), car=car(id=10653), groupCar=Security(id=10653))
Am I doing something wrong with Spring Data Rest or can it not handle POSTing this type of data?
Spring Data Rest tries to deserialize the Car
fields in GroupMember
using URIs. There are no URIs provided by the client though. Instead, groupCar
and car
are nested objects in the JSON.
Adding @RestResource(exported = false)
on the two car fields within GroupMember
(on the server side) resolves the issue by telling Spring Data Rest to expect nested objects instead of URIs.
Alternatively, an EntityModel<GroupMember>
could be passed from the client.