I completed this tutorial: https://guides.micronaut.io/latest/micronaut-data-jdbc-repository-gradle-java.html
I then added code to the completed, working tutorial to try to do a custom SQL query for a use case involving reading the data with a GROUP BY
clause. My code then looked like this: https://github.com/mattwelke/micronaut-jdbc-guide-complete/tree/rec-engine (note the branch name rec-engine
)
When I run the script ./scripts/start_mysql.sh
and then run curl http://localhost:8080/views/popular
in another terminal while the application runs, I get the following error returned (and in the logs):
{
"message":"Internal Server Error",
"_embedded":{
"errors":[
{
"message":"Internal Server Error: Cannot convert type [class java.lang.String] with value [def456] to target type: class example.micronaut.dtos.PopularProductResult. Consider defining a TypeConverter bean to handle this case."
}
]
},
"_links":{
"self":{
"href":"/views/popular",
"templated":false
}
}
}
I was under the impression that Micronaut Data would take care of this mapping. But I also understand if Micronaut Data isn't capable of doing this, and I'm required to do it myself. however, when I try to do it myself, I'm confused about how to follow the instructions to create some code that would map the query results to my class. The error describes mapping a String
to PopularProductResult
(my class). I don't want to map a string to that class. I want to map a string and a number to that class, since it represents an aggregation result.
How do I do this?
The GitHub repo shows the complete code, but for easy reference here, here's my domain model, repository (including the query in the @Query
) and my custom class for my query results:
package example.micronaut.domain;
import io.micronaut.data.annotation.GeneratedValue;
import io.micronaut.data.annotation.Id;
import io.micronaut.data.annotation.MappedEntity;
import javax.validation.constraints.NotNull;
@MappedEntity
public class View {
@Id
@GeneratedValue(GeneratedValue.Type.AUTO)
private Long id;
@NotNull
private String productId;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
@Override
public String toString() {
return "View{" +
"id=" + id +
", productId='" + productId + '\'' +
'}';
}
}
package example.micronaut;
import example.micronaut.domain.View;
import example.micronaut.dtos.PopularProductResult;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.data.annotation.Id;
import io.micronaut.data.annotation.Query;
import io.micronaut.data.exceptions.DataAccessException;
import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.PageableRepository;
import java.util.List;
import javax.transaction.Transactional;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@JdbcRepository(dialect = Dialect.MYSQL)
public interface ViewRepository extends PageableRepository<View, Long> {
View save(@NonNull @NotBlank String productId);
@Transactional
default View saveWithException(@NonNull @NotBlank String productId) {
save(productId);
throw new DataAccessException("test exception");
}
long update(@NonNull @NotNull @Id Long id, @NonNull @NotBlank String productId);
@Query("SELECT product_id AS productId, COUNT(*) AS count FROM `view` GROUP BY productId ORDER BY count DESC")
List<PopularProductResult> popularViews();
}
package example.micronaut.dtos;
public record PopularProductResult(String productId, Long count) {
}