My controller endpoint has a method which cannot be found:
@GetMapping("")
public ResponseEntity<PagedModel<PostModel>> findAll(
@PageableDefault(sort = { "orderedOn" }, direction = Sort.Direction.ASC) Pageable pageable, Sort sort,
PagedResourcesAssembler<PostModel> pagedResourcesAssembler, UriComponentsBuilder builder) {
sort = RESTUtils.stripColumnsFromSorting(sort, nonSortableColumns);
postService.addSortToPageable(pageable, sort);
Page<Post> posts = postService.findAll(pageable);
PagedModel<PostModel> pagedPostModels = pagedResourcesAssembler.toModel(posts, postModelAssembler);
UriComponentsBuilder uriComponentsBuilder = builder.path(RESTUtils.SLASH);
modelService.addPageableToUri(uriComponentsBuilder, pageable);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setLocation(uriComponentsBuilder.buildAndExpand().toUri());
return ResponseEntity.status(HttpStatus.OK).headers(responseHeaders).body(pagedPostModels);
}
The full error message is: The method toModel(Page<PostModel>, Link) in the type PagedResourcesAssembler<PostModel> is not applicable for the arguments (Page<Post>, PostModelAssembler)
The IDE contextual documentation shows the methods:
public <R extends RepresentationModel<?>> PagedModel<R> toModel(Page<T> page,
RepresentationModelAssembler<T, R> assembler) {
return createModel(page, assembler, Optional.empty());
}
public PagedModel<EntityModel<T>> toModel(Page<T> page, Link selfLink) {
return toModel(page, EntityModel::of, selfLink);
}
It feels like it cannot find the first method and ends up defaulting on the second one.
The assembler class:
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
import org.springframework.beans.BeanUtils;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.server.mvc.RepresentationModelAssemblerSupport;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import com.thalasoft.post.PostController;
import com.thalasoft.post.entity.Post;
import com.thalasoft.post.model.PostModel;
import com.thalasoft.post.service.ModelService;
@Component
public class PostModelAssembler extends RepresentationModelAssemblerSupport<Post, PostModel> {
private final ModelService modelService;
public PostModelAssembler(ModelService postService) {
super(PostController.class, PostModel.class);
this.modelService = postService;
}
@NonNull
@Override
public PostModel toModel(@NonNull Post entity) {
PostModel model = createModelWithId(entity.getId(), entity);
model.add(linkTo(methodOn(PostController.class)
.findById(entity.getId()))
.withSelfRel());
BeanUtils.copyProperties(modelService.fromPost(entity), model);
return model;
}
@NonNull
@Override
public CollectionModel<PostModel> toCollectionModel(@NonNull Iterable<? extends Post> entities) {
CollectionModel<PostModel> postModels = super.toCollectionModel(entities);
postModels.add(linkTo(methodOn(PostController.class).findAll()).withSelfRel());
return postModels;
}
}
The Post
entity:
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Entity
@Table(name = "post")
public class Post extends AbstractEntity {
@Column(name = "user_id", nullable = true)
private Long userId;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String body;
@NaturalId
@Column(nullable = false, unique = true)
private String isbn;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@MappedSuperclass
public abstract class AbstractEntity implements Serializable {
protected static final long serialVersionUID = 1L;
@Id
// Compatible with H2, MySQL, PostgreSQL, Oracle
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "id_generator")
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Version
@Column(nullable = false)
private Integer version;
@CreationTimestamp
private LocalDateTime createdOn;
@UpdateTimestamp
private LocalDateTime updatedOn;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof AbstractEntity)) {
return false;
}
AbstractEntity other = (AbstractEntity) o;
return id != null && id.equals(other.getId());
}
// The equals() and hashCode() methods must behave consistently across all state
// transitions of the entity
// The hash code must remain the same accross all states of the persistence life
// cycle
// Thus the object address cannot be the hash code as a new object is created
// after the entity is persisted
// And the entity id cannot be the hash code as an entity not yet persisted has
// no id and gets one after being persisted
// As the hash code should always return the same value, the class name is used
// as the hash code
// See
// https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
@Override
public int hashCode() {
return getClass().hashCode();
}
}
The PostModel
model:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@JsonRootName(value = "post")
@Relation(collectionRelation = "posts")
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class PostModel extends RepresentationModel<PostModel> {
@Id
private Long id;
private Long userId;
@NotEmpty
private String title;
@NotEmpty
private String body;
@NotEmpty
private String isbn;
@Version
private Integer version;
}
I’m on SpringBoot 3.3.0
and Java 21