Please I need some help. I've got 2 entities:
Appointment.class
#Entity
#Table(name = "appointment")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Appointment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "created_date")
private Date createdDate;
#Column(name = "modified_date")
private Date modifiedDate;
#Column(name = "appointment_date")
private LocalDate appointmentDate;
#Column(name = "start_time")
private LocalTime startTime;
private Boolean cancelled;
#ManyToOne
#JoinColumn(nullable = false, name = "client_id")
private Client clientId;
#ManyToOne
#JoinColumn(nullable = false, name = "employee_id")
private Employee employee;
#ManyToOne
#JoinColumn(nullable = false, name = "service_id")
private Service service;
}
And Employee.class
#Entity
#Table(name = "employee")
#NoArgsConstructor
#AllArgsConstructor
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "created_date")
private Date createdDate;
#Column(name = " modified_date")
private Date modifiedDate;
#OneToOne
#JoinColumn(name = "service_id", referencedColumnName = "id")
private Service service;
}
I need to get all the appointments that match with the given startTime, appointmentDate and employee
I want to define an abstract method in the interface AppointmentRepo so that in my AppointmentServices.class I can call that method with 3 arguments and get the appointment entity.
AppointmentServices.class
appointmentRepo.getAppointmentByDateAndEmployee(date, employee, scheduledHour);
AppointmentRepo interface
#Repository
public interface AppointmentRepo extends JpaRepository<Appointment, Integer>{
#Query("SELECT a FROM Appointment a INNER JOIN a.employee e WHERE a.appointmentDate = :appointment_date AND e = :employee AND s.startTime = :start_time")
public List<Appointment> getAppointmentByDateAndEmployee (#Param("appointment_date") LocalDate appointmentDate,
#Param("employee_id") Employee employee, #Param("start_time") LocalTime startTime);
}
How I have to set my #Query in order to be given an appointment entity that matches with 3 given arguments (a date, and time and a reference to other entity called Employee)
Am I doing wrong matching the entire object so I need just to use the id of the Employee entity?
Please help me, and thanks for your time!!
Happy Holidays
You can use SQL instead HQL (nativeQuery=true)
DAO Layer
package com.jb.app.repos;
import com.jb.app.beans.Appointment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
#Repository
public interface AppointmentRepository extends JpaRepository<Appointment, Integer> {
#Query(value = "SELECT * FROM APPOINTMENT WHERE appointment_date = ?1 AND start_time = ?2 AND employee_id = ?3", nativeQuery = true)
List<Appointment> getAppointmentByDateAndEmployee(LocalDate appointmentDate, LocalTime startTime, int employeeId);
}
Service Layer
package com.jb.app.services;
import com.jb.app.beans.Appointment;
import com.jb.app.beans.Employee;
import com.jb.app.repos.AppointmentRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
#Service
#RequiredArgsConstructor
public class AppointmentServiceImpl implements AppointmentService{
private final AppointmentRepository appointmentRepository;
#Override
public List<Appointment> getAppointmentByDateAndEmployee(LocalDate appointmentDate, LocalTime startTime, Employee e) {
return appointmentRepository.getAppointmentByDateAndEmployee(appointmentDate,startTime,e.getId());
}
}
Related
I am in the process of adding a DTO layer to a restful api. Before, the program used entity (Recipe and Ingredient) directly and now I added a new DTO layer in between (RecipeDTO IngredientDTO). However, the moment I made the change I started getting Null values from #RequestBody. Each recipe contains a list of Ingredients and it is the list of ingredients that are returning null values, the recipe by itself is returning fine.
The controller looks like this
package com.example.recipes.controller;
import com.example.recipes.DTO.RecipeDTO;
import com.example.recipes.Service.RecipeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/*...*/
#PostMapping(path = "/post")
public void postRecipes(#RequestBody RecipeDTO recipeDTO){
recipeService.postRecipes(recipeDTO);
}
/*...*/
Recipe Entity
package com.example.recipes.Entity;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;
#Data
#Entity
#Table(name = "recipe", schema = "public")
#AllArgsConstructor
#NoArgsConstructor
public class Recipe {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#Column(name = "id", updatable = false, nullable = false)
private long id;
#Column(name = "name")
private String name;
#Column(name = "instructions")
private String instructions;
#OneToMany(mappedBy = "recipe")
private List<Ingredient> ingredients;
#JsonProperty("date_added")
private String dateAdded = String.valueOf(LocalDateTime.now());
#JsonProperty("last_edited")
private String lastEdited = String.valueOf(LocalDateTime.now());
}
RecipeDTO
package com.example.recipes.DTO;
import lombok.*;
import javax.persistence.OneToMany;
import java.time.LocalDateTime;
import java.util.List;
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#ToString
public class RecipeDTO {
private long id;
private String name;
private String instructions;
private List<IngredientDTO> ingredientsDTO;
private String dateAdded = String.valueOf(LocalDateTime.now());
private String lastEdited = String.valueOf(LocalDateTime.now());
public RecipeDTO(long id, String name, String instructions, String dateAdded, String lastEdited) {
this.id = id;
this.name = name;
this.instructions = instructions;
this.dateAdded = dateAdded;
this.lastEdited = lastEdited;
}
}
Ingredient Entity
package com.example.recipes.Entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.*;
#Data
#Entity
#Table(name = "Ingredient")
#NoArgsConstructor
public class Ingredient {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#JsonProperty("ingredient_id")
private long ingredient_ID;
#JsonProperty("ingredient_name")
private String ingredientName;
#Column(name = "amount")
private int amount;
#Column(name = "unit")
private String unit;
#ManyToOne
#JoinColumn(name = "recipe_id")
#ToString.Exclude
#JsonIgnore
private Recipe recipe;
}
IngredientDTO
package com.example.recipes.DTO;
import lombok.*;
#Data
#AllArgsConstructor
#NoArgsConstructor
public class IngredientDTO {
private long ingredientID;
private String ingredientName;
private int amount;
private String unit;
}
the json i sent
{
"name":"unique2",
"ingredients":[
{
"ingredient_name":"Atlantic",
"amount":13,
"unit":"ton"
},
{
"ingredient_name":"Pacific",
"amount":15,
"unit":"boatload"
},
{
"ingredient_name":"Indian",
"amount":38,
"unit":"trucload"
}
],
"instructions":"easy on the salt"
}
and the #requestbody the ingredientsDTO is null
this is recipe: RecipeDTO(id=0, name=unique2, instructions=easy on the salt, ingredientsDTO=null, dateAdded=2022-08-08T15:04:10.678748100, lastEdited=2022-08-08T15:04:10.678748100)
Edit: I have just tried copying the code from the entity classes and pasting them in the DTO classes and it still returning null...
package com.example.recipes.DTO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;
#Data
#Entity
#Table(name = "recipe", schema = "public")
#AllArgsConstructor
#NoArgsConstructor
public class RecipeDTO {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#Column(name = "id", updatable = false, nullable = false)
private long id;
#Column(name = "name")
private String name;
#Column(name = "instructions")
private String instructions;
#OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredientDTOs;
#JsonProperty("date_added")
private String dateAdded = String.valueOf(LocalDateTime.now());
#JsonProperty("last_edited")
private String lastEdited = String.valueOf(LocalDateTime.now());
public RecipeDTO(long id, String name, String instructions, String dateAdded, String lastEdited) {
this.id = id;
this.name = name;
this.instructions = instructions;
this.dateAdded = dateAdded;
this.lastEdited = lastEdited;
}
}
package com.example.recipes.DTO;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.*;
#Data
#Entity
#Table(name = "Ingredient")
#NoArgsConstructor
public class IngredientDTO {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#JsonProperty("ingredient_id")
private long ingredientID;
#JsonProperty("ingredient_name")
private String ingredientName;
#Column(name = "amount")
private int amount;
#Column(name = "unit")
private String unit;
#ManyToOne
#JoinColumn(name = "recipe_id")
#ToString.Exclude
#JsonIgnore
private RecipeDTO recipeDTO;
public IngredientDTO(long ingredientID, String ingredientName, int amount, String unit) {
this.ingredientID = ingredientID;
this.ingredientName = ingredientName;
this.amount = amount;
this.unit = unit;
}
}
#RequestBody
this is recipe: RecipeDTO(id=0, name=unique2, instructions=easy on the salt, ingredientDTOs=null, dateAdded=2022-08-08T15:24:19.325806500, lastEdited=2022-08-08T15:24:19.325806500)
these are the ingredients: null
this is ingredientDTO: null
this is ingredientDTO: null
Edit2: I tried posting only the ingredientDTO and the #RequestBody was able to pick it up just fine
//this is fine
public void testRecipePost(#RequestBody IngredientDTO ingredientDTO) {
System.out.println("ingredientDTO: " + ingredientDTO);
}
You can replace
#OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredientDTOs;
to
#OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredients;
Or adding
#JsonProperty("ingredients")
Example:
#JsonProperty("ingredients")
#OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredientDTOs;
The reason for null is because Jackson doesn't know how to deserialise your fields properly with different names.
In the json, the name is ingredients but, in the DTO, it is ingredientsDTO. Those 2 need to match.
You request
{
"name":"unique2",
"ingredients":[...]
here the name of array you are passing in Json is different in the entity you are using.
Your DTO
#OneToMany(mappedBy = "recipeDTO")
private List<IngredientDTO> ingredientDTOs;
The name of fields in JSON request and Entity must match.
Change the name of field private List<IngredientDTO> ingredientDTOs; to ingredients.
I call the method below to calculate some values. I supply the agencyID and the integer representation of the month to carry out the calculation.
Map jan = new HashMap();
String a[] = shifts.getAgencyActiveAgentsByMonth(agencyID, 1).split(",");
jan.put("label", "Jan");
jan.put("active", a[0]);
jan.put("inactive", a[1]);
activeInactiveGraphData.add(jan);
Map feb = new HashMap();
a = shifts.getAgencyActiveAgentsByMonth(agencyID, 2).split(",");
feb.put("label", "Feb");
feb.put("active", a[0]);
feb.put("inactive", a[1]);
activeInactiveGraphData.add(feb);
Etc...till I get to December (value 12). However, the code gives a run time error when I get to May (month value 5). The strange thing however is that this error is thrown
java.sql.SQLException: Column 'shift_dayid' not found.
In my query, I did not include the shift_dayId anywhere in my query, as shown below:
#Query(value = "select distinct userid from shift_days where agencyid = :agencyID and month(created_at) = :givenMonth and shift_status = 1", nativeQuery=true)
List<ShiftDaysModel> getAgencyActiveAgentsCountForMonth(#Param("agencyID") String agencyID, #Param("givenMonth") int givenMonth);
I kindly request help in helping me identify why the givenMonth throws runtime exception when the value is greater than 4.
Thank you
EDIT
Here is my ShiftDaysModel as requested...
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
/**
*
* #author Javalove
*/
#Entity
#Getter
#Setter
#NoArgsConstructor
#Table(name = "shift_days")
public class ShiftDaysModel implements Serializable {
public static enum RequestStatus {
PENDING, ACCEPTED, COMPLETED, REJECTED, TRANSFERED, IN_PROGRESS, VALIDATED;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long shiftDayID;
#Column(name = "created_at", updatable=false)
#CreationTimestamp
#Temporal(javax.persistence.TemporalType.TIMESTAMP)
private Date createdAt;
#Column(name = "updated_at")
#UpdateTimestamp
#Temporal(javax.persistence.TemporalType.TIMESTAMP)
private Date updatedAt;
#Column(name = "userID")
private String userID;
#Column(name = "agencyID")
private String agencyID;
#Basic
#Column(name = "shiftDate")
private LocalDate shiftDate;
#Basic
#Column(name = "startTime")
private LocalTime startTime;
#Basic
#Column(name = "endTime")
private LocalTime endTime;
#Column(name = "shiftOptionID")
private String shiftOptionID;
#Column(name = "shiftStatus")
private RequestStatus shiftStatus;
#Column(name = "isTransferred")
private boolean isTransferred;
#Column(name = "transferredFromID")
private String transferredFromID;
#Column(name = "shiftID")
private String shiftID;
#Column(name = "shiftRate")
private Double shiftRate;
#Column(name = "siteID")
private String siteID;
#Basic
#Column(name = "actualStartDateTime")
private LocalDateTime actualStartDateTime;
#Basic
#Column(name = "actualEndDateTime")
private LocalDateTime actualEndDateTime;
#Column(name = "shiftRequestGroupID")
private String shiftGroupID = "0";
}
When you use the signature:
#Query(..)
List<ShiftDaysModel> getAgencyActiveAgentsCountForMonth(...);
The expectation is that the select clause will contain all the fields that are necessary to create the entity ShiftDaysModel.
In your case, the query only select the column userid. When Hibernate/Spring tries to covert each row into a ShiftDaysModel, it throws the exception because there is no value for the field corresponding to shift_dayid.
This might work if you change the query to select distinct * from shift_days ...
Or, if you only care about the userid:
#Query(..)
List<String> getAgencyActiveAgentsCountForMonth(...)
Whenever I am trying to save an object to my DB, I keep getting the error Column 'created_at' cannot be null.
Here is my audit model:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
#JsonIgnoreProperties(
value = {"createdAt", "updatedAt"},
allowGetters = true
)
public abstract class AuditModel implements Serializable {
#Temporal(TemporalType.TIMESTAMP)
#CreatedDate
#Column(name = "created_at", nullable = false, updatable = false)
#JsonIgnore
private Date createdAt;
#Temporal(TemporalType.TIMESTAMP)
#LastModifiedDate
#Column(name = "updated_at", nullable = false)
private Date updatedAt;
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
}
Here is an example of a model that extends it:
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.lang.Nullable;
import javax.persistence.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Set;
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class Category extends AuditModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty(message = "Item Name is required.")
private String categoryName;
#NotNull
private String categoryDescription;
#Nullable
#JsonIgnore
#OneToMany(mappedBy = "category", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Item> items;
public Category(String categoryName, String categoryDescription) {
this.categoryName = categoryName;
this.categoryDescription = categoryDescription;
}
And here is the temporary CommandLine bean that is performing some tests for me:
#Configuration
public class ItemConfig {
#Autowired
ItemRepository itemRepository;
#Autowired
CategoryRepository categoryRepository;
#Value("${STORE.ENV}")
private String env;
#Bean
CommandLineRunner itemRunner(ItemRepository itemRepository) {
return args -> {
System.out.println("true");
Category cueCategory = new Category
("Cues",
"This category contains all items relating to billiard cues. This includes yada, yadada, and yada."
);
categoryRepository.save(cueCategory);
Item item = new Item("Test Cue", 700, cueCategory);
itemRepository.save(item);
};
}
}
At first, I was creating a blank object then setting all the values with setters. I thought that maybe it all needed to happen in one fel-swoop for the created_at to register with a proper date, so I added some constructors. That still didn't work. Please let me know if you see anything glaring!
You can fix this issue by modifying your createdAt and updatedAt properties like below and also, modify your getter and setters.
#CreationTimestamp
#Column(name = "created_at", updatable = false)
private Timestamp createdAt;
#UpdateTimestamp
#Column(name = "updated_at")
private Timestamp updatedAt;
You should add #EnableJpaAuditing annotation.
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
// ...
#Configuration
#EnableJpaAuditing
public class ItemConfig {
// ...
}
I am making a Spring web service to learn more about it and I am currently mapping the database. I have a table that has a composite ID, where one of the ID's is a foreign key to another table (ManytoOne).
Creditors
Creditor_Invoices
ID
Creditor_ID
name
Invoice_ID
As anywhere you buy something they use their own way of making ID's it has a composite ID like this.
My Current code:
Serializable class CInvoiceId:
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import java.io.Serializable;
import java.util.Objects;
#Embeddable
public class CInvoiceId implements Serializable {
#ManyToOne
#JoinColumn(name = "creditors_id", nullable = false)
private Creditor cInvoiceCreditorId;
#Column(name = "invoice_id", nullable = false)
private String cInvoiceId;
public CInvoiceId(Creditor creditor, String cInvoiceId){
this.cInvoiceCreditorId = creditor;
this.cInvoiceId = cInvoiceId;
}
//Setters, Getters, Equals and Hash
}
My Creditor class
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "creditors")
public class Creditor {
#Id
#GeneratedValue
#Column(name = "id")
private int creditorId;
#Column(name = "name",nullable = false)
private String creditorName;
#OneToMany(mappedBy = "cInvoiceCreditorId")
private List<CInvoice> cInvoices;
}
My CInvoice class:
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
#Entity
#Table(name = "c_invoices")
public class CInvoice {
#EmbeddedId
private CInvoiceId cInvoiceID;
}
When I start it to try and test it I get the error that it can not find the mapped by from the creditor class, but I don't know what I should map it to as the ID is now made in the CInvoiceId class. What should it be?
Regards
Dany
You can use "derived identities" to map these classes:
Creditor:
#Entity
#Table(name = "creditors")
public class Creditor {
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "name",nullable = false)
private String name;
#OneToMany(mappedBy = "creditor")
private List<CInvoice> invoices;
}
CInvoiceId:
#Embeddable
public class CInvoiceId implements Serializable {
#Column(name = "invoice_id", nullable = false)
private String invoiceID;
private int creditorID; // corresponds to PK type of Creditor
// ...
}
CInvoice:
#Entity
#Table(name = "c_invoices")
public class CInvoice {
#EmbeddedId
private CInvoiceId id;
#MapsId("creditorID") // maps creditorID attribute of embedded id
#ManyToOne
#JoinColumn(name = "creditors_id", nullable = false)
Creditor creditor;
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.
I've got a spring mvc application in which users book meeting rooms. When I try to delete a booking by using deleteById(), the user that belongs to the booking is also deleted. How do I prevent this?
Booking object:
package spe_booker.models;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.Date;
#Entity
public class Booking {
#Id
#GeneratedValue
private Long id;
#NotNull
private Date startDateTime;
#NotNull
private Date endDateTime;
#NotNull
#ManyToOne(cascade = CascadeType.ALL)
private Room room;
#NotNull
#ManyToOne(cascade = CascadeType.ALL)
private User user;
private Date creationDate;
public Booking() { }
...getters and setters...
User object:
package spe_booker.models;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue
private Long id;
private String username;
String name;
private String password;
private String faculty;
private String role;
private Boolean blacklisted;
#Column(name = "enabled")
public int enabled;
#OneToMany(mappedBy = "user")
private List<Booking> bookings = new ArrayList<>();
...getters and setters...
Booking repository:
package spe_booker.Repositorys;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import spe_booker.models.Booking;
import spe_booker.models.User;
import java.util.List;
#Repository
public interface BookingRepository extends CrudRepository<Booking, Long> {
List<Booking> findAll();
List<Booking> findBookingsByUser(User user);
}
The delete call:
bookingRepository.deleteById(id);
Change the cascade of
#NotNull
#ManyToOne(cascade = CascadeType.ALL)
private User user;
to the appropriate values. Do you change a user and then save a booking and a user at the same time? Do you create bookings with users at the same time? If you can answer these questions with "no", you might just remove (cascade = CascadeType.ALL) at all. And you should ask yourself why the cascades are present in the first place!
CascadeType.ALL forces JPA to cascade all operations (persisting, removing, updating....). That means when you delete/update a booking, it willl delete/update the Room and User objects too