Modifying Data in a Many-to-One Relationship java spring boot - java

I'm having trouble updating my data in a Many-to-One relationship
Product Model:
#Entity
#Getter
#Setter
#Table(name = "Products")
public class ProductModel {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long product_id;
#Column(name = "ProductName")
#Lob
private String productName;
#Column(name = "ShortName")
private String shortName;
#Column(name = "Price")
private int price;
#Column(name = "Slider")
private Boolean slider;
#Column(name = "SpecialOffer")
private Boolean specialOffer;
#Column(name = "NewPrice")
private int newPrice;
#Column(name = "ShortDesc")
private String shortDesc;
#Column(name = "FullDescription")
#Lob
private String fullDescription;
#Column(name = "Image")
#Lob
private String image;
#ManyToOne
#JoinColumn(name="category_id")
private CategoryModel categoryModel;
}
Category Model:
#Table(name = "Category")
public class CategoryModel {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long category_id;
#Column(name = "CategoryName")
private String cateGoryName;
#Column(name = "Image")
#Lob
private String image;
}
Service:
Add Product:
public ResponseEntity<ProductModel> addProduct(#PathVariable Long id,
#RequestBody ProductModel productDetails){
ProductModel productModel = categoryRespository.findById(id).map(category -> {
productDetails.setCategoryModel(category);
return productRepository.save(productDetails);
}).orElseThrow(() -> new RuntimeException("Fail "));
return new ResponseEntity<>(productModel,HttpStatus.CREATED);}
Update Product:
public ResponseEntity<ProductModel> updateProduct(#PathVariable long id, long categoryId, #RequestBody ProductModel productDetails) {
ProductModel updateProduct = productRepository.findById(id).orElseThrow(() -> new RuntimeException("Fail " + id));
updateProduct.setFullDescription(productDetails.getFullDescription());
updateProduct.setImage(productDetails.getImage());
updateProduct.setNewPrice(productDetails.getNewPrice());
updateProduct.setPrice(productDetails.getPrice());
updateProduct.setShortDesc(productDetails.getShortDesc());
updateProduct.setShortName(productDetails.getShortName());
updateProduct.setProductName(productDetails.getProductName());
updateProduct.setSlider(productDetails.getSlider());
updateProduct.setSpecialOffer(productDetails.getSpecialOffer());
productRepository.save(updateProduct);
return ResponseEntity.ok(updateProduct);
}
Add product working fine, adds a product according to the category. Update product also works fine without modifying the categories.The problem is that I cannot correctly assign the modified category to the model.

Related

JPA : How to handle mapping with a table that has relationship with two other tables?

I have three tables, table A (product), table B (invoice) and table C (invoices_info) which contains two columns referencing invoice_id and product_id. How can i insert a new entry (a new invoice) while inserting the products to the appropriate table and inserting the invoice info to its table also ?
Here are the entity classes :
Product
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "family_id")
private long familyId;
#Column(name = "product_name")
private String productName;
#Column(name = "product_category")
private String productCategory;
#Column(name = "product_quantity")
private int productQuantity;
//getters and setters
}
Invoice
#Entity
#Table(name = "invoice")
public class Invoice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "invoice_id")
private Long id;
#Column(name = "provider_id")
private Long providerId;
#Column(name = "total")
private int invoiceTotal;
#Column(name = "date")
private Date invoiceDate;
//getters and setters
}
InvoiceInfo
#Entity
#Table(name = "invoice_info")
public class InvoiceInfo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id")
private long id;
#Column(name = "product_id")
private long productId;
#Column(name = "invoice_id")
private long invoiceId;
//getters and setters
}
InvoiceInfo should be join table, Define relationship on entities Product & Invoice using annotations #OneToMany, #ManyToOne based on your requirement.
You have to create relationships between your entities by using a set of annotations like: #ManyToOne, #OneToMany, #ManyToMany or #OneToOne... and other annotations if needed.
In your case I am not really sure you need an InvoiceInfo table, as the Invoice table can (or should) already contains the list of products.
I would suggest you the following relationships:
Product
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "family_id")
private long familyId;
#Column(name = "product_name")
private String productName;
#Column(name = "product_category")
private String productCategory;
#Column(name = "product_quantity")
private int productQuantity;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "invoice_id", referencedColumnName = "id")
private Invoice invoice;
//getters and setters
}
Invoice
#Entity
#Table(name = "invoice")
public class Invoice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "invoice_id")
private Long id;
#Column(name = "provider_id")
private Long providerId;
#Column(name = "total")
private int invoiceTotal;
#Column(name = "date")
private Date invoiceDate;
#OneToMany(mappedBy = "product")
private List<Product> products;
//getters and setters
}
As your table InvoiceInfo no longer exists, you just have to insert you data in two table like this:
Invoice invoice = invoiceRepository.save(invoice);
Product product = new Product();
// Set the other properties
product.setInvoice(invoice);
productRepository.save(product);

Cannot add or update a child row: a foreign key constraint fails spring boot

I have a problem. Please help me.
Partner.java
#Entity
#Table(name = "partners")
#Data
public class Partner {
public Partner() {
this.status = UserStatus.NOT_YET_ACTIVED;
this.createdDate = LocalDateTime.now();
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
private String name;
private String surname;
#NotNull
#Column(unique = true)
private String email;
#NotNull
private String phone;
#Column(columnDefinition="ENUM('MALE', 'FEMALE', 'COMPANY')")
#Enumerated(EnumType.STRING)
private Gender gender;
#Column(name = "country_id")
private Integer countryId;
#Column(name = "city_id")
private Integer cityId;
#Column(name = "street_id")
private Integer streetId;
#Column(columnDefinition="ENUM('ACTIVE', 'NOT_YET_ACTIVED', 'BLOCKED', 'DELETED')")
#Enumerated(EnumType.STRING)
private UserStatus status;
#NotNull
#Column(name = "is_company")
private Integer isCompany;
#Column(name = "created_date")
private LocalDateTime createdDate;
#OneToOne(mappedBy = "partner", cascade=CascadeType.ALL)
private VerificationToken verificationToken;
}
VerificationToken.java
#Entity
#Table(name = "user-token")
#NoArgsConstructor
public class VerificationToken {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Getter
private Long id;
#Getter
private String token;
#OneToOne(targetEntity = Partner.class, fetch = FetchType.EAGER)
#JoinColumn(nullable = false, name = "partner_id", foreignKey = #ForeignKey(name = "FK_VERIFY_USER"))
#Getter
private Partner partner;
#Column(name = "created_date")
#Getter
private LocalDateTime createdDate;
public VerificationToken(final Partner partner) {
this.createdDate = LocalDateTime.now();
this.token = UUID.randomUUID().toString();
this.partner = partner;
}
}
#Override
public PartnerResponseDto save(PartnerRequestDto partnerRequestDto) {
var a = partnerMapper.partnerRequestDtoTOPartner(partnerRequestDto);
VerificationToken v = new VerificationToken(a);
a.setVerificationToken(v);
var partner = ipartnerRepository.save(a);
if (partner == null)
throw new RuntimeException();
return partnerMapper.partnerToPartnerResponseDto(partner);
}
i want to do when partner save succesfuly, then save the verification token and partner id to user-token table.
when i save partner request hibernate response me the following exception
java.sql.SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (ft_partner_and_client_ms.partners, CONSTRAINT FK_VERIFY_USER FOREIGN KEY (id) REFERENCES user-token (id))
I don't understand why I'm getting this error. Please help

Spring Boot Data JPA detached entity passed to persist on #OneToMany and #OneToOne

Problem
I am trying to store an object in my Postgres database. This consists of the Order.class, (List) OrderDevice.class, and a Department.class.
The important thing is that the OrderDevices are always stored new in the DB, but a Department may already exist.
When I try to save the object to my database using save I get the following error message: (shown below)
I get the error message "detached entity passed to persist: com.niclas.model.OrderDevice" if the department does not exist yet, if the department exists the error message looks like this: "detached entity passed to persist: com.niclas.model.Department".
Solution attempts
This solution cannot be used because I do not use bidirectional mapping.
(I don't want to use a bidirectional mapping because I want to access the departments without an order.)
I also tried to change the Cascade types to MERGE like in this solution
I also tried using #Transactional on the method
I also tried to save the children in the database first and then the parent like this:
departmentRepository.save(order.getDepartment()); orderDeviceRepository.saveAll(order.getDevices()); orderRepository.save(order);
I hope I have described my good enough and I am happy about suggestions for solutions
Error.log
The log can be viewed here. (The formatting did not work here)
Order.class
#Entity
#Table(name = "orders")
public class Order extends AuditModel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column(name = "order_id")
private String orderId;
#Column(name = "department_id")
private long departmentId;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "department", referencedColumnName = "id")
private Department department;
#JsonProperty("deviceList")
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "order_id", referencedColumnName = "order_id")
private List<OrderDevice> devices;
#JsonProperty("forename")
#Column(name = "sender_forename")
private String senderForename;
#JsonProperty("surname")
#Column(name = "sender_surname")
private String senderSurname;
#Column(name = "notes", columnDefinition = "TEXT")
private String notes;
#Column(name = "month")
private int month;
#Column(name = "year")
private int year;
public Order() {
}
... Getter/Setters
}
OrderDevice.class
#Entity
#Table(name = "order_devices")
public class OrderDevice extends AuditModel{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column( name = "order_id", insertable = false, updatable = false )
private String orderId;
#Column(name = "device_id")
private long deviceId;
#Column(name = "device_name")
private String deviceName;
#Column(name = "priceName")
private String priceName;
#Column(name = "price")
private double price;
#Column(name = "count")
private int count;
public OrderDevice() {
}
... Getters/Setters
}
Department.class
#Entity
#Table(name = "departments")
public class Department {
//TODO add Form Validation
//TODO better Naming for From Attributes on Frontend and Backend
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column(name = "department_name")
private String department;
#Column(name = "contact_person_forename")
private String forename;
#Column(name = "contact_person_surname")
private String surname;
#Column(name = "contact_person_mail")
private String mail;
#Column(name = "street")
private String street;
#Column(name = "house_number")
private String houseNumber;
#Column(name = "location")
private String location;
#Column(name = "postal_code")
private int postalCode;
#Column(name = "country")
private String country;
#Column(name = "auto_send_invoice")
private boolean autoSend;
#Column(name = "registered")
private boolean registered;
public Department() {
}
... Getter/Setters
}
OrderController.class
#Slf4j
#RestController
public class OrderController {
private final DepartmentRepository departmentRepository;
private final OrderRepository orderRepository;
private final OrderDeviceRepository orderDeviceRepository;
public OrderController(OrderRepository orderRepository, DepartmentRepository departmentRepository,
OrderDeviceRepository orderDeviceRepository) {
this.orderRepository = orderRepository;
this.departmentRepository = departmentRepository;
this.orderDeviceRepository = orderDeviceRepository;
}
#PostMapping("/orders/add")
public ResponseEntity<Order> addDepartment(#RequestBody Order order) throws JsonProcessingException {
order.setOrderId(order.generateOrderId());
DateTime dateTime = new DateTime();
order.setMonth(dateTime.getMonthOfYear());
order.setYear(dateTime.getYear());
order.getDevices().forEach(orderDevice -> {
orderDevice.setOrderId(order.getOrderId());
});
//departmentRepository.save(order.getDepartment());
//orderDeviceRepository.saveAll(order.getDevices());
orderRepository.save(order);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
Update
If the objects are created in this way, no error will occur and the order will be successfully saved in the database.
However, I don't understand why it works this way and not via ObjectMapper. Does anyone know why?
#PostMapping("/orders/add")
public ResponseEntity<Order> addDepartment(#RequestBody JsonNode jsonNode) throws JsonProcessingException {
Order order = new Order();
JsonNode departmentJson = jsonNode.get("department");
Department department;
if ( departmentJson.get("id").canConvertToInt() ) {
department = departmentRepository.findDepartmentById(departmentJson.get("id").asInt());
} else {
department = new Department();
department.setDepartment(departmentJson.get("department").asText());
department.setForename(departmentJson.get("forename").asText());
department.setSurname(departmentJson.get("surname").asText());
department.setMail(departmentJson.get("mail").asText());
department.setStreet(departmentJson.get("street").asText());
department.setHouseNumber(departmentJson.get("houseNumber").asText());
department.setLocation(departmentJson.get("location").asText());
department.setPostalCode(departmentJson.get("postalCode").asInt());
department.setCountry(departmentJson.get("country").asText());
department.setAutoSend(departmentJson.get("autoSend").asBoolean());
department.setRegistered(departmentJson.get("registered").asBoolean());
}
order.setDepartment(department);
order.setOrderId(order.generateOrderId());
order.setDepartmentId(department.getId());
List<OrderDevice> orderDevices = new ArrayList<>();
JsonNode devices = jsonNode.get("deviceList");
for (JsonNode node : devices) {
//TODO replace this mess with objectMapper
if (node.has("count") && node.get("count").asInt() != 0){
OrderDevice device = new OrderDevice();
device.setOrderId(order.getOrderId());
device.setDeviceId(node.get("id").asLong());
device.setDeviceName(node.get("deviceName").asText());
device.setPriceName(node.get("priceName").asText());
device.setPrice(node.get("price").asDouble());
device.setCount(node.get("count").asInt());
orderDevices.add(device);
}
}
order.setDevices(orderDevices);
order.setSenderForename(jsonNode.get("forename").asText());
order.setSenderSurname(jsonNode.get("surname").asText());
order.setNotes(jsonNode.get("notes").asText());
DateTime dateTime = new DateTime();
order.setMonth(dateTime.getMonthOfYear());
order.setYear(dateTime.getYear());
orderRepository.save(order);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
You can try to use instead of orderRepository.save(order) use orderRespostiory.saveOrUpdate(order).

How get 2 SELECT in 1 query with SPRING DATA

I have 3 entities :
#Entity
#Table(name = "copy")
public class Copy {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#Column(name = "format")
private String format;
#Column(name = "status")
private String status;
#ManyToOne
#JoinColumn(name = "book_id")
private Book book;
#ManyToOne
#JoinColumn(name = "library_id")
private Library library;
#Entity
#Table(name = "book")
public class Book implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#Column(name = "title")
private String title;
#Column(name = "pub_date")
private Date pubDate;
#Column(name = "page")
private int page;
#Column(name = "synopsis")
private String synopsis;
//TODO Image à gérer
#Column(name = "cover")
private String cover;
#ManyToOne
#JoinColumn(name = "categorie_id")
private Categorie categorie;
#ManyToOne
#JoinColumn(name = "author_id")
private Author author;
#OneToMany(mappedBy = "book")
List<Copy> copyList = new ArrayList<>();
#Entity
#Table(name = "library")
public class Library implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#Column(name = "nom")
private String nom;
#Column(name = "adress")
private String adress;
#Column(name = "phone_num")
private String phoneNum;
#Column(name = "email")
private String email;
#OneToMany(mappedBy = "library")
private List<Copy> copyList = new ArrayList<>();
I would like to recover the number of copies of a book according to its format and its libraries. However I cannot figure out how to retrieve a list of copies and the total number depending on the format and its library. How can I do. I wrote this request but I can't get what I want.
My request :
#Query("SELECT DISTINCT c, COUNT(c.format) FROM Copy c WHERE c.book.id = :id")
List<Copy> getCopyById(#Param("id") Long id);
first you need to create class to handel query result (copy,total)
public class CopyWithTotal{
Copy c;
int total;
CopyWithTotal(Copy c, int total){
this.c = c;
this.total = total;
}
}
then you should constratc this class in the query
#Query("SELECT new packgeTo.CopyWithTotal(DISTINCT c, COUNT(c.format)) FROM Copy c WHERE c.book.id = :id group by c")
List<CopyWithTotal> getCopyById(#Param("id") Long id);
whenever you use aggregation function like count all selected column shoud apper in the group by

Some parent entity value cannot be saved to child entity

When i save the job entity i was expecting that the child under job should also reflect in the db. The problem is that the ref_id and revision does not contain any value from the db.
here is the result from mysql db (removed confidential data)
This is my Job entity class
#Entity
#Table(name = "job")
public class Job implements Serializable {
private static final long serialVersionUID = -2075866246194059832L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long id;
#Column(name = "ref_id")
private String refId;
#Column(name = "revision")
private int revision;
#Column(name = "appname")
private String appName;
#Column(name = "vendor")
private String vendor;
#Column(name = "version")
private String version;
#Column(name = "locale")
private String locale;
#Column(name = "platform")
private String platform;
#Column(name = "tier")
private String tier;
#Column(name = "category")
private String category;
#Column(name = "functional_tag")
private String functional;
#Column(name = "job_start_date")
private Date jobStartDate;
#Column(name = "author")
private String author;
#Enumerated(EnumType.STRING)
private Status status;
#Column(name = "release_version")
private String releaseVersion;
#OneToMany(mappedBy = "job", cascade = CascadeType.PERSIST)
private List<Task> tasks;
}
And here is the child
#Entity
#Table(name = "task")
public class Task implements Serializable {
private static final long serialVersionUID = -7395753611385528546L;
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "module_name")
private String moduleName;
#Column(name = "start_time")
private Date startTime;
#Column(name = "end_time")
private Date endTime;
#Enumerated(EnumType.STRING)
private Status status;
#Column(name = "machine_ip")
private String machineIp;
#Column(name = "data_center")
private String dataCenter;
#Column(name = "description")
private String description;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinColumns({#JoinColumn(name = "ref_id", referencedColumnName = "ref_id"),
#JoinColumn(name = "revision", referencedColumnName = "revision")})
private Job job;
}
Here is the code that persist the entity
#Service
public class QReaderService {
#Autowired
private JobRepository jobRepository;
#Autowired
private TaskRepository taskRepository;
public boolean addJob(Job job) {
Job previousJob = jobRepository.findByJobRefId(job.getRefId());
if (previousJob == null) {
**jobRepository.save(job);**
return true;
} else {
switch (job.getStatus()) {
case FAILED:
case EXCEPTION:
int revision = 0;
revision += previousJob.getRevision();
previousJob.setStatus(Status.FAILED);
jobRepository.save(previousJob);
break;
}
}
return true;
}
}
This is how i build the job entity
Job job = new Job();
job.setRefId("f78d9as7f98dsa7f97a97f98sda9f7");
job.setAppName("appname");
job.setLocale("locale");
job.setPlatform("platform");
job.setCategory("category");
job.setReleaseVersion("1.1");
job.setStatus(Status.PROCESSING);
job.setAuthor("author");
job.setFunctional("functional");
job.setJobStartDate(new Date());
job.setVersion("1.1");
job.setTier("tier1");
job.setVendor("vendor");
Task task = new Task();
task.setDescription("description");
task.setDataCenter("dataCenter");
task.setStartTime(new Date());
task.setStatus(Status.PROCESSING);
task.setMachineIp("ip");
task.setModuleName("module");
job.setTasks(new ArrayList<Task>(Arrays.asList(task)));
jobRepository.save(job);
To persist child entities when you call save on parent entity, both sides of the relations should be set.
job.setTasks(job.setTasks(new ArrayList<Task>(Arrays.asList(task)));
//Missing line //Important for persistence of child
task.setJob(job);
Hope this helps.

Categories