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")
Related
I have two tables, A and B (not defined by me, inherited from old legacy software), with the following structure:
A: PK (idA, ente),
B: PK(idB, ente), FK(idA, ente) --> A
I need to use #IdClass/#Id, rather than #Embeddeble/#EmbeddebleId, because #GeneratedValue on id columns.
I'd like to insert a new couple of rows this way:
A a = new A();
a.setEnte('E1');
a = repA.save(a);
B b = new B();
b.setA(a);
b = repB.save(b);
I use Spring Data JPA's repositories class. My best try, was:
#Getter
#ToString
#Setter
#Entity
#Table(name = "address")
#IdClass(BPK.class)
#NoArgsConstructor
public class B implements Serializable{
#ManyToOne
#JoinColumns({
#JoinColumn(name="idA", referencedColumnName="idA", nullable=false, insertable=false, updatable=false),
#JoinColumn(name="ente", referencedColumnName="ente", nullable=false, insertable=false, updatable=false)
})
private A a;
#Id
#org.hibernate.annotations.GenericGenerator(name = "incrementGenerator", strategy = "org.hibernate.id.IncrementGenerator")
#GeneratedValue(generator="incrementGenerator")
#Column(name = "idB", columnDefinition = "int(10)", nullable = false)
private Integer idB;
#Id
#Column(insertable=false, updatable=false, unique=true, nullable=false, length=6)
private String ente;
}
#Getter
#ToString
#Setter
#Entity
#Table(name = "Users")
#IdClass(APK.class)
#NoArgsConstructor
public class A implements Serializable {
#Id
#org.hibernate.annotations.GenericGenerator(name = "incrementGenerator", strategy = "org.hibernate.id.IncrementGenerator")
#GeneratedValue(generator="incrementGenerator")
#Column(name = "idA", columnDefinition = "int(10)", nullable = false)
private Integer idA;
#Id
#Column(name = "ente", columnDefinition = "char(6)", nullable = false)
public String ente;
}
This solution doesn't work, because idA and ente are not inserted in save(b) instruction. If I change insertable=true in the #ManyToOne lines, there will be error on ente field.
Any suggestions?
Your b.setA() method could also set this automatically
Thanks to Christian Beikov's suggestion, I found an acceptable solution to the problem.
Why it should not infer data directly from the entities is not clear to me.
However, the solution is to force the missing data to be set before persisting using #PrePersist.
The model for B becomes:
#Getter
#ToString
#Setter
#Entity
#Table(name = "address")
#IdClass(BPK.class)
#NoArgsConstructor
public class B implements Serializable{
#ManyToOne
#JoinColumns({
#JoinColumn(name="idA", referencedColumnName="idA", nullable=false, insertable=false, updatable=false),
#JoinColumn(name="ente", referencedColumnName="ente", nullable=false, insertable=false, updatable=false)
})
private A a;
#Id
#org.hibernate.annotations.GenericGenerator(name = "incrementGenerator", strategy = "org.hibernate.id.IncrementGenerator")
#GeneratedValue(generator="incrementGenerator")
#Column(name = "idB", columnDefinition = "int(10)", nullable = false)
private Integer idB;
#Id
#Column(insertable=false, updatable=false, unique=true, nullable=false, length=6)
private String ente;
//New lines (In the real code I used getters, of course) <--
#Column(name = "idA", nullable = false, columnDefinition = "int(10)")
private Integer idA;
#PrePersist
private void init() {
this.ente = a.ente;
this.idA= a.idA;
}
}
A.class
#Getter
#Entity
#Table(name = "A")
public class A extends JpaEntity<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "aId", nullable = false)
private Long id;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "aId")
#Setter
private Set<relationship_table_between_a_and_b> bOfa;
}
relationship_table_between_a_and_b.class
#Entity
#Table(name = "relationship_table_between_a_and_b")
#Getter
public static relationship_table_between_a_and_b extends JpaEntity<relationship_table_between_a_and_b.Id> {
#EmbeddedId
#Setter
private Id id;
#Embeddable
#Getter
public static class Id implements Serializable {
#Setter
#Column(name="aId", nullable = false)
private Long aId;
#Setter
#OneToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name="bId", nullable = false)
private B b;
}
}
B.class
public static B extends JpaEntity<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "bId", nullable = false)
private Long id;
#OneToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "relationship_table_between_b_and_a",
joinColumns = #JoinColumn(name = "bId", nullable = false),
inverseJoinColumns = #JoinColumn(name = "aId", nullable = false))
#Setter
#JsonIgnoreProperties("bOfa")
private List<A> as;
}
I tried several ways but didn't solve the stackoverflowerror
Could not solve the infinite reference that occurs when as of class B refers to bToa of a again.
I want to solve this problem... please help
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.
I am working on mapping a relationship using a composite key, but the composite key is a separate table.
Car Table
car Id
car description
car Value
SafetyReport Table
safetyReport Id
safetyReport Date
safetyReport Value
CompositeKey CarSafetyReport Table
carid
safetyReportId
One To Many: A car will have many safety reports
JPA:
#Data
#Entity
#Table(name = "CarTable")
public class CarEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "CarId", nullable = false)
private int carId;
#Column(name = "CarDescription", nullable = false)
private String carDescription;
#Column(name = "CarValue", nullable = false)
private String carDescription;
}
#Data
#Entity
#Table(name = "SafetyReport")
public class SafetyReportEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "SafetyReportId", nullable = false)
private int SafetyReportID;
#Column(name = "SafetyReportDate", nullable = false)
private OffsetDataTime date;
#Column(name = "SafetyReportValue", nullable = false)
private String safetyReportValue;
}
#Data
#Entity
#Table(name = "CarSafetyReport")
public class CarSafetyReportEntity implements Serializable {
#EmbeddedId
private CarSafetyReportPk id;
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Embeddable
public class CarSafetyReportPk implements Serializable {
#Column(name = "CarId", nullable = false)
private int carId;
#Column(name = "SafetyReportId", nullable = false)
private int SafetyReportId;
}
I tried
public class CarEntity {
#OneToMany(mappedBy = "id.carId" )
private List<CarSafetyReportEntity> carSafetyReportEntity;
}
I also tried putting the relationship in the composite key CarSafetyReportPk but i got an error for the annotation #OneToMany in a composite key.
I got 2 Entities
Customer.class
#Entity
#Table(name = "Customer")
public class Customer {
#Id
private int id;
#OneToMany(mappedBy = "customer"; fetch = FetchType.EAGER)
private Set<Invoice> invoice;
}
and
Invoice.class
#Entity
#Table(name = "Invoice")
public class Invoice {
#Id
#ManyToOne
#JoinColumn(name = "customer")
private Customer customer;
#Column(name = "price", nullable = false)
private double price;
}
They are both mapped in the persistence.xml.
So:
System.out.println(customer);
for a specific customer gives me 30 Invoice entrys, but I got 33 in the Database.
I use org.eclipse.persistence.jpa 2.5.0 and persistence-api 1.0.2
I appreciate every hint/solution.
Thanks in advance.
Sorry for the late reply
I found the problem/answer.
After just trying #EmbeddedId I had to declare the #JoinColumn insertable and updatable false.
The right mapping for the Invoice.class
#Entity
#Table(name = "Invoice")
public class Invoice {
#EmbeddedId
private InvoiceId invoiceId;
#ManyToOne
#JoinColumn(name = "customerId", insertable = false, updatable = false)
private Customer customer;
#Column(name = "price", nullable = false)
private double price;
}
#Embeddable
class InvoiceId implements Serializable {
//Composite PK
#Column(name = "customerId")
private int customerId;
#Column(name = "date")
private Date date;
}