How to insert values into PostConstruct - spring-boot

I have a H2 in-memory-database with 4 tables.
The table Shop has a connection to Address table.
Now I want to insert some values in my PostConstruct method.
So do my table definitions look like:
Shop:
#NotBlank
#Size(min = 2, max = 50)
#Column(name ="name")
private String name;
#NotNull
#ManyToOne
#JoinColumn(name ="address")
private Address address;
#ElementCollection
#JoinTable(name = "shop_products")
private List<Product> products;
Address
#NotBlank
#Size(min = 2, max = 50)
#Column(name ="address", unique = true)
private String address;
Product
#NotBlank
#Size(min = 2, max = 50)
#Column(name ="name", unique = true)
private String name;
#NotBlank
#Size(min = 2, max = 50)
#Column(name ="manufacturer")
private String manufacturer;
#NotNull
#Column(name ="currentPrice")
private BigDecimal currentPrice;
And these are my Service classes with the PostConsutrct method:
Address
#PostConstruct
public void initDb() {
Address address1 = Address.builder().address(AddressEnum.BURGENLAND.toString()).build();
Address address2 = Address.builder().address(AddressEnum.WIEN.toString()).build();
Address address3 = Address.builder().address(AddressEnum.TIROL.toString()).build();
addressRepository.saveAll(List.of(address1, address2, address3));
}
Product
#PostConstruct
public void initDb() {
Product product1 = Product.builder().name(ProductEnum.HAMMER.toString()).currentPrice(new BigDecimal(10)).manufacturer("Makita").build();
Product product2 = Product.builder().name(ProductEnum.KNIFE.toString()).currentPrice(new BigDecimal(20)).manufacturer("Bosch").build();
Product product3 = Product.builder().name(ProductEnum.NAIL.toString()).currentPrice(new BigDecimal(30)).manufacturer("Hilti").build();
productRepository.saveAll(List.of(product1, product2, product3));
}
Shop
#PostConstruct
public void initDb() {
Shop shop1 = Shop.builder().name("Shop 1").address(new Address(AddressEnum.BURGENLAND.toString())).build();
Shop shop2 = Shop.builder().name("Shop 2").address(new Address(AddressEnum.BURGENLAND.toString())).build();
Shop shop3 = Shop.builder().name("Shop 3").address(new Address(AddressEnum.BURGENLAND.toString())).build();
shopRepository.saveAll(List.of(shop1, shop2, shop3));
}
If I delete the PostConstruct in my ShopService class, it works, but then I do not have any data in my Shop table.
Whereas running my application with the PostConstruct, I always get the following error message:
TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation
The problem is I do not know how to get rid of the error message?

What I did change now was the following:
I added cascade = CascadeType.ALL in my Shop model class.
#NotNull
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name ="address")
private Address address;
Now, it works.

Related

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<>();

org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException

This is my orderservice implementation for creating and saving orders here with the customerid I'm getting customer and customer has a cart and in the cart product list is there and for that product list, I should create an order.
public Order save(int custid) {
Optional<Customer> cust = customerRepo.findById(custid);//here customer is there and inside customer cart is there inside cart medicine list is there.
Cart ct= cust.get().getCart();//getting cart from customer
if(ct.getMedicineList().size()!=0) {//create order only if the medicine list is not empty. there are 8 fields **orderDate,dispatchDate,status,medicineList,totalCost,customer and orderId(GeneratedValue)** I can't set the orderId cuz it is auto generated.
LocalDate todaysDate = LocalDate.now();
LocalDate dispatchdate = todaysDate.plusDays(3);
List<Medicine> orderList= new ArrayList<Medicine>();
List<Medicine> cartList= new ArrayList<Medicine>();
cartList=ct.getMedicineList();
orderList.addAll(cartList);
Order ord = new Order();
ord.setCustomer(cust.get());
ord.setMedicineList(orderList);
ord.setDispatchDate(dispatchdate);
ord.setOrderDate(todaysDate);
ord.setStatus("Placed");
ord.setTotalCost((float)ct.getTotalAmount());
logger.info("Add order to the database");
return orderRepository.save(ord);
}
return null;
}
this is my order controller
#PostMapping("/order/{custid}")
public ResponseEntity<Order> addOrder(#Valid #PathVariable("custid") int custid) {
logger.info("Add order in database");
return new ResponseEntity<>(orderService.save(custid), HttpStatus.OK);
}
this is my medicine Entity
#Data
#RequiredArgsConstructor
#ToString
#Table(name = "medicine")
#Entity
public class Medicine {
#Id
#Column(name = "medicine_id", nullable = false)
#NonNull
#GeneratedValue
private int medicineId;
#NonNull
#Size(min = 3, message = "Minimum charecters in medicine name should be 3.")
#NotEmpty
#Column(unique = true, name = "medicine_name", nullable = false)
private String medicineName;
#NonNull
#Column(name = "medicine_cost", nullable = false)
private float medicineCost;
#NonNull
#Column(name = "mfd", nullable = false)
private LocalDate mfd;
#NonNull
#Column(name = "expiry_date", nullable = false)
private LocalDate expiryDate;
#NonNull
#Column(name = "medicine_quantity", nullable = false)
private int medicineQuantity = 1;
#NonNull
private String medicineCategory;
#NonNull
private String medicineDescription;
#JsonIgnore
#ManyToMany(cascade = CascadeType.ALL, mappedBy = "medicineList",fetch = FetchType.EAGER)
private List<Order> orderList;
}
this is my order Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class Order {
#Id
#Column(name = "orderId")
#GeneratedValue
private int orderId;
#NonNull
private LocalDate orderDate;
#ManyToMany(targetEntity = Medicine.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "ord_med", joinColumns = { #JoinColumn(name = "ord_id") }, inverseJoinColumns = {
#JoinColumn(name = "med_id") })
private List<Medicine> medicineList = new ArrayList<>();
#NonNull
private LocalDate dispatchDate;
#NotEmpty
private float totalCost;
#NonNull
private String status;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="c_ord_fk",referencedColumnName = "customerId")
#NonNull
private Customer customer;
}
here when i try to create order for the list inside cart it gives me [36m.m.m.a.ExceptionHandlerExceptionResolver[0;39m [2m:[0;39m Resolved [org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction]
i'm not sure but i think its because of the id but it is autogenerated. Idk how to create an order object for the list of products or is this the correct way to do it.
Actually I found the answer, all I have to do is when you use ManyToMany mapping the cascade type must be cascade = {CascadeType.MERGE,CascadeType.REFRESH} instead of {cascade = CascadeType.ALL} and it works fine. reference JPA: detached entity passed to persist: nested exception is org.hibernate.PersistentObjectException

How to write custom findAll() with Specification in JPA repository

When I use skuRepository.getAll(), it works OK, but when I apply filters, defined in Specification (List filteredRegs = skuRepository.getAll(specification)) I still get all the rows of the table
What should i do to apply the specifications to my custom method?
public interface SkuRepository extends CrudRepository<Sku, Integer>, JpaSpecificationExecutor<Sku> {
#Query("select s from Sku s join fetch s.unit un join fetch s.supplier sup WHERE un.id = sku_unit_id AND sup.id = supplier_id")
List<Sku> getAll(#Nullable Specification<Sku> var1);
#Query("select s from Sku s join fetch s.unit un join fetch s.supplier sup WHERE un.id = sku_unit_id AND sup.id = supplier_id")
List<Sku> getAll();
}
UPD:
Here is my entities.
When I make sampling by a Sku table using the Specification API, I see three separate selects in log: one for Sku entity, one for Unit and one for Suppliers. I want my app to make one select with joins.
I read that this is due to the fact that I use EAGER fetch type, so I change it to LAZY, but then I got another problem: "InvalidDefinitionException: No serializer found..." This is logical because related entities Unit and Supplier are not loaded.
Then I decided to write my custom getAll() with request:
#Query("select s from Sku s join fetch s.unit un join fetch s.supplier sup WHERE un.id = sku_unit_id AND sup.id = supplier_id ORDER BY s.name")
But now it does not support Specification.
Please advise what to do.
#Entity
#Table(name = "sku")
public class Sku implements Cloneable, CloneableEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "sku_code", length = 6, nullable = false, unique = true)
private String code;
#Column(name = "sku_name", nullable = false)
private String name;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "sku_unit_id", nullable = false)
private Unit unit;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "supplier_id", nullable = false)
private Supplier supplier;
#Column(name = "qty_in_sec_pkg")
private int quantityInSecondaryPackaging;
#Column(name = "sku_is_active", nullable = false)
private boolean isActive;
//constructors, getters, setters
}
#Entity
#Table(name = "units")
public class Unit {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id ")
private int id;
#Column(name = "unit", nullable = false, unique = true)
private String unit;
#Column(name = "description")
private String description;
//constructors, getters, setters
}
#Entity
#Table(name = "suppliers")
public class Supplier {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id ")
private int id;
#Column(name = "supplier_code", length = 6, nullable = false, unique = true)
private String supplierCode;
#Column(name = "supplier_name", nullable = false)
private String name;
#Column(name = "create_date", length = 19, nullable = false)
private String createDate;
#Column(name = "update_date", length = 19)
private String updateDate;
//constructors, getters, setters
}
You can't mix #Query and Specification
You can only use JpaSpecificationExecutor interface methods to use Specification.
Find more details here

Suggestion for implementation of search filter with many2many relationship between two entites

I want to implement /search rest method that will filter my Product object for the given parameters and return me a pageable set of products that are filtered.
I was reading about Specification interface and Criteria API but i am having difficulties in implementing the solution.
Product entity:
#Entity
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long productId;
#NotEmpty(message = "The product name must not be null.")
private String productName;
private String productDescription;
#Min(value = 0, message = "The product price must no be less then zero.")
private double productPrice;
#Min(value = 0, message = "The product unit must not be less than zero.")
private int unitInStock;
#ManyToMany
#JoinTable(name = "category_product", joinColumns = #JoinColumn(name = "product_id"), inverseJoinColumns = #JoinColumn(name = "category_id"))
private Set<Category> categories = new HashSet<>();
As i want the user to be able to search by category name also,bedsides a price range and unitInStock which is separate entity and it is linked with #ManyToMany relationship ,i want to have a method that would look something like:
#GetMapping("/search")
public ResponseEntity<Set<Product>> advancedSearch(#RequestParam(name="category") String categoryName,
#RequestParam(name="price") double price,
#RequestParam(name="unitInStock") int unitInStock ){
}
Category entity:
#Entity
public class Category implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long categoryId;
#NotEmpty(message = "Can not be null")
private String CategoryName;
#ManyToMany(mappedBy = "categories")
#JsonBackReference
private Set<Product> products = new HashSet<>();
Create spring repository with method with JPQL query:
#Query("select p from Product p left join p.categories c where c.CategoryName like ?1 and p.productPrice=?2 and p.unitInStock=?3")
List<Product> search(String categoryName, double price, int unitInStock)

Spring Boot bidirectional ManyToOne constraint

Here are my 2 entities with a bidirectional ManyToOne relation. I deleted getters and setters to simplify the code.
#Entity
public class City {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected int id;
#Column(nullable = false, length = 50)
protected ZonedDateTime creationDate;
#Column(nullable = false, length = 50)
protected ZonedDateTime updatedDate;
#NotNull
#Size(min = 3, max = 20)
#Column(unique = true)
protected String name;
#OneToMany(mappedBy = "id")
#JsonBackReference
protected Collection<Spot> spots;
}
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = { "name", "address", "city_id" }))
#JsonIgnoreProperties(ignoreUnknown = true)
public class Spot {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected int id;
#Column(nullable = false, length = 50)
protected ZonedDateTime creationDate;
#Column(nullable = false, length = 50)
protected ZonedDateTime updatedDate;
#NotNull
#Size(min = 3, max = 30)
protected String name;
#NotNull
#NotBlank
protected String address;
#NotNull
#ManyToOne
#JoinColumn(name = "city_id")
// #JsonManagedReference
protected City city;
}
Because of the second constraint, i can't add more spots than the number of cities. IDs allowed for Spot is in the range of <1 to number of cities>.
Here is the error i get :
Cannot add or update a child row: a foreign key constraint fails (`myDatabase`.`spot`, CONSTRAINT `FKo3x3ohxcttkl2at3yr1xviw1r` FOREIGN KEY (`id`) REFERENCES `city` (`id`))
Any idea ? thank you

Resources