I have a list of persons in DB everyone having a CV field which is a MultiPart File in Spring. I'm trying to get all persons from db, but to ignore the CV field because I don't want to be downloaded when I use getAllPersons function. I saw a possibility with JsonIgnore but I want to ignore that field just in getAllPersons, not in other functions like getPersonById etc. How can I do it?
For that purpose you can use HQL.
i.e
interface Repo extends C... {
#Query(select h.name, h.phone, ... from Person h)
List<Person> getAllPerson();
}
Suppose that you use Sping and Spring-data you can use a projection in order to avoid maintaining custom queries. Consider the following example:
Entity class Book
#Data
#Entity
#Table(name = "book")
public class Book {
#Id
#GeneratedValue(generator = "book_sequence", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "book_sequence", sequenceName = "book_sequence", allocationSize = 1)
private Long id;
#NaturalId
private String name;
private String author;
private String publisher;
private String plot;
#ManyToMany(mappedBy = "books")
#ToString.Exclude
#EqualsAndHashCode.Exclude
private Set<BookFilter> filters = new HashSet<>();
}
Projection interface:
public interface BookNameAuthorOnly {
String getName();
String getAuthor();
}
Repository method:
#Repository
public interface BookRepository extends JpaRepository<Book, Long> {
List<BookNameAuthorOnly> findBy();
}
When the latter is invoked, the dynamic query generated by Spring, will select only the fields that you have specified in the related interface object. For more on this you can check the following documentation:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
Don't know if this fits your use case scenario but this is also another way of achieving what you need to achieve.
You can also use DTO as shown in below example:
Person entity:
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Lob;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Getter
#Setter
#NoArgsConstructor
#Entity
public class Person {
#Id
private long id;
private String name;
private String address;
#Lob
private Object cvFields;
}
PersonDTO:
package com.example.dto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Getter
#Setter
#NoArgsConstructor
public class PersonDTO {
private long id;
private String name;
private String address;
public PersonDTO(Person person) {
this.id = person.getId();
this.name = person.getName();
this.address = person.getAddress();
}
}
PersonRepository:
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface PersonRepository extends JpaRepository<Person, Long> {
#Query("SELECT new com.example.dto.PersonDTO(p) FROM Person p")
List<PersonDTO> getAll();
}
Related
I created POST API in Spring Boot, but 500 error occurs.
"timestamp": "2023-01-27T16:27:32.609+00:00",
"status": 500,
"error": "Internal Server Error",
"trace": "org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["PRIMARY KEY ON PUBLIC.CATEGORY(CATEGORY_ID) ( /* key:1 */ 1, U&'\\c1fc\\d551\\bab0', 1)"; SQL statement:\ninsert into category (category_id, category_name, site_user_id) values (default, ?, ?)
I want to put data in the 'category' table with 'categoryId', 'category_name', and 'site_user_id' as columns through POST API. It seems to be caused by putting 'siteUser' entity instead of 'site_user_id', but I don't know how to modify the code.
Below is the code I wrote.
Category.java
package com.kakaotrack.choco.linkupapi.category;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.kakaotrack.choco.linkupapi.linkcollection.LinkCollection;
import com.kakaotrack.choco.linkupapi.user.SiteUser;
import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
#Entity
#Data
#Table(name = "category")
#NoArgsConstructor
public class Category {
public Category(String category_name, SiteUser siteUser){
this.category_name = category_name;
this.siteUser = siteUser;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int categoryId;
private String category_name;
#OneToMany(mappedBy = "category", cascade = CascadeType.REMOVE)
#JsonIgnoreProperties({"category"})
private List<LinkCollection> link_collection_list;
#ManyToOne
private SiteUser siteUser;
}
SiteUser.java
package com.kakaotrack.choco.linkupapi.user;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Getter
#Setter
#Entity
#Table(name = "users")
#NoArgsConstructor
public class SiteUser {
public SiteUser(String username, String email){
this.username=username;
this.email=email;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(unique = true)
private String username;
private String password;
#Column(unique = true)
private String email;
}
CategoryService.java
package com.kakaotrack.choco.linkupapi.category;
import com.kakaotrack.choco.linkupapi.linkcollection.LinkCollection;
import com.kakaotrack.choco.linkupapi.user.SiteUser;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
#RequiredArgsConstructor
#Service
public class CategoryService {
private final CategoryRepository categoryRepository;
public List<Category> getAll() {return categoryRepository.findAll();}
public List<Category> getBySiteUser(int id){
return categoryRepository.findBySiteUserId(id);
}
public Category createCategory(String categoryName, SiteUser siteUser){
Category category = new Category(categoryName, siteUser);
return categoryRepository.save(category);
}
public void deleteByCategoryId(int category_id){categoryRepository.deleteById(category_id);}
}
CategoryController.java
package com.kakaotrack.choco.linkupapi.category;
import com.kakaotrack.choco.linkupapi.linkcollection.LinkCollection;
import com.kakaotrack.choco.linkupapi.linkcollection.LinkCollectionRepository;
import com.kakaotrack.choco.linkupapi.user.SiteUser;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
#RequiredArgsConstructor
public class CategoryController {
private final CategoryService categoryService;
#GetMapping(value = "/categories")
public List<Category> getAll() {return categoryService.getAll();}
#GetMapping(value = "/categories/{id}")
public List<Category> getBySiteUser(#PathVariable int id) {return categoryService.getBySiteUser(id);}
#PostMapping(value = "/categories")
public Category createCategory(String categoryName, SiteUser siteUser){
Category category = categoryService.createCategory(categoryName, siteUser);
return category;
}
#DeleteMapping(value = "/categories/{category_id}")
public void deleteCategory(#PathVariable int category_id){ categoryService.deleteByCategoryId(category_id);}
}
DELETE and GET APIs work well.
Try to update SiteUser fields as shown below:
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "site_user_id", referencedColumnName = "id")
private SiteUser siteUser;
I think the issue is with the category_name. It is not following the standard naming convention. Underscore is used to separate property names in JPA custom methods.
#Column(name = "category_name")
private String categoryName;
NB: Also you have to implement the changes mentioned by Murat. Use optional = false if it is Not Null in DB
I have a simple question about JpaRepository.
First, this is my Entity class.
package com.surveypedia.domain.pointhistory;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
#NoArgsConstructor
#Getter
#Entity
#Table(name = "pointhistory")
public class PointHistory {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer ph_code;
#Column(nullable = false)
private String email;
#Column(nullable = false, name = "s_code")
private Integer s_code;
#Column(nullable = false)
private Integer pointchange;
#Column(nullable = false)
#Enumerated(EnumType.STRING)
private PointHistoryType ph_type;
public PointHistory(String email, Integer s_code, Integer pointchange, PointHistoryType ph_type) {
this.email = email;
this.s_code = s_code;
this.pointchange = pointchange;
this.ph_type = ph_type;
}
}
And below is my repository interface to do CRUD operations.
package com.surveypedia.domain.pointhistory;
import com.surveypedia.tools.SQL;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface PointHistoryRepository extends JpaRepository<PointHistory, Integer> {
List<PointHistory> findByEmail(String email);
PointHistory findByS_codeAndEmailAndPh_type(Integer s_code, String email, PointHistoryType ph_type);
}
After starting my spring-boot project, I get this error :
java.lang.IllegalArgumentException: Failed to create query for method public abstract com.surveypedia.domain.pointhistory.PointHistory com.surveypedia.domain.pointhistory.PointHistoryRepository.findByS_codeAndEmailAndPh_type(java.lang.Integer,java.lang.String,com.surveypedia.domain.pointhistory.PointHistoryType)! No property s found for type PointHistory!
I tried findByEmailAndS_codeAndPh_type with proper arguments, but I got the same error log. What's the problem with my method there?
The problem is that underscore (_) is restricted to class hierarchies in spring-data-jpa mathod names. It's based on the simple convention of using camelCase in Java, which you're breaking.
Rename the field ph_code to phCode and s_code to sCode both in the entity and in the method name.
I am using Spring Boot, and have the following Entity definitions (abridged):
package com.vw.asa.entities;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.PreUpdate;
import javax.validation.constraints.NotNull;
public abstract class CmsModel extends Model {
#Basic(optional = false)
#NotNull
#Column(name = "is_active")
private short isActive;
public short getIsActive() {
return isActive;
}
public void setIsActive(short isActive) {
this.isActive = isActive;
}
public void setIsActive(String isActive) {
if (isActive.equals("true")) {
this.isActive = IS_TRUE;
} else if (isActive.equals("1")) {
this.isActive = IS_TRUE;
} else {
this.isActive = IS_FALSE;
}
}
}
Then I have several models which extend this 'base' model, following this flavor:
package com.vw.asa.entities.cms;
import com.vw.asa.entities.CmsModel;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Date;
import java.util.List;
/**
* #author Barry Chapman
*/
#Entity
#Table(name = "cms_extra_questions", schema = "asa")
public class CmsExtraQuestions extends CmsModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
...
}
When I initialize an instance of CmsExtraQuestions as a result of a hibernate query, if I call setActive(true) on the object, it has no effect on the members of that object. When I copy the setters and getters from the CmsModel base class into the CmsExtraQuestions class, it works fine.
$entity = new CmsExtraQuestions();
$entity->setActive(true);
Why does this not set the member properties of the instantiated object when calling the extended setter? If this is normal - is there a way to add these properties and member functions to the base model so that they can be inherited also?
Rookie mistake, I forgot to add #MappedSuperClass to the inherited model, CmsModel. That allows JPA to map the properties from that class as though they were defined in the model that was inheriting that base class.
#MappedSuperClass
public abstract class CmsModel extends Model {
#Basic(optional = false)
#NotNull
#Column(name = "is_active")
Here is the class of the object I am trying to map:
package com.agent.module.entities;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
#Entity
#Getter #Setter #NoArgsConstructor
#Accessors
public class Accommodation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String location;
#ManyToOne(optional=false, fetch=FetchType.EAGER)
private AccommodationType type;
private String description;
private String name;
#OneToMany(orphanRemoval=true, fetch=FetchType.EAGER)
#Cascade(CascadeType.ALL)
private Set<Document> images;
private Integer capacity;
#ManyToMany(fetch=FetchType.EAGER)
private Set<AdditionalService> additionalServices;
#OneToMany(orphanRemoval=true, fetch=FetchType.EAGER)
#Cascade(CascadeType.ALL)
private Set<PricePlan> pricePlan;
#ManyToOne(optional=false, fetch=FetchType.LAZY)
private Agent agent;
#OneToMany(orphanRemoval=true, mappedBy="accommodation", fetch=FetchType.EAGER)
#Cascade(CascadeType.ALL)
private Set<Restriction> restrictions;
#ManyToOne(fetch=FetchType.EAGER)
private Category category;
#Override
public String toString() {
return "Name: "+name+"\n"+"Agent PIB: "+agent.toString()+"\n";
}
}
And here is my DTO object:
package com.agent.module.dto;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Getter #Setter #NoArgsConstructor
#XmlRootElement
public class AccommodationView {
private Long id;
private String location;
private String typeName;
private String description;
private String name;
private List<String> imagesPath;
private Integer capacity;
private List<String> additionalServicesName;
private List<PricePlanView> pricePlan;
private String agentUsername;
private List<RestrictionView> restrictions;
private String categoryName;
#Override
public String toString() {
return "ID: "+id+"\n"+"Type: "+typeName+"\n"+"Description: "+description+"\n"+"Category: "+categoryName+"\n"+"Name: "+name+"\n";
}
}
When I open my Postman and try to get all the Accommodation objects from MySQL database, I actually want to get DTO objects, and in order to do that I am using ModelMapper. But for some reason every time I try to map Accommodation to AccommodationView, I get Null in return. Here is the class where I am trying to perform the mapping:
#RestController
#RequestMapping(value = "/accommodation")
public class AccommodationController {
#Autowired
AccommodationRepo accommodationRepo;
#Autowired
ModelMapper mapper;
#RequestMapping(value="/all",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
ResponseEntity<List<AccommodationView>> getAll(){
List<Accommodation> accommodations = accommodationRepo.findAll();
List<AccommodationView> accommodationViewList= new ArrayList<AccommodationView>();
for(Accommodation accommodation : accommodations) {
System.out.println(accommodation);
System.out.println(convertToDto(accommodation));
accommodationViewList.add(convertToDto(accommodation));
}
return new ResponseEntity<List<AccommodationView>>(accommodationViewList, HttpStatus.OK);
}
private AccommodationView convertToDto(Accommodation accommodation) {
return mapper.map(accommodation, AccommodationView.class);
}
private Accommodation convertToEntity(AccommodationView accommodationView) {
return mapper.map(accommodationView, Accommodation.class);
}
}
Here is the output I get when I hit the method:
Name: Test
Agent PIB: 2308995710368
ID: null
Type: null
Description: null
Category: null
Name: null
First part of the output is from Accommodation object, and second part of the output is from AccommodationView object. If anyone has any idea whats going on I would really appreciate the help.
you have to generate public setters functions for the target class, in your case (Accommodation Entity). elsewise the Modelmapper cannot access the private fields of your class to set their values.
I'm stuck with trying to display data for a One-to-One relationship in Twirl templates (using Play Framework Java - 2.5.10). Basically I have a User model:
package models;
import java.sql.Date;
import javax.persistence.*;
import com.avaje.ebean.Model;
#Entity
#Table(name = "users")
public class User extends Model {
#Id
#Column(name = "id")
public Long id;
#Column(name = "first_name")
public String firstName;
#Column(name = "middle_name")
public String middleName;
#Column(name = "last_name")
public String lastName;
#Column(name = "date_of_birth")
public Date dateOfBirth;
#Column(name = "sex")
public String sex;
#OneToOne
#JoinColumn(name = "time_zone_id")
public TimeZone timeZone;
public static Finder<Long, User> find = new Finder<>(User.class);
}
and the Farmer model:
package models;
import com.avaje.ebean.Model;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name="farmers")
public class Farmer extends Model {
public enum Status {INACTIVE, ACTIVE}
#Id
#Column(name="id")
public Long id;
#OneToOne(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="user_id")
public User user;
#Column(name="profile_pic_url")
public String profilePicUrl;
#Column(name="access_url")
public String accessUrl;
#Column(name="status")
public String status = Status.INACTIVE.name();
#OneToMany(mappedBy = "farmer", targetEntity = Farm.class, fetch = FetchType.LAZY)
public List<Farm> farms;
public static Finder<Long, Farmer> find = new Finder<>(Farmer.class);
public static List<Farmer> getAllActive() {
return Farmer.find.where().eq("status", Status.ACTIVE.name()).findList();
}
}
Notice there's a one-to-one with User model with fetch type set to eager. Now, I want to display data of farmers in my template, where a farmer's name is actually the name in the associated User model.
So I did this in my controller:
public class FarmerController extends Controller {
public Result all() {
return ok(farmers.render(Farmer.getAllActive()));
}
public Result farmer(Long id, String url) {
return ok(farmer.render());
}
}
Now this gets me the right farmer data, but when I try to display the name via the User model, I get null. More specifically, writing this results in nulls (I get nullnull, actually):
<div><h4>#(farmer.user.firstName + farmer.user.lastName)</h4></div>
What am I missing?
As discussed at the comments, this is because play-enhancer does not works for views or any Scala code at all. Since Twirl compiles scala.html code to scala code, this compiled code is not touched by the enhancer.
The solution is then to manually create the get for the relationship:
public class Farmer extends Model {
public User getUser() {
return this.user;
}
}
This is Java code and then will be handled as expected. Of course, you have to change your views to use farmer.getUser instead of farm.user.
Also, as stated at the docs, byte code enhancement involves some magic. But you can avoid it at all and just use regular POJOs (with explicitly declared gets and sets).