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!
Related
I am trying to display a list of books by category.
Category.java
public enum Category {
NEW,
CLASSIC,
STANDARD;
}
BookDto.java
public class BookDto {
private Long id;
private String title;
private String author;
private String publisher;
private Category category;
private int totalCount;
}
RentController.java
#GetMapping("/booksCategory")
public List<BookDto> getBookByCategory(#RequestParam Category category) {
return rentService.getBookByCategory(category);
}
RentService.java
public List<BookDto> getBookByCategory(Category category) {
List<Book> book = bookRepository.findAllBookByCategory(category);
return mapBookListToBooDtoList(book);
}
BookRepository.java
public interface BookRepository extends JpaRepository<Book, Long> {
#Query("select count(b) from Book b")
List<Book> findAllBookByCategory(Category category);
}
From code above I am getting following error:
Required Category parameter 'category' is not present
If you check your query, you see that the parameter category is not used.
Also the query returns count(b) instead a books as required by the return type.
For simple queries you can omit the explicit query:
public interface BookRepository extends JpaRepository<Book, Long> {
List<Book> findByCategory(Category category);
}
Also make sure you actually pass a category as a query parameter to the HTTP request when calling your service, e.g.: GET https://my-service-url/booksCategory?category=CLASSIC
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);
}
I have MyEntity class:
#Entity
#Table("entities)
public class MyEntity {
#ID
private String name;
#Column(name="age")
private int age;
#Column(name="weight")
private int weight;
...getters and setters..
}
In #RestController there are 2 #GetMapping methods.
The first:
#GetMapping
public MyEntity get(){
...
return myEntity;
}
The second:
#GetMapping("url")
public List<MyEntity> getAll(){
...
return entities;
}
It's needed to provide:
1. #GetMapping returns entity as it's described in MyEntity class.
2. #GetMapping("url") returns entities like one of its fields is with #JsonIgnore.
UPDATE:
When I return myEntity, client will get, for example:
{
"name":"Alex",
"age":30,
"weight":70
}
I want in the same time using the same ENTITY have an opportunity depending on the URL send to client:
1.
{
"name":"Alex",
"age":30,
"weight":70
}
2.
{
"name":"Alex",
"age":30
}
You could also use JsonView Annotation which makes it a bit cleaner.
Define views
public class View {
static class Public { }
static class ExtendedPublic extends Public { }
static class Private extends ExtendedPublic { }
}
Entity
#Entity
#Table("entities)
public class MyEntity {
#ID
private String name;
#Column(name="age")
private int age;
#JsonView(View.Private.class)
#Column(name="weight")
private int weight;
...getters and setters..
}
And in your Rest Controller
#JsonView(View.Private.class)
#GetMapping
public MyEntity get(){
...
return myEntity;
}
#JsonView(View.Public.class)
#GetMapping("url")
public List<MyEntity> getAll(){
...
return entities;
}
Already explained here:
https://stackoverflow.com/a/49207551/3005093
You could create two DTO classes, convert your entity to the appropriate DTO class and return it.
public class MyEntity {
private String name;
private int age;
private int weight;
public PersonDetailedDTO toPersonDetailedDTO() {
PersonDetailedDTO person = PersonDetailedDTO();
//...
return person;
}
public PersonDTO toPersonDTO() {
PersonDTO person = PersonDTO();
//...
return person;
}
}
public class PersonDetailedDTO {
private String name;
private int age;
private int weight;
}
public class PersonDTO {
private String name;
private int age;
}
#GetMapping
public PersonDTO get() {
//...
return personService.getPerson().toPersonDTO();
}
#GetMapping("/my_url")
public PersonDetailedDTO get() {
//...
return personService.getPerson().toPersonDetailedDTO();
}
EDIT:
Instead of returning an Entity object, you could serialize it as a Map, where the map keys represent the attribute names. So you can add the values to your map based on the include parameter.
#ResponseBody
public Map<String, Object> getUser(#PathVariable("name") String name, String include) {
User user = service.loadUser(name);
// check the `include` parameter and create a map containing only the required attributes
Map<String, Object> userMap = service.convertUserToMap(user, include);
return userMap;
}
As an example, if you have a Map like this and want
All Details
userMap.put("name", user.getName());
userMap.put("age", user.getAge());
userMap.put("weight", user.getWeight());
Now if You do not want to display weight then you can put only two
parameters
userMap.put("name", user.getName());
userMap.put("age", user.getAge());
Useful Reference 1 2 3
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();
}
I have two classes Employee and Address.
public class Employee implements java.io.Serializable {
private long id;
private String name;
private Address address;
private UserType userType;
private Date bdate;
public Employee() {
}
// getters and setters
}
public class Address implements java.io.Serializable {
private long id;
private Set<String> lines;
public Address() {
}
// getters and setters
}
I am using moxy to convert hibernate entities to json. I have enabled it in pom.xml. Also added #XmlRootElement on the top of entity. Below is my service code.
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("{/Id}")
public Employee test(#PathParam("Id") long Id)
{
return EmployeeDao.getEmployee(Id);
}
Right now I am getting 500 for this service. Any help will be appreciated.