JPA - Join another entity by EmbeddedId with 2 columns - spring

Basically. I have a ProductEntity, like this:
#Entity
#Table(name = "product", schema = "shop")
public class ShopProductEntity {
#EmbeddedId
private ProductEntityId id;
#Column(name = "product_name")
private String name;
#Column(name = "product_category_id")
private int categoryId;
#Column(name = "product_language_id")
private int languageId;
// TODO: Get category by categoryId and languageId.
private CategoryEntity category;
I have another CategoryIdentity:
#Entity
#Table(name = "category", schema = "shop")
public class CategoryEntity {
#EmbeddedId
private CategoryEntityId id;
#Column(name = "category_name")
private String name;
#Column(name = "category_url")
private String url;
It has an EmbeddedId like this:
#Embeddable
public class CategoryEntityId implements Serializable {
#Column(name = "category_id", nullable = false)
private int categoryId;
#Column(name = "language_id", nullable = false)
private int languageId;
public int getCategoryId() {
return categoryId;
}
public int getLanguageId() {
return languageId;
}
Now, every product has a category. Categories are unique by their id and language. The shop connects to a category by both the categoryId and languageId columns. How do I add the CategoryEntity to my ProductEntity so I can use the category's url value for my product?
I tried adding this to ShopProductEntity:
#ManyToOne
#JoinColumns({
#JoinColumn(name="categoryId", referencedColumnName="categoryId"),
#JoinColumn(name="languageId", referencedColumnName="languageId"),
})
private CategoryEntity category;

Related

HOW JOIN two tables JPQL

How create a join pageable USING JPQL in Class Movie with MovieLanguage?
The Class Movie as a relationship 1 to 1 with MovieLanguage.
Movie:
Entity
#Table(name = "tb_movie")
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private Double score;
private Integer count;
private String image;
public Movie() {}
public Movie(Long id, String title, Double score, Integer count, String image) {
this.id = id;
this.title = title;
this.score = score;
this.count = count;
this.image = image;
}
#OneToOne(cascade = CascadeType.ALL, mappedBy = "movie")
#JsonManagedReference
MovieLanguage movieLanguage;
/* getter and setter */
}
MovieLanguage:
#Entity
#Table(name = "tb_movie_language")
public class MovieLanguage {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String titleBrazil;
private String titleSpanish;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "movie_id")
#JsonBackReference
private Movie movie;
public MovieLanguage(){}
/* getter and setter */
}
Here is a JPQL example in MovieJoinRepository, but not working:
MovieJoinRepository:
public interface MovieJoinRepository extends JpaRepository<Movie, Long> {
#Query("SELECT obj FROM Movie obj INNER JOIN obj.movie_language mm WHERE obj.id = mm.id ")
Page<String> findAllPaged(Pageable pageable);
}
Thank you for you help!

JPA ManyToMany fails with "must have same number of columns as the referenced primary key"

I have three tables customer, product and sales. sales is the join table to store the customer's products as shown below:
customer, product and Sales tables
My Entities defined as described below:
Customer.java
#Entity
#Table(name="customer")
public class Customer {
#Id
#Column(name="c_id")
private String customerId;
#Column(name="customer_name")
private String customerName;
#ManyToMany
#JoinTable(
name = "sale",
joinColumns = #JoinColumn(name = "c_id"),
inverseJoinColumns = #JoinColumn(name = "p_id"))
private Set<Product> customerProducts = new HashSet<>();
}
Product.java
#Entity
#Table(name="product")
public class Product {
#Id
#Column(name="p_id")
private String productId;
#Column(name="product_name")
private String productName;
#Column(name="price")
private Double price;
// ... Setters & Getters
}
Sales.java
#Entity
#Table(name="sales")
public class Sales {
#EmbeddedId
private SalesPK salesId;
#Column(name="qty")
private Long qty;
// ... Setters & Getters
}
SalesPK.java
#Embeddable
public class SalesPK implements Serializable {
#Column(name = "c_id")
private String customerId;
#Column(name = "p_id")
private String productId;
public SalesPK() {}
public SalesPK(String customerId, String productId) {
this.customerId = customerId;
this.productId = productId;
}
}
CustomerRepository.java
#Repository
public interface CustomerRepository extends CrudRepository<Customer, String> {
#Query("select customer from Customer customer " +
"left join fetch customer.customerProducts " +
"where customer.customerName = :customerName")
public Customer getCustomerPurchasedProducts(String customerName);
}
My Spring boot application fail to start with following exception:
org.hibernate.MappingException: Foreign key (FK7wwx8x75009xqb1y0tawm8rty:SALES [p_id])) must have same number of
columns as the referenced primary key (SALES [c_id,p_id])
What am I missing here? I have followed the notes as described here in https://www.baeldung.com/jpa-many-to-many
UPDATE:
There is no issue with above solution, I have misspelled "sales" table in #ManyToMany declaration changing from "sale" to "sales" fixed the issue. Strange behavior why it didn't compline about missing table instead it complain about actual composite primary key definition.
Following code Fixed the issue:
#ManyToMany
#JoinTable(
name = "sales",
joinColumns = #JoinColumn(name = "c_id"),
inverseJoinColumns = #JoinColumn(name = "p_id"))
private Set<Product> customerProducts = new HashSet<>();
}
I would map these classes a bit differently:
#Entity
#Table(name="customer")
public class Customer {
#Id
#Column(name="c_id")
private String customerId;
#Column(name="customer_name")
private String customerName;
#OneToMany(mappedBy = "customer")
private Set<Sale> customerSales = new HashSet<>();
}
#Entity
#Table(name="product")
public class Product {
#Id
#Column(name="p_id")
private String productId;
#Column(name="product_name")
private String productName;
#Column(name="price")
private Double price;
}
#Entity
#Table(name="sales")
public class Sales {
#EmbeddedId
private SalesPK salesId;
#MapsId("customerId") // maps customerId attribute of embedded id
#ManyToOne
Customer customer;
#MapsId("productId") // maps productId attribute of embedded id
#ManyToOne
Product product;
#Column(name="qty")
private Long qty;
// ... Setters & Getters
}
#Embeddable
public class SalesPK implements Serializable {
#Column(name = "c_id")
private String customerId;
#Column(name = "p_id")
private String productId;
public SalesPK() {}
public SalesPK(String customerId, String productId) {
this.customerId = customerId;
this.productId = productId;
}
}

Can I get the list of objects in relation #OneToMany?

I've got 2 classes: Device and Category. 1 Device can have 1 assigned category, but 1 category can have assigned many different devices.
#Entity
#Data
#Table(name = "devices")
public class Device implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#Column(name="amount_of_items")
private Integer amountOfItems;
private BigDecimal price;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "category_id")
private Category category;
public Device(String name, String description, Integer amountOfItems, BigDecimal price, Category category){
this.name = name;
this.description = description;
this.amountOfItems = amountOfItems;
this.price = price;
this.category = category;
}
public Device() {}
}
#Entity
#Data
#Table(name = "categories")
public class Category implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#OneToMany(mappedBy = "category", fetch = FetchType.EAGER)
private List<Device> devices = new ArrayList<>();
public Category(String name, String description){
this.name = name;
this.description = description;
}
public Category() { }
}
Can I get the actual list of devices for one Category? The below code returns me a null list of devices:
Category category = new Category("Urzadzenia AGD", "tylko dla klientow premium");
categoryRepository.save(category);
Device device = new Device("pralka", "samoobslugowa", 50, new BigDecimal("220"),
category);
deviceRepository.save(device);
System.out.println(category.getDevies()) ---> returns NULL
Can I do it by calling a getter like above?
save method already return value after save in Database you can use this
Category category = new Category("Urzadzenia AGD", "tylko dla klientow premium");
category= categoryRepository.save(category);
Device device = new Device("pralka", "samoobslugowa", 50, new BigDecimal("220"),
category);
deviceRepository.save(device);
System.out.println(category.getDevies())
and you must be make setter and getter method in your class
after this you have problem stackover flow exciption becouse the all device called category and categore call Devices
you can used #JsonIgnore annotation
Like this :
#OneToMany(mappedBy = "category", fetch = FetchType.EAGER)
#JsonIgnore
private List<Device> devices = new ArrayList<>();

Persisting data with JPA with existing datas

I want to persist an object like this one :
{
"paymentMode": "CREDIT CARD",
"totalAmount": 158.0,
"orderProducts": [
{
"productKeyId": "HycaR7sPeecIMZEewanuK0jzPo7S33",
"name": "Cornish crab salad, brown crab mayonnaise, toasted muffin",
"price": 20.0,
"qty": 1,
"imgPath": "pathImage",
"category": {
"categoryKeyId": "23ume70Fu6yqyGUWfQkW110P4ko3gZ",
"name": "Starter"
}
},...
],
"seller": {
"userKeyId": "qmNR5g2TD8Ja5KvA1DCQWzYj55nvbP",
"firstName": "David",
"lastName": "Vera",
"email": "david.vera#9online.fr",
"addresses": [
{
"addressKeyId": "2t7x0bFgP5B9Qb2ymnLL5aPZVwMFhJ",
"city": "Vancouver",
"country": "Canada",
"streetName": "123 street name",
"postalCode": "ABCCBA",
"type": "billing",
},...
]
},
"createdAt": "2019-10-22T09:48:06.000+0000"
}
Some object are already stored in Database such as seller, addresses, products and product category.
I created : Orders tables
#Entity
#Table(name="orders")
#Getter #Setter
public class OrderEntity implements Serializable {
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(nullable = false, unique = true)
private String orderKeyId;
// A sale belong to one seller
#ManyToOne(fetch = FetchType.LAZY)
private UserEntity seller;
private String paymentMode;
private double totalAmount;
#OneToMany(mappedBy = "pk.order")
#Valid
private List<OrderProductEntity> orderProducts;
}
An order_product table (pivot table):
#Entity
#Table(name="order_product")
#Getter #Setter
public class OrderProductEntity {
#EmbeddedId
#JsonIgnore
private OrderProductPK pk;
#Column(nullable = false)
private Integer qty;
// default constructor
public OrderProductEntity() {
super();
}
public OrderProductEntity(OrderEntity order, ProductEntity product, Integer quantity) {
pk = new OrderProductPK();
pk.setOrder(order);
pk.setProduct(product);
this.qty = quantity;
}
#Transient
public ProductEntity getProduct() {
return this.pk.getProduct();
}
...
}
And a product table
#Entity
#Table(name="products")
#Getter #Setter
public class ProductEntity implements Serializable {
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = 1L;
#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;
#JsonManagedReference
#OneToMany(mappedBy = "pk.product", fetch = FetchType.EAGER)
#Valid
private List<OrderProductEntity> orderProducts;
}
The userEntity class :
#Entity
#Table(name = "users")
#Getter #Setter
public class UserEntity implements Serializable {
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(nullable = false, unique = true)
private String userKeyId;
#Column(nullable = false, length = 50)
private String firstName;
#Column(nullable = false, length = 50)
private String lastName;
#Column(nullable = false, length = 120, unique = true)
private String email;
#Column(nullable = false)
private String encryptedPassword;
private String emailVerificationToken;
// column definition do not work for all database engine. So set the value to false is the same
// #Column(nullable = false, columnDefinition = "boolean default false")
#Column(nullable = false)
private Boolean emailVerificationStatus = false;
// One user can have Many Addresses
#OneToMany(mappedBy = "userDetails", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JsonManagedReference
private List<AddressEntity> addresses;
}
And finally the embedded class :
#Embeddable
#Getter #Setter
#ToString
public class OrderProductPK implements Serializable {
private static final long serialVersionUID = 476151177562655457L;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "order_id")
private OrderEntity order;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "product_id")
private ProductEntity product;
...
}
I created a controller method to persist orders in a MySQL database:
public OrderRest createOrder(#RequestBody OrderRequestModel orderRequestModel) throws Exception {
OrderRest returnValue = new OrderRest();
ModelMapper modelMapper = new ModelMapper();
OrderDto orderDto = modelMapper.map(orderRequestModel, OrderDto.class);
OrderDto createdOrder = orderService.createOrder(orderDto);
returnValue = modelMapper.map(createdOrder, OrderRest.class);
// 5. Return the expected object
return returnValue;
}
I have an orderDTO object that contains several fields (including ID).
public class OrderDto implements Serializable {
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = 1L;
private Long id;
private String orderKeyId;
private String paymentMode;
private double totalAmount;
private List<ProductDto> orderProducts;
private UserDto seller;
private Date createdAt;
}
My productDTO object
public class ProductDto implements Serializable {
// ommit this member and do not generate getter / setter
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = 1L;
private Long id;
private String productKeyId;
private String name;
private double price;
private int qty;
private String imgPath;
private CategoryDto category = new CategoryDto();
}
The UserDto :
#Getter #Setter
#ToString
public class UserDto implements Serializable {
// ommit this member and do not generate getter / setter
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = 1L;
private long id;
private String userKeyId;
private String firstName;
private String lastName;
private String email;
private String password;
private String encryptedPassword;
private String emailVerificationToken;
private Boolean emailVerificationStatus = false;
private List<AddressDto> addresses;
// private List<RoleDto> roles;
}
the controller is calling my service layer :
#Override
public OrderDto createOrder(OrderDto orderDto) {
// create a new order
ModelMapper modelMapper = new ModelMapper();
OrderEntity orderEntity = modelMapper.map(orderDto, OrderEntity.class);
String orderKeyId = utils.generateOrderKeyId(30);
orderEntity.setOrderKeyId(orderKeyId);
orderEntity.setCreatedAt(orderDto.getCreatedAt());
orderEntity.setPaymentMode(orderDto.getPaymentMode());
orderEntity.setTotalAmount(orderDto.getTotalAmount());
// set the seller
UserEntity userEntity = modelMapper.map(orderDto.getSeller(), UserEntity.class);
orderEntity.setSeller(userEntity);
List<OrderProductEntity> orderProductEntities = new ArrayList<>();
// set the products
for (int i = 0; i < orderDto.getOrderProducts().size(); i++) {
ProductDto productDto = orderDto.getOrderProducts().get(i);
OrderProductEntity orderProductEntity = modelMapper.map(orderDto.getOrderProducts().get(i), OrderProductEntity.class);
orderProductEntities.add(orderProductEntity);
orderDto.getOrderProducts().set(i, productDto);
}
orderEntity.setOrderProducts(orderProductEntities);
OrderEntity storedOrder = orderRepository.save(orderEntity);
OrderDto returnValue = modelMapper.map(storedOrder, OrderDto.class);
return returnValue;
}
I have 3 issues :
I save the order in the database but the seller is not persisted
In the order product table datas are not persisted.
And i obtain an error message in my rest response :
"trace": "org.modelmapper.MappingException: ModelMapper mapping errors: Converter org.modelmapper.internal.converter.CollectionConverter#685b36d6 failed to convert java.util.List to java.util.List.error...

SQL error or missing database / OneToMany SQLIte-Spring Boot

I have a problem with the relationship oneToMany. I created tables in SQLite DB, this is my tables:
My CategoryModel:
#Entity
#Table(name = "Category")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class CategoryModel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String category_name;
private String category_description;
private String image_path;
#JsonIgnore
#OneToMany( mappedBy = "category")
private Set<ProductModel> category;
My ProducCategory:
#Entity
#Table(name = "Product_Category")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class ProductModel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long product_id;
private Long category_id;
private String name;
private String description;
private int numberOfProduct;
private String image;
private int price;
#ManyToOne
#JoinColumn(name = "country_id", nullable = false)
private CategoryModel category;
I can get data from the Category table well but when I call data from the Product_Category table I have the error:
SQL error or missing database (no such column: productmod0_.country_id)
country_id does not exist anywhere in your tables.
What you want is : #JoinColumn(name = "category_id", nullable = false)

Resources