Home > Mobile >  Spring REST and patching a list is not updating first element
Spring REST and patching a list is not updating first element

Time:05-19

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" : []
}
  • Related