I have two entities
#Entity
#Table(name = "categories")
public class Category {
#Getter
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "category_id", unique = true, nullable = false)
private long categoryId;
#Getter #Setter
#ManyToMany(mappedBy = "categories", cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
List<Product> products;
#Getter #Setter
#Column(name = "category_name", nullable = false, unique = true)
private String categoryName;
And
#Entity
#Table(name = "products")
public class Product {
#Getter
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "product_id", unique = true, nullable = false)
private long productId;
#Getter #Setter
#Column(name = "price")
private float price;
#Getter #Setter
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "categories_product")
private List<Category> categories;
#Getter #Setter
#Column(name = "product_code", unique = true, nullable = false)
private String productCode;
#Getter #Setter
#Column(name = "product_name", nullable = false)
private String productName;
#Getter #Setter
#Column(name = "description", nullable = false)
private String description;
#Getter #Setter
#Column(name = "short_description", nullable = false)
private String shortDescription;
}
I`m using MapStruct for DTO. When I want to add new product through controller I get the following error:
org.hibernate.PropertyValueException: not-null property references a null or transient value : com.project.shop.models.Category.categoryName
As I understand, hibernate tries to create a new Category, when I want it to use an already existing one in database.
CategoryDTO:
#Getter
#Setter
public class CategoryDto {
private long categoryId;
private String categoryName;
private boolean categoryActive;
}
ProductDTO:
#Getter
#Setter
public class ProductDto {
private String productName;
private String productCode;
private float price;
private String shortDescription;
private String description;
private List<CategoryDto> categories;
}
CategoryMapper:
#Mapper(componentModel = "spring")
public interface CategoryMapper {
CategoryDto toDto(Category category);
List<CategoryDto> toDtos(List<Category> categories);
List<Category> toModels(List<CategoryDto> categoryDtos);
Category toModel(CategoryDto categoryDto);
}
ProductMapper:
#Mapper(uses = {CategoryMapper.class},
componentModel = "spring")
public interface ProductMapper {
ProductDto toDto(Product product);
List<ProductDto> toDtos(List<Product> products);
List<Product> toModels(List<ProductDto> productDtos);
Product toModel(ProductDto productDto);
}
Controller:
#PostMapping("/product")
public ResponseEntity<ProductDto> create(#RequestBody ProductDto productDto) {
productService.save(productMapper.toModel(productDto));
return ResponseEntity.status(HttpStatus.CREATED).body(productDto);
}
productService.save:
public void save(Product product) {
productRepository.save(product);
}
It is not that easy basically. My suggestion (and my implementation) is that you pass only the categoryId with your ProductDTO. And at the service, take this ID, find the respective Category via a Repository and then set the Product's Category to this entity.
Simple example:
public ProductDTO addProduct(ProductDTO newDto) {
Category category = categoryRepo.findById(newDto.getCategory().getId())
.orElseThrow(// something);
Product entity = modelMapper.map(newDto, Product.class); // This does the same thing as your mapper, You can also implement this in your project
entity.setCategory(category );
return modelMapper.map(productRepo.save(entity), ProductDTO.class); // This saves the entity and converts it to a DTO and returns it
}
If you look at the identity, nullable = false option appears to be defined option is defined.
#Entity
#Table(name = "categories")
public class Category {
....
#Getter #Setter
#Column(name = "category_name", nullable = false, unique = true)
private String categoryName;
I think it would be good to look for the categoryName column value of CategoryDto first.
Related
customer
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "customer_id")
private Long customerId;
#NonNull
#Column(name = "name")
private String name;
#NonNull
#Column(name = "address")
private String address;
#NonNull
#Column(name = "house_no")
private String houseNo;
#Column(name = "active")
private boolean active = true;
#NonNull
#Column(name = "customer_type")
private String customerType;
#NonNull
#Column(name = "pack")
private String pack;
#JsonIgnore
#OneToOne(mappedBy = "customer",cascade = CascadeType.ALL)
private Stb stb;
#JsonIgnore
#OneToOne(mappedBy = "customer",cascade = CascadeType.ALL )
private Payment payment;
#JsonIgnore
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
private List<History> history;
}
History
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "history")
public class History {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "history_id")
private Long historyId;
#Column(name = "amount_paid")
private Long AmountPaid;
#LastModifiedDate
#Column(name = "payment_date")
private String paymentDate;
#Column(name = "due")
private Long due;
#JsonIgnore
#ManyToOne(optional = false,fetch=FetchType.LAZY)
#JoinColumn(name = "customer_f_id",referencedColumnName = "customer_id")
private Customer customer;
payment
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "payment")
public class Payment {
public Long normalPrice =220L;
public Long sportsPrice = 250L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "payment_id")
private Long paymentId;
#Nonnull
#Column(name = "amount_paid")
private Long paid;
#Nullable
#LastModifiedDate
#Column(name = "payment_date")
private String paymentDate;
#Nullable
#Column(name = "due")
private Long due;
#JsonIgnore
#OneToOne(fetch=FetchType.EAGER,optional=false)
#JoinColumn(name = "customer_f_id",referencedColumnName = "customer_id")
private Customer customer;
stb
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "stbox")
public class Stb {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "stb_id")
private Long StbId;
#NonNull
#Column(name = "stbox_number")
private String StboxNumber;
#NonNull
#Column(name = "stbox_id")
private String StboxId;
#NonNull
#Column(name = "stbox_cust_number")
private String StboxCustNumber;
#NonNull
#Column(name = "stbox_type")
private String StboxType;
#JsonIgnore
#OneToOne(optional = false,fetch=FetchType.LAZY)
#JoinColumn(name = "customer_f_id",referencedColumnName = "customer_id")
private Customer customer;
im new to springboot , i just assigned foreign key in many ways i watched many tutorials and blogs and tried it out but it all failed , the foreign key is always set to null, anybody help , thanks in advance :) .
im trying to create a foreign key in stb , payment ,history but i refered it correctly but it ssets to null
Are you setting both sides of the joins?
public class Customer {
public void setStb(Stb stb) {
this.stb = stb;
stb.customer = this;
}
}
or outside maybe?
public void setCustomerStb(Customer customer, Stb stb) {
customer.setStb(stb);
stb.setCustomer(customer);
}
The same also applies to the other joins.
I am working on a Spring project and I am using JPA to access database. I have two tables that have #ManyToMany property. And one other table (join table) that keeps the foreign keys of those two tables. I need to retrieve the join table with a given property of one of the tables. I tried to use JPA methods and queries but I couldn't find a solution.
I have an abstract class called Course:
#Data
#NoArgsConstructor
#MappedSuperclass
public abstract class Course {
#Id
#GeneratedValue(generator = "UUID")
#Column(name = "id", nullable = false)
private UUID courseId;
#NotBlank
#Column(name = "course_code", nullable = false)
private String courseCode;
#NotBlank
#Column(name = "course_name", nullable = false)
private String courseName;
#NotNull
#Column(name = "ects", nullable = false)
private Double ects;
public Course(
#JsonProperty("id") UUID id,
#JsonProperty("courseCode") String courseCode,
#JsonProperty("courseName") String courseName,
#JsonProperty("ects") Double ects) {
this.courseId = id;
this.courseCode = courseCode;
this.courseName = courseName;
this.ects = ects;
}
}
I have two database model classes that extends Course:
#Entity
#Data
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
#Table(name = "uni_course")
public class UniCourse extends Course {
#NotNull
#Column(name = "department", nullable = false)
#Enumerated(EnumType.STRING)
private Department department;
#ManyToMany
#JoinTable(
name = "approved_courses",
joinColumns = #JoinColumn(name = "uni_course_id"),
inverseJoinColumns = #JoinColumn(name = "host_course_id"))
private Set<HostCourse> approvedCourses;
public UniCourse(
#JsonProperty("id") UUID id,
#JsonProperty("courseCode") String courseCode,
#JsonProperty("courseName") String courseName,
#JsonProperty("ects") Double ects,
#JsonProperty("department") Department department) {
super(id, courseCode, courseName, ects);
this.department = department;
this.bilkentCredit = bilkentCredit;
}
}
and,
#Entity
#Data
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
#Table(name = "host_course")
public class HostCourse extends Course {
#NotBlank
#Column(name = "syllabus", nullable = false)
private String syllabus;
#NotBlank
#Column(name = "web_page", nullable = true)
private String webPage;
#NotNull
#Column(name = "university_id", nullable = false)
private UUID universityId;
#ManyToMany(mappedBy = "approvedCourses")
private Set<UniCourse> uniCourses;
public HostCourse(
#JsonProperty("id") UUID id,
#JsonProperty("courseCode") String courseCode,
#JsonProperty("courseName") String courseName,
#JsonProperty("ects") Double ects,
#JsonProperty("syllabus") String syllabus,
#JsonProperty("webPage") String webPage,
#JsonProperty("universityId") UUID universityId) {
super(id, courseCode, courseName, ects);
this.syllabus = syllabus;
this.webPage = webPage;
this.universityId = universityId;
this.courseApproval = courseApproval;
}
}
I want to get approvedCourses using universityId of HostCourse. How can I achieve this?
I tried using the findByApprovedCoursesUniversityId(UUID universityId) method and it returned the following:
{
"data": [
{
"courseCode": "CS315",
"courseName": "Programming Languages",
"ects": 5.0,
"department": "CS",
"courseId": "8a890716-aaf1-40bb-81b2-e0a638b878dd",
"approvedCourses": []
}
]
"timestamp": "2022.12.15 21.48.32",
"status": "OK"
}
I also want to get approved courses but it returns an empty array.
**Exception is happening when trying to find simple diff but its not throwing exception if I change it to MappingStyle.BEAN **
**.withMappingStyle(MappingStyle.BEAN)**
JaversException ENTITY_INSTANCE_WITH_NULL_ID: Found Entity instance
'Employee' with null Id-property 'id' at
org.javers.core.metamodel.type.EntityType.getIdOf(EntityType.java:115)
at
org.javers.core.metamodel.type.EntityType.createIdFromInstance(EntityType.java:122)
#SuperBuilder
#MappedSuperclass
#Data
#EqualsAndHashCode(of= "id")
#EntityListeners(AuditingEntityListener.class)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
#NoArgsConstructor
public abstract class BaseEntity {
#DiffIgnore
#Id
#Column(name = "id", columnDefinition = "uuid",updatable=false, unique=true, nullable=false)
#GeneratedValue
private UUID id;
#DiffIgnore
#Column(name="\"createDateTime\"", updatable=false, nullable=false)
#CreationTimestamp
private LocalDateTime createDateTime;
#DiffIgnore
#LastModifiedBy
#Column(name="\"createUser\"", updatable=false, nullable=false, length=100)
private String createUser;
#DiffIgnore
#Column(name="\"modifiedDateTime\"", nullable=false)
#UpdateTimestamp
private LocalDateTime modifiedDateTime;
#DiffIgnore
#LastModifiedBy
#Column(name="\"modifiedUser\"", nullable=false, length=100)
private String modifiedUser;
}
#Data
#EqualsAndHashCode(callSuper = true)
#SuperBuilder
#AllArgsConstructor(access = AccessLevel.PRIVATE)
#TypeName("Employee")
public class Employee extends BaseEntity {
public Employee(String name) {
super();
this.name = name;
}
private String name;
private String position;
private int salary;
private int age;
private Employee boss;
#Builder.Default
private List<Employee> subordinates = new ArrayList<>();
private Address primaryAddress;
private Set<String> skills;
}
#Test
void compareTwoEntitiesTest() {
//given
Javers javers = JaversBuilder.javers()
.withListCompareAlgorithm(ListCompareAlgorithm.LEVENSHTEIN_DISTANCE)
.build();
UUID id = UUID.randomUUID();
Employee oldEntity = Employee.builder()
.id(id)
.name("Frodo")
.age(40)
.position("Townsman")
.salary(10_000)
.boss(new Employee("Gandalf"))
.primaryAddress(new Address("Shire"))
.skills(Set.of("management"))
.subordinates(List.of(new Employee("Sam")))
.createDateTime(LocalDateTime.now())
.createUser("ch")
.build();
Employee newEntity = Employee.builder()
.id(id)
.name("Frodo")
.age(41)
.position("Hero")
.salary(12_000)
.boss(new Employee("Gandalf"))
.primaryAddress(new Address("Mordor"))
.skills(Set.of("management", "agile coaching"))
.subordinates(List.of(new Employee("Sam"), new Employee("Sméagol")))
.createDateTime(LocalDateTime.now().plusMinutes(1))
.createUser("zy")
.build();
//when
Diff diff = javers.compare(oldEntity, newEntity);
System.out.println(diff.prettyPrint());
//then
assertThat(diff.getChanges()).hasSize(7);
I'm having problem with mapping two classes with composite keys.
The first class is Product:
#Entity
#Table(name = "Products")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#SuperBuilder
public class Product {
#EmbeddedId
private ProductKey prodPK;
#Column(name = "name", length = 50, nullable = false)
private String name;
#Column(name = "description", length = 80)
private String description;
#Column(name = "totalStock", columnDefinition = "double(8,2) default 0")
private double totalStock;
#ManyToOne
#JoinColumn(name = "companyId", referencedColumnName = "id", nullable = false)
private Company company;
}
With this #EmbeddedId:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#Embeddable
public class ProductKey implements Serializable {
#Column(name = "sku", length = 50)
private String sku;
#Embedded
private LotKey lot;
}
At the same time, this embedded class has as part of its composite key another composite key "LotKey"
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#Embeddable
public class LotKey implements Serializable {
#Column(name = "lot")
private String lot;
#ManyToOne
#JoinColumn(name = "company", referencedColumnName = "id")
private Company company;
}
which belongs to the class:
#Entity
#Table(name = "Lots")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#SuperBuilder
public class Lote {
#EmbeddedId
private LotKey lotpk;
#Column(name = "stock")
private double stock;
#Column(name = "expirationDate", columnDefinition = "default current_timestamp()")
private Date expirationDate;
}
But I'm having trouble referencing to them:
#Entity
#Table(name = "quantityProduct")
public class QuantityProduct{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#ManyToOne
#JoinColumns({
#JoinColumn(
name = "sku",
referencedColumnName = "sku"),
#JoinColumn(
name = "lot")
})
private Product product;
#Column(name = "quantity", columnDefinition = "double(8,2) default 0")
private double quantity;
}
I am getting the following error
image
Thank you so much !
In QuantityProduct, set also referencedColumnName in
#JoinColumn(
name = "lot")
I use two class as models to build a JSON :
The productCreateRequestModel:
#Getter #Setter
public class ProductCreateRequestModel {
private Long id;
private String name;
private double price;
private int qty;
private String imgPath;
private CategoryRequestCreateProductModel category;
}
My CategoryRequestCreateProductModel
#Getter #Setter
public class CategoryRequestCreateProductModel {
private Long id;
private String name;
private String categoryKeyId;
}
I created 2 entities to manage categories and products.
#Entity
#Table(name="products")
#Getter #Setter
public class ProductEntity {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String productKeyId;
// many to one relationship with category
#ManyToOne
#JoinColumn(name = "category_id")
private CategoryEntity category;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private double price;
#Column(nullable = false)
private int qty;
private String imgPath;
}
And :
#Entity
#Table(name="categories")
#Getter #Setter
public class CategoryEntity {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(length = 30, nullable = false)
private String categoryKeyId;
#Column(nullable = false)
private String name;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name="parent_id", nullable=true)
private CategoryEntity parentCategory;
// allow to delete also subcategories
#OneToMany(mappedBy="parentCategory", cascade = CascadeType.ALL)
private List<CategoryEntity> subCategories;
//Here mappedBy indicates that the owner is in the other side
#OneToMany(fetch = FetchType.EAGER, mappedBy = "category", cascade = CascadeType.REMOVE)
private List<ProductEntity> products;
}
This generated this table in the database.
In my controller:
public ProductRestResponseModel createProduct(#RequestBody ProductCreateRequestModel productCreateRequestModel) throws Exception {
ProductRestResponseModel returnValue = new ProductRestResponseModel();
if(productCreateRequestModel.getName().isEmpty() || productCreateRequestModel.getPrice() <= 0)
throw new ApplicationServiceException(ErrorMessages.MISSING_REQUIRED_FIELDS.getErrorMessage());
ModelMapper modelMapper = new ModelMapper();
ProductDto productDto = modelMapper.map(productCreateRequestModel, ProductDto.class);
ProductDto createdProduct = productService.createProduct(productDto);
returnValue = modelMapper.map(createdProduct, ProductRestResponseModel.class);
return returnValue;
}
In my Service I use the DTO:
#Override
public ProductDto createProduct(ProductDto productDto) {
ProductDto returnValue = new ProductDto();
if (productRepository.findByName(productDto.getName()) != null)
throw new ApplicationServiceException("Record already in Database");
ModelMapper modelMapper = new ModelMapper();
ProductEntity productEntity = modelMapper.map(productDto, ProductEntity.class);
String productKeyId = utils.generateProductKeyId(30);
productEntity.setProductKeyId(productKeyId);
ProductEntity storedProduct = productRepository.save(productEntity);
returnValue = modelMapper.map(storedProduct, ProductDto.class);
return returnValue;
}
My issue is when I send a post request with this object :
{
"name": "Pizza",
"price": 344.0,
"qty": 15,
"imgPath": "new/pathImage",
"category": {
"categoryKeyId": "23ume70Fu6yqyGUWfQkW110P4ko3gZ",
"name": "CatName"
}
}
When i send this request I obtain an error message : org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.app.ws.io.entity.ProductEntity.category -> com.app.ws.io.entity.CategoryEntity
My problem is that the Category already exists in the database and that i just need to set the foreign key in the product table