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!
Related
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);
}
}
Here is my database structure:
When I make a REST call to user_product with an intent to update the product value I keep on getting a null pointed exception. My assumption is that the problem lies in ServiceImpl though no matter what I change the error still consists.
My serviceImpl:
public void update(UserProductVO userProductVO) {
UserProduct userProduct = new UserProduct();
userProduct.setId(new UserProductId(userProductVO.getProduct().getId(), userProductVO.getUser().getId()));
userProduct.setUser(userProductVO.getUser());
userProduct.setProduct(userProductVO.getProduct());
UpdatedProduct updatedProduct = new UpdatedProduct(userProductVO.getAmountOfNewProducts());
updatedProductRepository.save(updatedProduct);
userProduct.getUpdatedProducts().add(updatedProduct);
userProductRepository.save(userProduct);
Product product = productRepository.getById(userProductVO.getProduct().getId());
product.setAmount(product.getAmount() + userProductVO.getAmountOfNewProducts());
productRepository.save(product);
}
Eror log says that the problem is here:
userProduct.getUpdatedProducts().add(updatedProduct)
I would appreciate any kind of clue where I might be messing up. Thanks in advance.
Edit:
My UserProduct class:
#Entity
#Table(name = "user_product")
public class UserProduct {
#EmbeddedId
private UserProductId id;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("userId")
#JsonBackReference(value = "userJ")
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("productId")
#JsonBackReference(value = "productJ")
private Product product;
#OneToMany(
mappedBy = "userProduct",
cascade = CascadeType.ALL,
orphanRemoval = true
)
#JsonManagedReference(value = "userProductJ")
private List<UpdatedProduct> updatedProducts;
public UserProduct() {
}
public UserProduct(UserProduct user, UserProduct product, int amountOfNewProducts) {
}
public UserProduct(User user, Product product, List<UpdatedProduct> updatedProducts) {
this.user = user;
this.product = product;
this.updatedProducts = updatedProducts;
}
..getters/setters/hashcode/equals
Edit2:
My UpdatedProduct class:
#Entity
#Table(name = "updated_product")
public class UpdatedProduct {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "amount_of_new_products")
private int amountOfNewProducts;
#Column(name = "updated_on")
private Date updatedOn = new Date();
#ManyToOne(fetch = FetchType.LAZY)
#JsonBackReference(value = "userProductJ")
private UserProduct userProduct;
public UpdatedProduct() {
}
public UpdatedProduct(int amountOfNewProducts) {
this.amountOfNewProducts = amountOfNewProducts;
}
You would have to initialize the UserProduct class with an empty list, not null:
public class UserProduct {
private List<UpdatedProduct> updatedProducts = new ArrayList<>();
// rest of the fields
}
All the annotations were omitted for brevity.
When inserting a record using post request foreign key related reference record is not linking.
#RestController
#RequestMapping("auth")
public class PatientController {
#Autowired
private PatientService patientService;
#PostMapping(value = "patient/register", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public String registerPatient(#RequestBody Patient patient) {
String response = patientService.registerPatient(patient);
return "{'result':" + response + "}";
}
}
#Service
public class PatientService {
#Autowired
private PatientRepository patientRepo;
public String registerPatient(Patient patient) {
patient = patientRepo.save(patient);
}
}
#Repository
public interface PatientRepository extends CrudRepository<Patient, Integer> {
}
Entity Classes:
#Entity
#Table(name = "patient")
public class Patient implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "patient_id")
private int patientId;
#Column(name = "patient_name", length = 200)
private String patientName;
#Column(name = "problem", length = 200)
private String problem;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "doctor_id", nullable = false, insertable = false, updatable = false)
private Doctor doctor;
}
#Entity
#Table(name = "doctor")
public class Doctor implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "doctor_id")
private int doctorId;
#Column(name = "doctor_name", length = 200)
private String doctorName;
#Column(name = "department", length = 200)
private String department;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "doctor")
private Set<Patient> patients = new HashSet<Patient>(0);
}
Database - Doctor Table:
doctor_id doctor_name department
12345678 Dfirstname Dlastname ENT
POST Request - JSON Body
{
"patientName":"Pfirstname Plastname",
"problem:"visibility problem - difficulty in low light",
"doctor":{"doctorId":"12345678"}
}
When I am sending this request the patient table doctor_id column is not being populated with the docortId.
at first glance (as service layer is not provided) You have to remove insertable=false and updatable=false from #JoinColumn
#JoinColumn(name = "doctor_id", nullable = false, insertable = false, updatable = false)
change this to:
#JoinColumn(name = "doctor_id", nullable = false)
As this directives doesn't let jpa to insert/update the DOCTOR_ID column
Also I prefer using werappers over primitive type as #Id change int to Integer as suggested here Using wrapper Integer class or int primitive in hibernate mapping
Also it seems that you have already persisted doctor (as it has already assigned id) you should firstly select doctor to db and add patient to it with both ends:
public void assignToDoctor(Doctor doctor) {
doctor.patients.add(this);
this.doctor = doctor;
}
here is full example:
public static void main(String[] args) {
SpringApplication.run(DemostackApplication.class, args);
}
#Component
public static class AppRunner implements ApplicationRunner {
#Autowired
MainService mainService;
#Override
public void run(ApplicationArguments args) throws Exception {
Doctor doctor = new Doctor();
doctor.department = "a";
doctor.doctorName = "Covid19 Ninja";
doctor = mainService.saveDoctor(doctor);
Patient patient = new Patient();
patient.patientName = "test";
patient.problem = "test";
patient.assignToDoctor(doctor);
Patient newPatient = mainService.savePatient(patient);
}
}
#Service
public static class MainService {
#Autowired
DoctorRepo doctorRepo;
#Autowired
PatientRepo patientRepo;
#Transactional
public Doctor saveDoctor(Doctor doctor) {
return doctorRepo.save(doctor);
}
#Transactional
public Patient savePatient(Patient patient) {
return patientRepo.save(patient);
}
}
#Entity
#Table(name = "patient")
public static class Patient implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "patient_id")
private Integer patientId;
#Column(name = "patient_name", length = 200)
private String patientName;
#Column(name = "problem", length = 200)
private String problem;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "doctor_id", nullable = false)
private Doctor doctor;
public void assignToDoctor(Doctor doctor) {
doctor.patients.add(this);
this.doctor = doctor;
}
}
#Entity
#Table(name = "doctor")
public static class Doctor implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "doctor_id")
private Integer doctorId;
#Column(name = "doctor_name", length = 200)
private String doctorName;
#Column(name = "department", length = 200)
private String department;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "doctor")
private Set<Patient> patients = new HashSet<Patient>(0);
}
I have not used getter/setters but you should :)
EDIT
your registerPatient() logic should be something like this:
#Transactional
public String registerPatient(Patient patient) {
Integer doctorId= patinet.getDoctor().getId();
//fetch the doctor from database
Doctor doctor = doctorRepository.findById(doctorId).orElseThrow(() -> new RuntimeException("doctor not found"));
//create bidirectional reference between patient and doctor
patient.setDoctor(doctor);
doctor.getPatients().add(patient);
//save patient
patient = patientRepo.save(patient);
return "OK";
}
I make method (acceptUseroffermapping) in a REST-controller (UserOfferController) in which I want to delete record in the DB (UserOfferMapping table). But the problem is that record not deleted and relation also saved after I run this method.
I have also UserOfferMapping class which maps to User class. In UserOfferController I manipulate with UserOfferMapping: creating, selecting records from DB and also trying to delete records but have fail.
UserOfferController.java:
/*...*/
#POST
#RequestMapping("/acceptUserOfferMapping")
public void acceptUseroffermapping(#RequestBody Map<String,
String> body) throws ParseException {
String userId = body.get("userId");
String offerId = body.get("offerId");
Optional<User> user = userRepository.findById(Integer.parseInt(userId));
UserOfferMapping mapping = userOfferMappingRepository.getById(Integer.parseInt(userId));
user.get().getUserOfferMapping().remove(mapping);
userRepository.save(user.get());
userOfferMappingRepository.deleteById(Integer.parseInt(offerId));
}
/*...*/
User.java:
/*some imports*/
#Entity
#Table(name = "User")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
/* ...
* a lot of fields
* ...
*/
// Important section which describes all Role Project and Skill mapping
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<UserUserrolemapping> userrolemapings = new HashSet<>();
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<Userprojectmapping> userprojectmappings = new HashSet<>();
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<UserOfferMapping> userOfferMapping = new HashSet<>();
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#OrderBy
private Set<Userskillmapping> userskillmappings = new HashSet<>();
/* ...
* a lot of fields too
* ...
*/
/* getter and setters */
}
UserOfferMappingRepository.java:
public interface UserOfferMappingRepository extends JpaRepository<UserOfferMapping, Integer> {
public List<UserOfferMapping> getAllByUser(Optional<User> user);
public UserOfferMapping getUserOfferMappingByUserAndProjectAndUserRole(User user, Userproject userproject, Userrole userrole);
public UserOfferMapping getById(int id);
public void deleteById(int id);
}
UserOfferMapping.java:
#Entity
public class UserOfferMapping {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
#JoinColumn(name = "userid")
#JsonBackReference
private User user;
#ManyToOne
#JoinColumn(name = "roleid")
private Userrole userRole;
#ManyToOne
#JoinColumn(name = "projectid")
private Userproject project;
#Column(name = "fromdate", nullable = true)
private Date fromdate;
#Column(name = "todate", nullable = true)
private Date todate;
#Column(name = "chance")
private int chance;
#Column(name = "percent")
private int percent;
public int getId() {
return id;
}
public User getUser() {
return user;
}
public Userrole getUserRole() {
return userRole;
}
public Userproject getProject() {
return project;
}
public Date getFromdate() {
return fromdate;
}
public int getChance() {
return chance;
}
public int getPercent() {
return percent;
}
public void setId(int id) {
this.id = id;
}
public void setUser(User user) {
this.user = user;
}
public void setUserRole(Userrole userRole) {
this.userRole = userRole;
}
public void setProject(Userproject project) {
this.project = project;
}
public void setFromdate(Date fromdate) {
this.fromdate = fromdate;
}
public void setChance(int chance) {
this.chance = chance;
}
public void setPercent(int percent) {
this.percent = percent;
}
public void setTodate(Date todate) {
this.todate = todate;
}
public Date getTodate() {
return todate;
}
}
Can you try to use this
public interface UserOfferMappingRepository extends JpaRepository<UserOfferMapping, Integer> {
public List<UserOfferMapping> getAllByUser(Optional<User> user);
public UserOfferMapping getUserOfferMappingByUserAndProjectAndUserRole(User user, Userproject userproject, Userrole userrole);
public UserOfferMapping getById(int id);
// public void deleteById(int id);
#Modifying(clearAutomatically = true)
#Query(value = "Delete from UserOfferMapping c WHERE c.id=:id")
public void deleteById(#Param("id") int id);
}
So, you have bidirectional entity association.
Try to add mapping.setUser(null); before userRepository.save.
Persisting and deleting objects requires a transaction in JPA so that is why you have to define #Transactional annotation before the method in Repository for example `
#Transactional
public void deleteById(#Param("id") int id);
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;
}