How to fetch only IDs from nested array in Hibernate? - java

I have problem getting only ID from objects that are in nested collection in Hibernate.
Here is my code:
public class Company {
private int id;
private Set<Account> owners = new HashSet<Account>();
#Id
public int getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.LAZY, targetEntity = Account.class)
#Fetch(org.hibernate.annotations.FetchMode.JOIN)
#JoinTable(
name = "company_accountOwners",
joinColumns = #JoinColumn(name = "company_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "account_id", referencedColumnName = "id")
)
public Set<Account> getOwners() {
return owners;
}
public void setOwners(Set<Account> owners) {
this.owners = owners;
}
Account.class
public class Account {
private String id;
private String name;
private int age;
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(columnDefinition = "VARCHAR(36)")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
How only get JSON with all Company.class data and array of owners (Account.class) that has only ID, not all other attributes (name, age)?
I'm in complete dark.
Json i get now:
{
"id": 1,
"owners": [
{
"id": "testID",
"name": "name",
"age": 45
},
{
"id": "testID2",
"name": "name2",
"age": 90
}]
}
I want json like:
{
"id": 1,
"owners": [
{
"id": "testID"
},
{
"id": "testID2"
}]
}

Related

Build a jpa specification query without retriving child entity data

I'm trying to build a search query to find all Test entity data.
Here are my classes...
TestParent.java
#Entity
public class TestParant{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String createdBy;
#OneToMany(
targetEntity = Channel.class,
cascade = {CascadeType.ALL},
fetch = FetchType.LAZY)
#JoinColumn(name = "test_id")
private List<Channel> channels;
}
Channel.java
#Entity
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Channel{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Enumerated(EnumType.STRING)
private Channel channelName;
#ManyToOne
#JoinColumn(name = "id", referencedColumnName = "id")
#JsonBackReference
private Test test;
#OneToMany(
targetEntity = TvSet.class,
cascade = {CascadeType.ALL},
fetch = FetchType.LAZY)
#JoinColumn(name = "channel_id")
private List<TvSet> tvSets;
}
My specification query...
public Specification<TEST> generateFilterTestQuery(FilterRequest filterRequest) {
log.info("start to build query from" + filterRequest);
return (root, cq, cb) -> {
Predicate p = cb.conjunction();
Join<Channel, Campaign> channel = root.join("channels", JoinType.INNER);
if (!filterRequest.getStartCreatedDate().isEmpty() && !filterRequest.getEndCreatedDate().isEmpty())
p = cb.and(p,
cb.between(root.get(CREATED_DATE),
filterRequest.getStartCreatedDate(), filterRequest.getEndCreatedDate()));
else if (!(filterRequest.getStartCreatedDate().isEmpty()))
p = cb.and(p,
cb.greaterThanOrEqualTo(root.get(CREATED_DATE),
filterRequest.getStartCreatedDate()));
else if (!(filterRequest.getEndCreatedDate().isEmpty()))
p = cb.and(p,
cb.lessThanOrEqualTo(root.get(CREATED_DATE),
filterRequest.getEndCreatedDate()));
if (!filterRequest.getCreatedBy().isEmpty()) {
p = cb.and(p,
cb.equal(root.get("createdBy"), filterRequest.getCreatedBy()));
}
if (!filterRequest.getBrandId().isEmpty()) {
p = cb.and(p,
cb.equal(root.get("brandId"), filterRequest.getBrandId()));
}
if (!filterRequest.getChannel().isEmpty()) {
p = cb.and(p,
cb.equal(channel.get("channelName"), Channel.valueOf(filterRequest.getChannel())));
}
if (filterRequest.getSort().equals(SortEnum.ASC)) {
cq.orderBy(cb.asc(root.get(CREATED_DATE)));
} else {
cq.orderBy(cb.desc(root.get(CREATED_DATE)));
}
return p;
};
}
And my service method...
public Page<TEST> filter(FilterRequest filterRequest) throws BaseException {
try {
log.info("Getting campaign for filterRequest : {} ", filterRequest);
// Find Sorted Creatives By filterRequest
Pageable pageable =
PageRequest.of(filterRequest.getPageNumber(), filterRequest.getPageSize());
return repository.findAll(generateFilterTestQuery(filterRequest),pageable);
} catch (IllegalArgumentException e) {
log.error(e.getMessage());
throw new BaseException(e.getMessage());
}
}
The result I'm getting...
"content": [
{
"id": 516,
"name": "Tes",
"createdBy": "test",
"brandId": "1",
"createdDate": 1671529546,
"channels": [
{
"id": 312,
"channelName": "TV1",
"channelStatus": "DONE",
"tvSet": [
{
"id": 312,
"locations": [
{
"id": 302,
"isGeoLocation": true,
"cities": [
{
"id": 282,
"distanceUnit": "kilometer",
},
{
"id": 283,
"distanceUnit": "kilometer",
}
],
}
But I want to get results(channels ) that without getting tvSet array like in below
"content": [
{
"id": 516,
"name": "Tes",
"createdBy": "test",
"brandId": "1",
"createdDate": 1671529546,
"channels": [
{
"id": 312,
"channelName": "TV1",
"channelStatus": "DONE",
"tvSet": [],
}
Can someone please help me to resolve this issue?? Thank you!!

Return id of Many-To-One relation in Java Spring rest api

I have 2 models: Student and Teacher. Teacher has One-To-Many relation with Student, Student has Many-To-One with teacher. I want to build API what gonna have in response just id of teacher, in request just id of teacher too. How can i do so?
[
{
"id": 0,
"name": "string",
"grade": "int32",
"teacher": 0,
}
]
And thisrequest to the POST method
{
"name": "string",
"grade": "int",
"teacher": 0,
}
But now I have this example data (from Swagger) to POST method:
{
"name": "string",
"grade": "int",
"teacher": {
"id": 0,
"name": "string",
"surname": "string",
"students": [
{
"id": 0,
"name": "string",
"grade": "int",
"teacher": "string"
}
]
},
}
Models:
#Entity
#Getter
#Setter
#NoArgsConstructor
public class Teacher {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String surname;
#OneToMany(orphanRemoval = true, mappedBy = "author")
private List<Student> students;
public Teacher(TeacherRequest request) {
this.name = request.getName();
this.surname = request.getSurname();
this.students = new ArrayList<Student>();
}
}
#Entity
#Getter
#Setter
#NoArgsConstructor
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int grade;
#ManyToOne(fetch = FetchType.LAZY)
#OnDelete(action = OnDeleteAction.CASCADE)
private Teacher teacher;
public Book(BookRequest request) {
this.name = request.getName();
this.grade = request.getGrade();
this.teacher = request.getTeacher();
}
}
also i have there bodies for Stundent:
#Getter
#Setter
public class StudentRequest {
private String name;
private int grade;
private Teacher Teacher;
}
#Getter
public class StudentResponse {
private final long id;
private final String name;
private final int grade;
private final Long teacher;
public BookResponse(Book b) {
this.id = b.getId();
this.name = b.getName();
this.grade = b.getGrade();
this.teacher = b.getAuthor().getId();
}
}
If you specifically only want the id or only one of the entity fields to be returned then, you can use #JsonIdentityInfo and #JsonIdentityReference annotations to get the job done.
Reference: https://stackoverflow.com/a/65389727/14011502

Jackson Ignor From Another Java Class

I want the Employee object to only display the id property when adding a CV. When I put #JsonIgnore on the employee object in the CV, the id doesn't come either. How can I hide property that I do not want to appear in employee class (password, national identity) on the CV class?
Cv Class :
#Entity
#Table(name = "cvs")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class CV {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#OneToOne
#JoinColumn(name = "employee_id")
private Employee employee;
#OneToMany(mappedBy = "cv", cascade = CascadeType.ALL, orphanRemoval = true)
List<Education> educations;
#OneToMany(mappedBy = "cv", cascade = CascadeType.ALL, orphanRemoval = true)
List<Work> works;
#OneToMany(mappedBy = "cv", cascade = CascadeType.ALL, orphanRemoval = true)
List<Languege> langueges;
#OneToMany(mappedBy = "cv", cascade = CascadeType.ALL, orphanRemoval = true)
List<Technology> technologies;
#Column(name = "github")
private String github;
#Column(name = "linkedin")
private String linkedin;
#NotNull
#NotBlank
#Column(name = "cover_letter")
private String coverLetter;
#Column(name = "photo")
private String photo;
}
Employee Class :
#Data
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "employees")
#Entity
#PrimaryKeyJoinColumn(name = "user_id", referencedColumnName = "id")
public class Employee extends User {
#NotNull
#NotBlank
#Column(name = "first_name", length = 50)
private String firstName;
#NotNull
#NotBlank
#Column(name = "last_name", length = 50)
private String lastName;
#NotNull
#NotBlank
#Column(name = "national_identity", length = 11)
private String nationalIdentity;
#NotNull
#Column(name = "year_of_birth")
private int yearOfBirth;
#JsonIgnore
#OneToOne(mappedBy = "employee")
private CV cv;
}
I read data this format;
{
"id": 0,
"employee": {
"id": 0, //visible
"email": "string", //not visible
"password": "string", //not visible
"firstName": "string",
"lastName": "string",
"nationalIdentity": "string", //not visible
"yearOfBirth": 0 //not visible
},
"educations": [
{
"id": 0,
"schoolName": "string",
"department": "string",
"startingDate": "2022-02-11",
"graduationDate": "2022-02-11"
}
],
"works": [
{
"id": 0,
"workplace": "string",
"job": {
"id": 0,
"jobName": "string"
},
"startingDate": "2022-02-11",
"endDate": "2022-02-11"
}
],
"langueges": [
{
"id": 0,
"languege": "string",
"level": 0
}
],
"technologies": [
{
"id": 0,
"technology": "string"
}
],
"github": "string",
"linkedin": "string",
"coverLetter": "string",
"photo": "string"
}
You can use a custom serializer that you would use on your CV employee field : #JsonSerialize(using = CustomEmployeeSerializer.class)
public class CustomEmployeeSerializer extends SerializerBase<Employee> {
public CustomEmployeeSerializer() {
super(Employee.class, true);
}
#Override
public void serialize(Employee employee, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeString(employee.getId());
}

Spring boot JPA save manytomany relationship

I am trying to store a manytomany relationship but it not stores the relationship.
The following code has generated 3 tables.
Soldier, medal and soldier_medals.
The service just make a call to a CRUD Interface with save(soldier).
It stores correctly the soldier, but it not fill any row at the soldier_medals table.
This is the JSON I send to the server:
{
"abbreviatedSequence": "XDF",
"medals": [
{
"name": "Purple",
"id": 1
},
{
"name": "Red",
"id": 2
}
],
"sequence": "XDFREE",
"name": "Savier"
}
Employee.java
#Entity(name="employees")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class Employee {
#Id
#GeneratedValue(generator="increment")
#GenericGenerator(name="increment", strategy = "increment")
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Soldier.java
#Entity(name="soldier")
public class Soldier extends Employee{
private String sequence;
private String abbreviatedSequence;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "soldiers")
private List<Medal> medals;
public List<Medal> getMedals() {
return medals;
}
public void setMedals(List<Medal> medals) {
this.medals = medals;
}
public String getSequence() {
return sequence;
}
public void setSequence(String sequence) {
this.sequence = sequence;
}
public String getAbbreviatedSequence() {
return abbreviatedSequence;
}
public void setAbbreviatedSequence(String abbreviatedSequence) {
this.abbreviatedSequence = abbreviatedSequence;
}
}
Medal.java
#Entity
#Table(name = "medal")
public class Medal {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.AUTO, generator = "medal_seq")
#SequenceGenerator(name = "medal_seq", sequenceName = "medal_seq", allocationSize = 1)
private Long id;
#Column(name = "NAME", length = 50, unique = true)
#NotNull
#Size(min = 3, max = 50)
private String name;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "soldier_medals", joinColumns = {
#JoinColumn(name = "medal_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "soldier_id",
nullable = false, updatable = false) })
private List<Soldier> soldiers;
public List<Soldier> getSoldiers() {
return soldiers;
}
public void setSoldiers(List<Soldier> soldiers) {
this.soldiers = soldiers;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
SoldierController.java
#RestController
public class SoldierController {
#Autowired
private SoldierService soldierService;
#RequestMapping(value="/api/contextsource/soldier",method= RequestMethod.POST)
public Soldier addSoldier(#RequestBody Soldier soldier) {
return soldierService.addSoldier(soldier);
}
}
I was persisting the entity with in the wrong order. This is how I solved it.
#RequestMapping(value="/api/contextsource/soldier",method= RequestMethod.POST)
public Soldier addPeptide(#RequestBody Soldier soldier) {
for(Medal s: soldier.getMedals()) {
Medal ss = medalService.getMedalById(s.getId());
ss.getMedals().add(soldier);
medalService.addMedal(ss);
}
return null;
}
Did you try this in Soldier entity class like medal entity class:
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "soldier_medals", joinColumns = {
#JoinColumn(name = "soldier_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "medal_id",
nullable = false, updatable = false) })
private List<Medal> medals;
instead of this :
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "soldiers")
private List<Medal> medals;

Spring Data Rest Many to many tree projection mapping

I have an entity called ContentPath, with may have a parent of the same type, and sons of the same type, represented with something like:
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "NAME", length = 50)
#NotNull
private String name;
#Column(name = "DESCRIPTION")
private String description;
#ManyToOne
#JoinColumn(name="CONTENT_PATH_ID")
public ContentPath contentPath;
#OneToMany(mappedBy="contentPath")
public Set<ContentPath> contentPaths;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "ACTIVITY_CONTENT_PATH",
joinColumns = {#JoinColumn(name = "CONTENT_PATH_ID", referencedColumnName = "ID")},
inverseJoinColumns = {#JoinColumn(name = "ACTIVITY_ID", referencedColumnName = "ID")})
private Set<Activity> activities;
I have my ContentPathRepository, which exposes it as an API.
#RepositoryRestResource(collectionResourceRel = "contentPaths", path = "contentPaths", excerptProjection = ContentPathProjection.class)
public interface ContentPathRestRepository extends PagingAndSortingRepository<ContentPath, Long> {
}
and my projection, which should format correctly my object.
#Projection(name = "contentPathProjection", types = ContentPath.class)
public interface ContentPathProjection {
Long getId();
String getName();
Set<ContentPath> getContentPaths();
}
I was expecting to get a list of ContentPaths, which have their ContentPaths inside, and I got success, but it is not bringing IDs, it is bringing Name and Description only, and it is very weird because my projection doesn't have the Description.
Current response:
"name": "BOOK 1",
"id": 1,
"contentPaths": [
{
"name": "UNIT 1",
"description": "UNIT 1 description"
},
{
"name": "UNIT 2",
"description": "UNIT 2 description"
}
]
Why is it happening? How fix it?
This is a normal behavior of SDR. It don't show id by default. To turn this on just register a bean like this:
#Bean
public RepositoryRestConfigurerAdapter repositoryRestConfigurerAdapter() {
return new RepositoryRestConfigurerAdapter() {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(ContentPath.class);
super.configureRepositoryRestConfiguration(config);
}
};
}
About description - you have this field:
#Column(name = "DESCRIPTION")
private String description;

Categories