In Hibernate how to ignore associated table in selection - spring-boot

I am using spring boot and Hibernate and I have two tables order and orderdetails and I need two queries, first I want to select records in order along with records that relate to that order in order detail table which works fine, but when I want to select only records from order table I can't because it will bring all associated records for each order in the order detail(just like the first one). How I can select records from the order without selecting related records in order detail table?
I also tried to use #JsonIgnore annotation but it doesn't work like what I want.
My entity classes
orders
#Entity
#Setter
#Getter
public class Orders {
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "order_id")
private Long orderId;
#Column(name = "user_id")
private Long userId;
#Column(name = "user_name")
private String userName;
#Column(name = "created_date", columnDefinition = "DATE")
#JsonFormat(pattern = "MM/dd/yyyy")
private LocalDate createdDate;
private String status;
private float amount;
#Transient
String error;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "order_id")
private List<OrderDetail> orderDetails;
}
order detail
#Entity
#Setter
#Getter
public class OrderDetail {
public OrderDetail() {
}
public OrderDetail(Long productId, String productName, int quantity, float unitPrice, float subtotalPrice){
this.productId = productId;
this.productName = productName;
this.quantity = quantity;
this.unitPrice = unitPrice;
this.subtotalPrice = subtotalPrice;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "order_detail_id", nullable = false)
Long orderDetailId;
#Column(name = "product_id")
Long productId;
#Column(name = "product_name")
String productName;
int quantity;
#Column(name = "unit_price")
float unitPrice;
#Column(name = "subtotal_price")
float subtotalPrice;
}
Queries that I tried:
#Query(value = "select orders.order_id, orders.amount, orders.created_date, orders.status, orders.user_id, " +
"orders.user_name from orders where orders.user_id = :userid", nativeQuery = true)
List<Orders> selectAllOrdersOfSpecificUser(#Param("userid") Long userId);
==================================================================================
List<Orders> findAllByUserId(Long userId);
Both of them bring all the records from the orders table and associated records in the order detail table.

FetchType.EAGER will retrive all the orderdetails as well. Use FetchType.LAZY and create a special JOIN FETCH query for getting orderdetails with orders.

Related

Trying to Convert a SQL Query to a JPA Query

I want to convert this sql query to a JPA query, but I can't seem make sense of it... Should I use findByMarinaIdAndMovementGroupMeanId?? or findByMarinaIdAndMovementGroupMeanIdAndMovementMeanId??
Sql:
select m.* from movement_group m
join movement_group_mean mgm on m.id = mgm.movement_group_id
join movement_mean mm on mgm.movement_mean_id = mm.id
where mm.id = 1 and m.marina_id = :marinaId and mm.active = true;
MovementGroup:
#Entity
public class MovementGroup {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String code;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private Boolean active;
private String iconUrl;
#OneToMany(mappedBy = "movementGroup")
private Set<MovementGroupMean> movementGroupMeans;
#JsonIgnore
#ManyToOne()
#JoinColumn(name = "marina_id")
private Marina marina;
MovementGroupMean:
#Entity
public class MovementGroupMean {
#EmbeddedId
#JsonIgnore
private MovementGroupMeanPK movementGroupMeanPK;
#JsonBackReference
#ManyToOne
#JoinColumn(name = "movement_group_id", insertable = false, updatable = false)
private MovementGroup movementGroup;
#ManyToOne
#JoinColumn(name = "movement_mean_id", insertable = false, updatable = false)
private MovementMean movementMean;
MovementMean:
#Entity
public class MovementMean {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#Enumerated(EnumType.STRING)
private MovementMeanType movementMeanType;
private Boolean active;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
#JsonBackReference
#ManyToOne()
#JoinColumn(name = "marina_id")
private Marina marina;
Not sure where the problem lies, so excuse the lengthy explanation on SQL->JPQL:
Replace your table names with your entity names
movement_group -> MovementGroup
Replace your joins with the java references, letting JPA use the relationship mapping you've defined instead.
"join movement_group_mean mgm on m.id = mgm.movement_group_id" becomes "join m.movementGroupMeans mgm"
"join movement_mean mm on mgm.movement_mean_id = mm.id becomes "join mgm.movementMean mm"
Only tricky spot is your entities do not define a basic mapping for the marina_id value. So to get at m.marina_id, you will have to use the 'marina' reference and use its presumably ID value:
"m.marina_id = :marinaId" -> "m.marina.id = :marinaId"
Giving you JPQL:
"Select m from MovementGroup m join m.movementGroupMeans mgm join mgm.movementMean mm where mm.id = 1 and m.marina.id = :marinaId and mm.active = true"

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

Spring Boot 2 - JPA + Pagable problem on ManyToMany tables

In Spring Boot 2 JPA, I have the following two many to many Entities.
1- Labor:
#Entity
public class Labor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(length = 100, nullable = false)
private String name;
#Column(length = 50)
private String mobile;
private Date dateOfBirth;
private boolean male;
private boolean active;
private String brife;
#Column(length = 500)
private String specifcation;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "labor_tag",
joinColumns = #JoinColumn(name = "labor_id"),
inverseJoinColumns = #JoinColumn(name = "tag_id"))
private Set<Tag> tags = new HashSet<>();
}
and Tag table:
#Entity
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(length = 100, unique = true)
private String name;
private boolean active = true;
#ManyToMany(cascade = CascadeType.ALL, mappedBy = "tags")
#JsonIgnore
private Set<Labor> labors = new HashSet<>();
}
Then I defined Labor Repository to query Labors with certain Tag ID ,gender, or ages
#Repository
public interface LaborReoistory extends JpaRepository<Labor, Long> {
#Query("select l from Labor l join l.tags t where (:tid is null or t.id in :tid) and " +
"(:isMale is null or :isMale = TRUE) and " +
"((:startYear is null or :endYear is null or :startYear > :endYear) or year(l.dateOfBirth) >= :startYear and year(l.dateOfBirth) <= :endYear)")
Page<Labor> findLaborsByCondition(#Param("tid") final long[] tid,
#Param("isMale") final Boolean isMale,
#Param("startYear") final Integer startYear,
#Param("endYear") final Integer endYear,
final Pageable pageable);
}
When I use this repository in my controller, I find the totalElements property of the Pagable returned counts to records in labor_tag(in this case 16 records),but what I actually want is to have totalElements count on Labors with given conditions. does JPA Pagable support such query or how can I find a workaround?
Thanks
After joining there will be duplicate Labor but totalElements is the count of total number of row using the query. So you should use Distinct on Labour to get the count of distinct Labour
#Query("select distinct l from Labor l join l.tags t where (:tid is null or t.id in :tid) and " +
"(:isMale is null or :isMale = TRUE) and " +
"((:startYear is null or :endYear is null or :startYear > :endYear) or year(l.dateOfBirth) >= :startYear and year(l.dateOfBirth) <= :endYear)")

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

Hibernate Formula with join

i have an entity order and an entity order_items ( 1->n relation).
I want a field in order that table that show the sum(quantity) of related order items. These are my entities:
#Entity(name="ORDERS")
public class Order {
#Id
#GeneratedValue
#Column (name="order_id")
private long id;
#OneToOne
#JoinColumn(name="customer_id")
private Customer customer;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "order", cascade =CascadeType.ALL)
#JsonManagedReference
private List<OrderItem> orderItems=new ArrayList();
#Entity(name="ORDER_ITEMS")
public class OrderItem {
#Id
#GeneratedValue
#Column (name="order_item_id")
private long id;
#Column (name="quantity")
private int quantity;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "order_id", nullable = false)
#JsonBackReference
private Order order;
I want a new field in Order entity that show the total quantity ( sum of quantity of the childs).
I've tried to add the field in order but it not works
#Formula("select sum(oi.quantity) from ORDER_ITEMS oi where oi.order_id= order_id)")
private int totalQuantity;
Can you help me to fix it ?
The solution
#Formula("(select sum(oi.quantity) from ORDER_ITEMS oi where oi.order_id= order_id)")
private int totalQuantity;

Resources