Spring Data JPA and Projection getting ConverterNotFoundException for TupleConverter - java

Is there a way to write and register a TupleConverter converter in Spring Data? I'm getting this exception when I have an #Query annotation in the Repository interface and asking for Projection.
The Interface:
public interface ProjectRepository extends JpaRepository<Project, Integer> {
#Query("select p.projectId, p.projectName, p.techstack from Project p")
public List<ProjectItem> findAllForTest();
}
The DTO:
public class ProjectItem {
private final Integer projectId;
private final String projectName;
private final String techstack;
#JsonCreator
public ProjectItem(
#JsonProperty("projectId") Integer projectId,
#JsonProperty("projectName") String projectName,
#JsonProperty("techstack") String techstack
) {
this.projectId = projectId;
this.projectName = projectName;
this.techstack = techstack;
}
public Integer getProjectId() {
return projectId;
}
public String getProjectName() {
return projectName;
}
public String getTechstack() {
return techstack;
}
}
The exception
No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [project.item.ProjectItem]] with root cause

Use a JPQL Constructor Expression:
#Query("select new com.company.path.to.ProjectItem(p.projectId, p.projectName, p.techstack) from Project p")

You're close. If you just want a DTO with a few of the items from the original item, just use the interface projection technique with methods having the same signatures as the Project class method items you want:
public interface ProjectTestSummary {
public Integer getProjectId();
public String getProjectName();
public String getTechstack();
}
And in your DAO:
public interface ProjectRepository extends JpaRepository<Project, Integer> {
public List<ProjectTestSummary> findAllProjectedBy();
}

Related

Can you send a Query object as parameter to a SpringData Repository method?

I was reading this link about Spring Data JPA and it got me curious: Instead of using #Query annotation, can you create a query and then use it as a param to the method?
More like this:
#Repository
public interface MyRepository extends CrudRepository<MyClass, Integer>
{
void doSomething(Query query);
}
(BTW, I know I could implement a fragment repository and solve my problem, but I'm curious)
you could not create an implementation class, instead of that you can write interface methods like this:
#Entity
public class Part {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long id;
#Column(unique = true)
private String partId;
public Part() {
}
public Part(String partId) {
this.partId = partId;
}
public String getPartId() {
return partId;
}
public void setPartId(String partId) {
this.partId = partId;
}
public Set<Card> getCards() {
return cards;
}
}
public interface PartRepository extends CrudRepository<Part, Long> {
public Optional<Part> findByPartId(String partId);
public List<Part> findAllByPartId(String partId);
}
Spring automatically convert these lines to SQL in background, you should don't care about that.
You can find some details here: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods

convert a DTO to Entity with using mapper class

I have a Entity class something like this:
#Entity
public class Website {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String url;
public Website() {
//Constructor
//getters and setters
}
here is the DTO class:
public class WebsiteDto {
private Integer id;
private String name;
private String url;
public WebsiteVo() {
//Constructor
//getters and setters
}
I have the WebsiteMapper something like this:
#Component
public class WebsiteMapper {
public List<WebsiteDto> getWebsiteList() {
return repository.findAll().stream().map(w -> {
WebsiteDto dto = new WebsiteVo(w.getId(), w.getName(), w.getUrl());
return dto;
}).collect(Collectors.toList());
I also have Repository Interface:
public interface WebsiteRepository extends JpaRepository<Website, Integer> {
}
I want now to convert DTO to entity using my class WebsiteMapper. Because I did the conversion in this class. How I can do it?
How about using BeanUtils provided by spring org.springframework.beans.BeanUtils, something like this
public List<WebsiteDto> getWebsiteList() {
return repository.findAll().stream().map(w -> {
WebsiteDto dto = new WebsiteVo();
BeanUtils.copyProperties(w, dto); // copys all variables with same name and type
return dto;
})
.collect(Collectors.toList());
}
Hi I guess you wish to converting your entity to DTO. It's quite simple. Create static methods in your DTO class or any util class. The return type should be your DTO type.
e.g.
public class WebsiteDto {
private Integer id;
private String name;
private String url;
public static WebsiteDto export(Website website) {
// Return a new instance of your website DTO
return new WebsiteDto(
website.getId(),
website.getName(),
website.getUrl()
);
}
public static List<WebsiteDto> export(List<Website> websites) {
// Return a new instance of your website DTO list
return websites.stream().map(website -> {
return new WebsiteDto(
website.getName(),
website.getUrl()
}).collect(Collectors.toList());
}
}
NOTE You can also convert your DTO to entity using similar method.

SpringData MongoDB Query on nested object and list object

I want to find a Type document by the code of job and by the list of code of category, i tried the below query but it didn't work
#Document
public class Type {
#Id
private String id;
#DBRef
private Job job;
#DBRef
private List<Category> categories;
}
public class Job {
#Id
private String id;
private String code;
}
public class Category {
#Id
private String id;
private String code;
}
public interface TypeRepository extends MongoRepository<Type, String> {
#Query("{ 'job.code': ?0, 'category.code': { $in: ?1 }}")
Type findByJobAndCategoriesCode(String codeJob, List<String> codeCategories);
}
try using this one
public interface TypeRepository extends MongoRepository<Type, String> {
Type findOneByJobCodeAndCategoriesCodeIn(String codeJob, List<String> codeCategories);
}

hibernate - Persisting a composition interface of strategy pattern

I have the following class structure:
public abstract class Creature{
private String name;
//strategy pattern composition
private SkillInterface skill;
}
public interface SkillInterface {
void attack();
}
public class NoSkill implements SkillInterface {
#Override
public void attack() {
//statements
}
}
My goal is to persist Creature objects at one table in database. Subclasses of SkillInterface are without any fields. As they determine the behaviour, I want to convert selected SkillInterface class name to a String, as I only need to persist the classname of the current skill strategy of creature, with a String like skill.getClass().getSimpleName(). I tried to implement it with #Converter annotation, using AttributeConverter class to convert SkillInterface to String and save, but always had mapping exceptions. I want to be able to save it as String and retrieve as SkillInterface object.
But how can I implement it with Hibernate? Or do I have a design mistake?
Ok looks like I have found a basic solution that can be used to persist Strategy Pattern interfaces implementations. I used a #Converter annotation and a AttributeConverter class to convert strategy class names to column while saving to database and cast the retrieved String back to strategy class as following:
#Entity
public class Creature {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Convert(converter = SkillConverter.class)
private SkillInterface skill;
}
public class SkillConverter implements AttributeConverter<SkillInterface,String> {
#Override
public String convertToDatabaseColumn(SkillInterface skill) {
return skill.getClass().getSimpleName().toLowerCase();
}
#Override
public SkillInterface convertToEntityAttribute(String dbData) {
//works as a factory
if (dbData.equals("noskill")) {
return new NoSkill();
} else if (dbData.equals("axe")) {
return new Axe();
}
return null;
}
}
public interface SkillInterface {
public String getSkill();
void attack();
}
public class NoSkill implements SkillInterface{
public String getSkill() {
return getClass().getSimpleName();
}
#Override
public void attack() {
//strategy statements
}
}
You can use a proxy field to this for you like below:
abstract class Creature {
#Column
private String name;
// strategy pattern composition
private SkillInterface skill;
#Column
private String skillName;
public String getSkillName() {
return skill.getClass().getSimpleName();
}
public void setSkillName(String skillName) {
//ignore
}
}

Spring data repository projection findByEntity

That is the JPA entity MyEntity.
Entity
class MyEntity{
private Integer id;
private Date date;
private Double montant;
#ManyToOne(fetch = FetchType.LAZY)
private User creator;
}
class User {
private Integer id;
private String name;
private String image;
private Integer age;
private String anotherField;
}
I would like to retrieve a list of MyEntity with some attributes of its creator (just the id, the name and its image).
So I created a Projection interface.
interface Projection{
public Integer getId();
public Date getDate();
public Double getMontant();
public User getCreator();
interface User {
public Integer getId();
public String getName();
public String getImage();
}
}
here JPA repository implementation :
public interface CommandeRepository extends JpaRepository<EbCommande, Integer> {
<T> Collection<T> findById(Integer id, Class<T> type);
<T> Collection<T> findByCreator(User client, Class<T> type);
}
The first query works as I hope.
On the other hand with the second, when I loop on the list of MyEntity returned, each access to the User attribute triggers a request to the database fetching all the attributes of the User.
I do not understand how JPA projections work anymore.
Help please!

Categories