Error: Duplicate key value violates uniqueness constraint - java

When trying to send a request, with the same "flower_id", to Postman, returns 500 with message:
"could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement."
At the same time, it does not matter if the same ids are in the same request or in different ones with different users, if one flower has already been added earlier, it is no longer possible to add it to another user.
Entity Order:
import javax.persistence.*;
import java.time.LocalDate;
import java.util.List;
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private LocalDate orderCreateDate;
private LocalDate orderCompleteDate;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
#ManyToMany
private List<Flower> flower;
private Integer price;
public Order() {
}
public Order(LocalDate orderCreateDate, LocalDate orderCompleteDate, User user, List<Flower> flower) {
this.orderCreateDate = orderCreateDate;
this.orderCompleteDate = orderCompleteDate;
this.user = user;
this.flower = flower;
}
//Getters and setters
}
Entity Flower:
import javax.persistence.*;
#Entity
#Table(name = "flowers")
public class Flower {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer price;
public Flower() {
}
public Flower (String name, Integer price) {
this.name = name;
this.price = price;
}
//Getters and Setters
}
OrderService:
import com.learning.flowershop.Entity.Order;
import com.learning.flowershop.Repositories.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.List;
#Service
public class OrderService {
private final OrderRepository orderRepository;
#Autowired
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public List<Order> getAllOrdersByUserId(Long userId) {
return orderRepository.findAllByUserId(userId);
}
#Transactional
public void saveOrder(Order order) {
orderRepository.save(order);
}
}

Did you check the constraints in your database? The 500 error indicates an internal server error. It seems like there might be a unique constraint in your relation table which causes an SQL exception. If this exception is not properly caught it will get rethrown as an internal server error.

I still don't fully understand why this is the case, but I still want to leave a solution to my question.
It was only worth adding a save method for User
public User saveUser(User user) {
return userRepository.save(user);
}
Which is strange, because before that all my users were quietly saved.

Related

How can I fix Spring boot POST API's 500 error?

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

Spring JPA joint table doesn't return all fields

I created two tables -student and subject.
Then I created a third table student_subject, which is a joint table to connect students with subjects. It has 5 fileds: row id, student id, subject id and the created_at and updated_at fields (which comes from the AuditModel class):
package com.rest.web.postgresapi.data.models;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
#Entity
#Table(uniqueConstraints = {
#UniqueConstraint(columnNames = {"student_id", "subject_id"})
})
public class StudentSubject extends AuditModel {
#Id
#GeneratedValue(generator = "enrollment_generator")
#SequenceGenerator(
name = "enrollment_generator",
sequenceName = "enrollment_sequence",
initialValue = 4420
)
private Long id;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "student_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private Student student_id; // If I put private Long student_id, it fails
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "subject_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private Subject subject_id; // If I put private Long subject_id, it fails
// Constructors
protected StudentSubject() {}
public StudentSubject(Student student_id, Subject subject_id) {
this.student_id = student_id;
this.subject_id = subject_id;
}
// Getters
public Long getId() {
return id;
}
public Student getStudent_id() {
return student_id;
}
public Subject getSubject_id() {
return subject_id;
}
// Setters
public void setId(Long id) {
this.id = id;
}
public void setStudent_id(Student student) {
this.student_id = student;
}
public void setSubject_id(Subject subject) {
this.subject_id = subject;
}
}
The application perfectly creates the tables in the database and I can get and post in the student and subject tables. No problem with that. The pain comes with the controller for the joint table.
This is the controller for the student_subject joint table table
package com.rest.web.postgresapi.controllers;
import com.rest.web.postgresapi.data.models.StudentSubject;
import com.rest.web.postgresapi.repository.StudentSubjectRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
#RestController
public class StudentSubjectController {
#Autowired
private StudentSubjectRepository studentSubjectRepository;
#GetMapping("/enrollments")
public List<StudentSubject> getAllStudentsSubjects(){
return studentSubjectRepository.findAll();
}
#PostMapping("/enrollments/student/subject")
public StudentSubject createStudentSubject(#Valid #RequestBody StudentSubject studentSubject) {
return studentSubjectRepository.save(studentSubject);
}
}
There are two problems:
1 .- when I do the get from the student_subject table, It only retrieves the id of the row and the created_at and updated_at fields. No student_id nor subject_id.
response from get
2 .- when I do the post (from postman) to insert a row, I got the following error:
Detail: Failing row contains (4671, 2018-11-20 11:04:34.176, 2018-11-20 11:04:34.176, null, null).
I provide both student_id and subject_id, as you can see at this screenshot from postman, but the error clearly states both fields are null:
postman post
It seems that my definition of the table is somehow wrong. What am I missing in my code?
Spring MVC uses Jackson to serialize/deserialize Java objects to/from JSON.
If you annotate an attribute with #JSONIgnore then Jackson will ignore it.
This is the reason why you don't see the student_id field or the subject_id field in your JSON response of the GET method. Because Jackson is ignoring them when converts from object to json.
And this is the reason why your POST fails. Because Jackson is ignoring the received attributes. Jackson is creating an empty entity and JPA is saving the entity without student_id and subject_id.
Solved by replacing
#JsonIgnore
with
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
as indicated in this answer

Spring Boot Rest and JPA Deserializing Nested Entities with Jackson

I am new to Spring Boot, Jackson, and JPA. I am attempting to build an RESTful application. From my understanding of the Spring Boot documentation, much of the work of deserializing data and returning JSON is "magically" handled by the Spring framework (when the appropriate starter/dependencies included).
My Controller endpoint is simple.
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.midamcorp.roominspections.models.*;
import com.midamcorp.roominspections.service.ItemService;
#RestController
#RequestMapping("/item")
public class ItemController {
#Autowired
private ItemService itemService;
#RequestMapping(method= RequestMethod.GET)
public List<Item> getItems() {
return itemService.findAll();
}
#RequestMapping(method=RequestMethod.GET, value="/{id}")
public Item getItem(#PathVariable int id) {
return itemService.findOne(id);
}
#RequestMapping(method=RequestMethod.GET,value="/page/{pageNumber}")
public List<Item> getItemsByPage(#PathVariable int pageNumber) {
return itemService.findByPage(pageNumber);
}
}
However, my classes (JPA entities) exhibit some relationships. In example:
import java.util.Set;
import javax.persistence.*;
#Entity
#Table(name="Items")
public class Item {
public Item() {}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int ItemID;
#ManyToOne(optional=false)
#JoinColumn(name="CommentCategoryID", insertable = false, updatable = false)
private CommentCategory commentCategory;
#ManyToOne(optional=false)
#JoinColumn(name="ItemCategoryID", insertable = false, updatable = false)
private ItemCategory itemCategory;
#OneToMany(mappedBy="item",targetEntity=ReportDetail.class)
private Set<ReportDetail> detail;
private String ItemName;
private String ItemDescription;
private String ItemType;
#Column(name="PageNumber")
private int pageNumber;
private int PossPoints;
private int CommentCategoryID;
private int ItemCategoryID;
// getters and setters follow
}
Whenever, I attempt to access the item endpoint, I receive the following error:
Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: could not deserialize; nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not deserialize (through reference chain: java.util.ArrayList[0]->com.midamcorp.roominspections.models.Item["commentCategory"]->com.midamcorp.roominspections.models.CommentCategory["comments"]->org.hibernate.collection.internal.PersistentSet[0]->com.midamcorp.roominspections.models.Comment["reportDetails"])
Now I understand the issue lies in deserializing the data (through Jackson), and the problem "ends" with the reportDetails property of the Comment entity. However, I have inspected both the Comment and ReportDetails entities and could find nothing amiss.
Comment
import java.util.Set;
import javax.persistence.*;
#Entity
#Table(name="Comments")
public class Comment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int CommentID;
public Comment() {}
#OneToMany(mappedBy="comment", targetEntity=ReportDetail.class)
private Set<ReportDetail> reportDetails;
#ManyToOne(optional=false)
#JoinColumn(name="CommentCategoryID", insertable = false, updatable = false)
private CommentCategory commentCategory;
private int CommentCategoryID;
private String CommentBody;
// getters and setters
}
ReportDetail
import javax.persistence.*;
#Entity
#Table(name="ReportDetailRows")
public class ReportDetail {
public ReportDetail() {}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int ReportRowID;
private int ReportID;
private int ItemID;
private int CommentID;
private int PointsDeducted;
private String ItemIMG;
#ManyToOne(optional=false)
#JoinColumn(name="CommentID", insertable = false, updatable = false)
private Comment comment;
#ManyToOne(optional=false)
#JoinColumn(name="ReportID", insertable = false, updatable = false)
private ReportSummary summary;
#ManyToOne(optional=false)
#JoinColumn(name="ItemID", insertable = false, updatable = false)
private Item item;
// getters and setters
}
Repository interfaces (extending the CrudRepository) and a Service layer are used.
The following is an example service:
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.midamcorp.roominspections.models.Item;
import com.midamcorp.roominspections.models.ItemRepo;
#Service
#Transactional
public class ItemServiceImpl implements ItemService {
#Autowired
private ItemRepo itemRepo;
#Override
public List<Item> findAll() {
return (List<Item>) itemRepo.findAll();
}
#Override
public List<Item> findByPage(int pageNumber) {
return itemRepo.findByPageNumber(pageNumber);
}
#Override
public Item findOne(int id) {
return itemRepo.findOne(id);
}
}
In my experience with .NET, I would have to create DTOs (using a library such as AutoMapper) to flatten the entities for transfer. However, from my review of the Jackson documentation, it seemed as if the library handles such flattening automatically (note, I realize it is not a good practice to transfer such large volumes of data; as I am new to the technologies, I simply want to get it working first) . If DTOs are required, where would they be situated? For example, in the Controller, Service, Repository, etc?
I truly appreciate any advice. I have searched around but have been unable to find a solution for my particular problem.

java.sql.SQLException: ORA-00942: table or view does not exist with JPA entityManager.createQuery()

I am using JPA createquery API to fetch the data.
Here is my query data
#PersistenceContext
EntityManager entityManager;
#Override
public List<String> fetchAllReleaseNumbers() {
Query query = entityManager.createQuery("SELECT release FROM ReleaseModel", String.class);
return query.getResultList();
}
and here is my pojo class.
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "dbname.tablenamefromDB")
public class ReleaseModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "dbcolumnname", unique = true, nullable = false)
private String release;
#Column(name = "dbcolumnname")
private String releaseDesc;
#Column(name = "dbcolumnname")
private Integer releaseStatus;
#Column(name = "dbcolumnname")
private Integer releaseMode;
public String getRelease() {
return release;
}
public void setRelease(String release) {
this.release = release;
}
public String getReleaseDesc() {
return releaseDesc;
}
public void setReleaseDesc(String releaseDesc) {
this.releaseDesc = releaseDesc;
}
public Integer getReleaseStatus() {
return releaseStatus;
}
public void setReleaseStatus(Integer releaseStatus) {
this.releaseStatus = releaseStatus;
}
public Integer getReleaseMode() {
return releaseMode;
}
public void setReleaseMode(Integer releaseMode) {
this.releaseMode = releaseMode;
}
}
Though the table exists in db its throwing not exist.Any ideas where I made mistake.
I tried whether any aliases can be given to the table name.
I am using pojo class name only for createQuery.
TIA.
You should specify a schema name by this way
#Table(schema = "dbname", name = "tablenamefromDB")
You have an incorrect mapping:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "dbcolumnname", unique = true, nullable = false)
private String release;
I think String can't be auto generated.
Also all your columns have dbcolumnname name.
The issue was that the schema was not specified in the entity class or the user did not login using proxy. If the user login using a proxy access i.e. userName[schemaName] they do not need to specify schema in the entity class. But if the user login using just the userName, they need to specify the schema in the entity. This is to specify where the table can be found in the database.

Unable to display data from relationships in templates

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).

Categories

Resources