I'm coding for fun, and i was wondering what would be the best approach (clean code) for storing entities with multiple relations (OneToMany, ManyToMany, OneToOne etc...)
I have three Entitiy classes: Product, Shop, ShopProducts.
A Shop has #OneToOne ShopProducts, and ShopProducts has #ManyToMany List products (see below).
For creating a shop (with or without products for that shop), i do this
public Shop createShop(ShopDto shopDto) {
if(shopRepository.findShopByCity(shopDto.getCity()).isPresent()) {
throw new IllegalArgumentException("A shop already exists with that name.");
}
/* Persist shopProducts instance */
Shop shop = new Shop();
ShopProducts shopProducts = new ShopProducts();
if(shopDto.getProducts() != null) {
shopProducts.setAvailableProducts(shopDto.getProducts().getAvailableProducts());
}
ShopProducts save = shopProductsRepository.save(shopProducts);
shop.setCity(shopDto.getCity());
shop.setStoreName(shopDto.getStoreName());
shop.setProducts(save);
return shopRepository.save(shop);
}
Adding a product to an existing shop
public Shop addProductsToShop(String city, List<ProductDto> products) {
Shop shop = shopRepository.findShopByCity(city)
.orElseThrow(() -> new RuntimeException("Invalid city"));
products.stream()
.filter(p -> productRepository.findByProductId(p.getProductId()).isPresent())
.forEach(product -> {
Product p = productRepository.findByProductId(product.getProductId()).get();
shop.getProducts().addProductToShop(p);
shopProductsRepository.save(shop.getProducts());
shopRepository.save(shop);
});
return shop;
}
--- MODEL CLASSES ---
Product class
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private BigDecimal price;
private Boolean available;
private String productId;
private String imageUrl;
private ProductType productType;
}
Shop class
public class Shop implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String storeName;
#Column(unique = true)
private String city; /* Only one store per city */
#OneToOne
#JoinColumn(name = "shop_products_id", referencedColumnName = "id")
private ShopProducts products;
}
ShopProduct class
public class ShopProducts implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "shop_product",
joinColumns = {#JoinColumn(name = "shop_product_id")},
inverseJoinColumns = {#JoinColumn(name = "product_id")})
private List<Product> availableProducts = new ArrayList<>();
public void addProductToShop(Product product) {
availableProducts.add(product);
}
}
Related
I have 3 entity classes: User, Product and Fridge. The Fridge is something between User and Product. I mean that in the Fridge I store the ID of the Product, the id of the User and some quantity. And I need to see the IDs that are(stored) used for relation.
User Entity Class:
#Entity
#Table(name = "`user`")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#NotNull
#Column(name = "username")
private String username;
#NotNull
#Column(name = "password")
private String password;
#OneToMany(mappedBy="user")
#JsonManagedReference(value = "user-fridge")
private List<Fridge> fridge;
}
Product Entity Class :
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#Column(name = "name")
private String name;
#Column(name = "measurementUnit")
private Enum measurementUnit;
#Column(name = "calories")
private int calories;
#OneToMany(mappedBy="product")
#JsonManagedReference(value = "ingredients-products")
private List<Ingredients> ingredients;
#OneToMany(mappedBy="product")
#JsonManagedReference(value = "fridge-product")
private List<Fridge> fridge;
}
Fridge Entity Class :
#Entity(name = "Fridge")
#Table(name = "fridge")
public class Fridge{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#ManyToOne
#JoinColumn(name="user_id")
#JsonBackReference(value = "user-fridge")
private User user;
#ManyToOne
#JoinColumn(name="product_id")
#JsonBackReference(value = "fridge-product")
private Product product;
#Column(name = "quantity")
private int quantity;
#Transient
private DateFormat dform = new SimpleDateFormat("dd/MM/yy");
#Transient
private Date intermediar = new Date();
#Column(name = "added_at")
private String added_at = dform.format(intermediar);
}
What I get is something like this :
"fridge": [
{
"id": "79baae3e-8189-4ebb-8a40-2116a77693b8",
"quantity": 25,
"added_at": "25/08/21"
}
But I need the id's of the product and user as well.
How should I structure my model to get that?
I want to store recipes and ingredients in db. I came up with these entities:
#Entity
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String notes;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
private List<IngredientQuantity> ingredientQuantities = new ArrayList<>();
}
#Entity
public class Ingredient {
#Id
private String name;
#OneToMany(mappedBy = "ingredient", cascade = CascadeType.ALL)
private List<IngredientQuantity> ingredientQuantities = new ArrayList<>();
}
#Entity
public class IngredientQuantity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "fk_recipe")
private Recipe recipe;
#ManyToOne
#JoinColumn(name = "fk_ingredient")
private Ingredient ingredient;
private double quantity;
}
Now when I want to create a new recipe, I would have to check every ingredient if they already exist in db or not. Is this approach any good? And if it is fine, do I make a one call to db to fetch all ingredients using "WHERE (...) IN ()" clause or hit db with multiple single queries?
I am unable to solve this error. java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'ordered_songs.UK_q73sm6y7ob1asdtqty7nxbj2j'
I have a many to many relationship annotation with Order and Song, which points to collection type elements. I can successfully add one order, but after that my app breaks when trying to add a second order, and I cannot seem to figure out why. It looks as though order id is not being incremented when it's called in the controller.
Here is my Order POJO
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "ord")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToMany
#JoinTable(name = "ordered_songs", joinColumns = #JoinColumn(name = "song_id"), inverseJoinColumns = #JoinColumn(name = "order_id"))
#ElementCollection
private List<Song> orderedSongs = new ArrayList<>();
#ManyToOne
#JoinColumn(name = "customer_id")
private Customer customer;
private String status;
private Date date;
#Embedded
private MailingAddress mailingAddress;
}
The second POJO is
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Embeddable
public class Song {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String title;
private String description;
private String artist;
private String genre;
private String format;
private double price;
private String imageUrl;
#ManyToMany(mappedBy = "orderedSongs")
#ElementCollection
#ToString.Exclude
private List<Order> orders = new ArrayList<>();
// private Order order;
}
Here is my controller
#PostMapping("/mailing")
public ModelAndView saveMailingAddress(#RequestParam("street") String street,
#RequestParam("city") String city,
#RequestParam("state") String state,
#RequestParam("zip") int zip,
ModelMap model) {
Customer c=(Customer) model.get("customer");
MailingAddress ma=new MailingAddress();
ma.setStreet(street);
ma.setCity(city);
ma.setState(state);
ma.setZip(zip);
Order o=new Order();
o.setStatus("ordered");
o.setDate(new Date());
c.getOrders().add(o);
o.setCustomer(c);
o.setMailingAddress(ma);
Order o1 = orderDao.save(o);
System.out.println("first msg b4 for loop");
for(Song s: cs.getSongs()) {
System.out.println("b4 add to o1");
o1.getOrderedSongs().add(s);
System.out.println("b4 save");
songDao.save(s);
System.out.println("after save");
}
System.out.println("after loop");
cs.removeAll();
return new ModelAndView("success");
}
What am I doing wrong here?
I have two entities lets call them Categories and Products. These two entities are mapped by a many to many relationship.
My problem is that i am trying to get category information from products. Trying this results in empty categories.
This is my code :
PersistenceEntity
#MappedSuperclass
public class PersistenceEntity implements Serializable {
private static final long serialVersionUID = 4056818895685613967L;
// Instance Variables
#Id
#Column(unique = true)
#GeneratedValue(strategy = GenerationType.TABLE)
protected Long id;
#JsonIgnore
#Temporal(javax.persistence.TemporalType.TIMESTAMP)
protected Date creationDate = new Date();
...Getters and Setters omitted for brevity
}
Category
#Entity
#Table(name = "category")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Category extends PersistenceEntity{
private static final long serialVersionUID = 1L;
#Column(nullable = false)
private String categoryName;
#Column(nullable = false)
private Boolean active;
#Column(nullable = true)
private String picture;
#JsonIgnore
private MetaData metadata;
#ManyToMany(fetch = FetchType.EAGER,mappedBy = "categories")
private Set<Product> products;
...Getters and Setters omitted for brevity
}
Product
#Entity
#Table(name = "products",uniqueConstraints = { #UniqueConstraint(columnNames = "productCode")})
#JsonIgnoreProperties(ignoreUnknown = true)
public class Product extends PersistenceEntity {
private static final long serialVersionUID = 8727166810127029053L;
#Column(name = "product_name")
private String name;
private String productImageUrl;
#JsonIgnore
#ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
#JoinTable(name="category_products",
joinColumns={#JoinColumn(name="product_id", unique = false)},
inverseJoinColumns={#JoinColumn(name="category_id", unique = false)})
private Set<Category> categories;
...Getters and Setters omitted for brevity
}
ProductServiceImplementation
#Service
public class ProductService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
private ProductRepository productRepository;
public List<Product> getProductsByShopId( Long id) {
List<Product> productList = new ArrayList<>();
productList = productRepository.findByShopId(id);
return productList;
}
public Set<Long> getCategoryIds(List<Product> products){
Set<Long> categoriesIDs = new HashSet<Long>();
for (Product product : products) {
product.getCategories().forEach(category -> {
categoriesIDs.add(category.getId());
});
}
return categoriesIDs;
}
}
The problem is getting the categoryIds that are mapped to the list of products.
How can i get CategoryIds from Product. My getCategoryIds function returns empty always
public Set<Long> getCategoryIds(List<Product> products){
Set<Long> categoriesIDs = new HashSet<Long>();
for (Product product : products) {
product.getCategories().forEach(category -> {
categoriesIDs.add(category.getId());
});
}
return categoriesIDs;
}
I have two table Orders and OrderItem as below:
#Entity
#Table(name = "orders")
public class Order implements Serializable {
#Id
#GeneratedValue
private Integer orderID;
#Column(nullable = false)
private Date orderDate;
#ManyToOne(cascade = CascadeType.ALL)
private User user;
#OneToMany(cascade = CascadeType.ALL)
private Set<OrderItem> orderItems = new HashSet<OrderItem>();
public Order() {
}
public Order(Date orDate, User currentUser) {
this.orderDate = orDate;
this.user = currentUser;
}
public Set<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(Set<OrderItem> orderItems) {
this.orderItems = orderItems;
}
//getter/setters for orderDate, user
And
#Entity
public class OrderItem implements Serializable {
#Id
#GeneratedValue
private Integer id;
#ManyToOne(cascade = CascadeType.ALL)
private Book book; // F.K to Book table
#Column(nullable = false)
private int quantity;
#Column(nullable = false)
private double totalPrice;
public OrderItem() {}
public OrderItem( Book currentBook, int qty, double totalPrice) {
this.book = currentBook;
this.quantity = qty;
this.totalPrice = totalPrice;
}
//getter/setters
And here i initialize them to store in database:
#Transactional
public void storeOrderInDB() {
order = new Order(currentDate(), currentUser); //date,user
orderService.addOrder(order);
OrderItem orderItem = new OrderItem();
orderItem.setBook(currentBook);
orderItem.setQuantity(qty);
orderItem.setTotalPrice(getTotalCost(qty, unitPrice));
orderItemService.addOrderItem(orderItem);
}
This is the result:
Orders:
OrderID: 5, OrderDate: "2015-04-25 23:11:16", userId: 1
OrderItem:
id:2 , quantity:1 , totalPrice:5000 , bookId:5 , order_orderID: null
Why order-orderID is null?
I need it to be not null.
In Order Entity You have declared
#OneToMany(cascade = CascadeType.ALL)
private Set<OrderItem> orderItems = new HashSet<OrderItem>();
Above declaration says that you foreign-key is maintain by Order Entity ,
To resolve your problem
#Transactional
public void storeOrderInDB() {
Order order = new Order(currentDate(), currentUser); //date,user
OrderItem orderItem = new OrderItem();
orderItem.setBook(currentBook);
orderItem.setQuantity(qty);
orderItem.setTotalPrice(getTotalCost(qty, unitPrice));
//orderItemService.addOrderItem(orderItem);
Set<OrderItem> orderItemSet=order.getOrderItems();
orderItemSet.add(orderItem);
order.setOrderItems(orderItemSet);
orderService.addOrder(order);
}
Hope this will resolve your problem
Thanks!