I have a problem to replace elements in a List of an object by Springs default patch-Method. I use spring-boot 2.6.7.
I have 2 related entities, a rental object which can belongs to multiple portals:
Rental:
@Data
@Entity(name = "rental")
public class Rental implements Serializable {
private static final long serialVersionUID = -6281190735417544253L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany
@JoinTable(
name = "rental_portal", joinColumns = @JoinColumn(name = "rentalId"),
inverseJoinColumns = @JoinColumn(name = "portalId")
)
@RestResource(exported = false)
private List<Portal> portals;
Portal:
@Data
@Entity
@Table(name = "portal")
public class Portal implements Serializable {
private static final long serialVersionUID = 8534905447925259846L;
@Id
@Column(updatable = false)
private long id;
@Column(updatable = false)
private String name;
}
The rental is accessible via the repository interface:
@RepositoryRestResource(path = "rental")
public interface RentalRepository extends PagingAndSortingRepository<Rental, Long> {
}
For my use case I want to list all portals in my rental object without calling the portal repository.
GET /rental/1
{
"id" : 1,
"name" : "Test rental",
"portals" : [ {
"id" : 2,
"name" : "PORTAL_2"
} ],
"_links" : {
"self" : {
"href" : "http://localhost:8080/api/rental/1"
},
"rental" : {
"href" : "http://localhost:8080/api/rental/1"
}
}
}
If I call the patch method and
- add one or more portals to the rental object works fine
- send an empty list to delete all portals from the rental object works also as expected
- if I want to replace an existing portal with another one, this has no effect. I receive the html status code 204 but the rentals portal value has not been changed
PATCH /rental/1
{
"portals": [
{
"id": 1
}
]
}
CodePudding user response:
As I think, problem here, that it cannot understand what it should do with your portals
. I would recommend you if you want replace a single portal
, make another endpoint for it (ex. PATCH /rental/{rentalId}/portal/{portalId}) and inside logic handling of this endpoint, you should get the rental
get portals
of itself, search for portalId
you want replace, delete it, and insert new portal from request body.
CodePudding user response:
I found a suitable solution for my use case:
Instead of using a list I created a map with the portal id as the maps key. The consumer of this api just want to know to which portals this rental belongs, so I return not a map with the whole object but the keys. For this behaviour I added a custom setter and getter.
@Data
@Entity
public class Rental implements Serializable {
private static final long serialVersionUID = -3647935687142095639L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "rental_portal", joinColumns = @JoinColumn(name = "rentalId"),
inverseJoinColumns = @JoinColumn(name = "portalId")
)
@RestResource(exported = false)
@JsonIgnore
@MapKey(name = "id")
private Map<Long, Portal> portals;
@JsonGetter
public Set<Long> getPortals() {
final Set<Long> portalKeys = new HashSet<>(this.portals.keySet());
return portalKeys;
}
@JsonSetter
public void setPortals(final Set<Long> portals) {
if (CollectionUtils.isNotEmpty(portals)) {
this.portals.clear();
for (final Long portalKey : portals) {
final Portal portal = new Portal();
portal.setId(portalKey);
this.portals.put(portalKey, portal);
}
} else if (this.portals != null) {
this.portals.clear();
}
}
Now the consumer can patch any values. For example:
PATCH /rental/1
{
"portals" : [ 1]
}
{
"portals" : [ 2]
}
{
"portals" : [ 1, 2]
}
{
"portals" : []
}