One to many associated entities are not getting deleted with Spring Data JPA - spring-data-jpa

I have a one-to-many relationship between two entities Order and LineItem. I also have a parent to Order entity which is Customer and Customer to Order is one-to-many. When I try to delete the order entity, neither the order, nor the line_items associated with the order is getting deleted. I have tried the below options
orphanRemove
Fetch type from Lazy to Eager
Cascade to ALL
Below is the Order and LineItem definitiona
Order
#Data
#NoArgsConstructor
#Builder
#AllArgsConstructor
#EqualsAndHashCode(exclude = {"customer", "lineItems"})
#ToString(exclude = {"customer", "lineItems"})
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Min(value = 2000, message = "Min order value should be 2000 INR")
#Max(value = 10_000, message = "Max order value can be 10000 INR")
private double price;
#PastOrPresent(message = "Order date cannot be in future")
private LocalDate date;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="customer_id", nullable = false)
#JsonBackReference
private Customer customer;
#OneToMany(mappedBy = "order",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.EAGER)
private Set<LineItem> lineItems;
#Data
#NoArgsConstructor
#EqualsAndHashCode(exclude = "order")
#ToString(exclude = "order")
#Builder
#AllArgsConstructor
#Entity
#Table(name="line_items")
public class LineItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private int qty;
private double price;
private String name;
#JsonIgnore
#ManyToOne
#JoinColumn(name="order_id", nullable = false)
private Order order;
}
this.orderRepository.deleteById(fetchedOrder.getId());
Output:
Hibernate:
select
lineitems0_.order_id as order_id5_2_0_,
lineitems0_.id as id1_2_0_,
lineitems0_.id as id1_2_1_,
lineitems0_.name as name2_2_1_,
lineitems0_.order_id as order_id5_2_1_,
lineitems0_.price as price3_2_1_,
lineitems0_.qty as qty4_2_1_
from
line_items lineitems0_
where
lineitems0_.order_id=?
Where am I going wrong and how to troubleshoot this issue?

This might be a shot in the dark, but the only way I was able to reproduce Hibernate only executing a SELECT query and not going for DELETE was when I annotated the service method that calls orderRepository.deleteById(id); with #Transactional(readOnly=true), so this might be it, otherwise it might be some combination of configuration properties you've set.
It might help to add your dependencies versions to the question, since it might be some version-specific behavior of Hibernate.
While I'm at it, note that using lombok's #EqualsAndHashCode and #ToString (and, by extension, #Data) with jpa entities is not advised due to potential problems with related entities causing infinite loop and such.

I figured out the reason. There was one more entity reference with Customer and Order. I removed the reference of order from Customer and saved the customer to resolve the issue.
customer.getOrders().removeIf(order -> order.getId() == orderId);
this.customerRepository.save(customer);

Related

spring data JPA OneToMany query pagination of child (many) table

I have the following code. The number of rows in the One table will be about 100 and the number of rows in the Many table will be several hundred thousand (millions). I'm going to need to paginate the Many rows per single row in One when I query.
I'm not sure how to accomplish this.
My first thought is to get only the rows (and NOT the manyList content) in One but not even sure of that in JPA OneManyRepository. And then for each row use it's id primary key to query the Many with that one_id value and do pagination that way.
Any help will be apprecited.
Thanks,Jim
public interface OneManyRepository extends JpaRepository<One, Long> {
#Query(value = "select one.id,one.columnOne,one.columnTwo from one", nativeQuery = true)
Optional<List<One2>> findAllOne();
#Query(value = "select many.someTimeValue,many.value from many where many.one_id = :id", nativeQuery = true)
Optional<List<Many>> findAllBy(#Param("id") long id);
}
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class One2 {
private Long id;
private String columnOne;
private String columnTwo;
}
#Entity
#Table(name = "one")
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class One {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long id;
private String columnOne;
private String columnTwo;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "one")
private List<Many> manyList;
}
#Entity
#Table(name = "many")
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Many {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long id;
#ManyToOne
private One one;
private long someTimeValue;
#Column(length = 10)
private String value;
}

spring data error when trying to sort by a field of joined entity inside a crudrepository

I am using springboot and springdata with Mysql.
I have 2 entities, Customer & Order:
#Entity
#Table(name = "customers")
public class Customer {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id", nullable = false)
protected long id;
#Column(name = "name")
private String name;
}
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id", nullable = false)
protected long id;
#Column(name="customer_id")
private long customerId;
}
I also have a repository:
#Repository
public interface OrdersRepository extends JpaRepository<Order, Long> {
#Query("select o from Order o, Customer c where o.customerId = c.id")
Page<Order> searchOrders(final Pageable pageable);
}
The method has some more arguments for searching, but the problem is when I send a PageRequest object with sort that is a property of Customer.
e.g.
Sort sort = new Sort(Sort.Direction.ASC, "c.name");
ordersRepository.search(new PageRequest(x, y, sort));
However, sorting by a field of Order works well:
Sort sort = new Sort(Sort.Direction.ASC, "id");
ordersRepository.search(new PageRequest(x, y, sort));
The error I get is that c is not a property of Order (but since the query is a join of the entities I would expect it to work).
Caused by: org.hibernate.QueryException: could not resolve property c of Order
Do you have any idea how I can sort by a field of the joined entity?
Thank you
In JPA , the thing that you sort with must be something that is returned in the select statement, you can't sort with a property that is not returned
You got the error because the relationship is not modeled properly. In your case it is a ManyToOne relation. I can recomend the wikibooks to read further.
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id", nullable = false)
protected long id;
#ManyToOne
#JoinColumn(name="customer_id", referencedColumnName = "id")
private Customer customer;
}
The query is not needed anymore because the customer will be fetched.
#Repository
public interface OrdersRepository extends PagingAndSortingRepository<Order, Long> {
}
Now you can use nested properties.
Sort sort = new Sort(Sort.Direction.ASC, "customer.name");
ordersRepository.findAll(new PageRequest(x, y, sort));

Feed a list with the last value

I have theses entity and I do this query.
select r from RentAmount r Join r.lodger l join l.bailList b where r.unpaidBalance > 0 and (r.paymentDueDate > :date or r.paymentDueDate is null ) and b.paymentPeriod= order by r.rentAmountId")
Is there a way to feed Lodger.bailList only with the last bail or i would need to loop on every record to get this information?
#Entity
public class RentAmount {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long rentAmountId;
#OneToOne
private Lodger lodger;
}
#Entity
public class Lodger{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long lodgerId;
#OneToOne(fetch = FetchType.LAZY, mappedBy="lodger")
private RentAmount rentAmount;
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY, mappedBy = "lodger", orphanRemoval = true)
private List<Bail> bailList;
}
#Entity
public class Bail {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long bailId;
#Enumerated(EnumType.STRING)
private PaymentPeriodEnum paymentPeriod;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "lodger_id")
private Lodger lodger;
}
There are a few options:
One (Non JPA, Hibernate Only)
Ensure the collection is correctly ordered and mark it is as extra lazy. You will have access to the whole collection but accessing of individual items will not trigger a full load.
https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/performance.html
"Extra-lazy" collection fetching: individual elements of the
collection are accessed from the database as needed. Hibernate tries
not to fetch the whole collection into memory unless absolutely
needed. It is suitable for large collections.
The mapping will look like:
#OneToMany(mappedBy = "lodger")
#LazyCollection(LazyCollectionOption.EXTRA)
#OrderBy("theRelevantProperty ASC")
private List<Bail> bailList;
public void getCurrentBail(){
//will only load this item from the database
return bailList.get(bailList.size() - 1);
}
Two (Non JPA, Hibernate Only.)
Use the #Where annotation to filter the collection so that while still #OneToMany, only one element will be accessible.
https://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-hibspec-collection
The mapping will look like:
#OneToMany(mappedBy = "lodger")
#Where(clause="some native sql which will filter to include onyl 1item"))
private List<Bail> bailList;
public void getCurrentBail(){
//will be the only item accessible
return bailList.get(0);
}
Three (JPA Compliant)
Would involve creating views at the database level. Various options in this area. If we are only ever interested in the current bail then this view would be similar to option 2 above. Simply point the Bail entity to this view rather than the concrete table:
#Entity
#Table(name = "vw_active_bail")
public class Bail {
}

JPA -- Using the one-to-one dependency relationship on insertion

I have 2 entity classes with one-to-one dependencies on their primary keys:
The primary table:
#Entity
#Table(name = "tablePrimary")
#XmlRootElement
//...
public class TablePrimary implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Basic(optional = false)
#Column(name = "code")
private String code;
// set the dependency of table2 to this class
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Table2 table2inst;
// ...
} // end of class TablePrimary
The dependent table:
#Entity
#Table(name = "table2")
#XmlRootElement
//...
public class Table2 implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Basic(optional = false)
#Column(name = "name")
private String name;
#MapsId
#OneToOne(mappedBy = "table2inst")
#JoinColumn(name = "id")
private TablePrimary tablePrimaryInst;
//...
} // end of class Table2
Whenever there is a row with say, id==55 in TablePrimary, there is
a row with the same id==55 in Table2 and vice-versa.
So in essence, these two tables are one table in logical level-- split into 2 physical tables for practicality.
When i'm inserting a row into the "logical" table,
i first am inserting to TablePrimary-- the primary table in the relationship,
getting the value of id==55 field of this new row i just inserted and inserting a row to
Table2 with that id value.
As part of this, i'm checking, just in case,
whether a row with id==55 is already in Table2.
Is there a better way of doing this?
Does JPA have a feature to make these two insertions to these two physical tables
by using the 1-1 dependency I configured on them-- without me having to do it "manually" in the code? Or a control feature on the id fields of the tables I set the dependency on?
If there is-- how is done? how does it handle the key value collision in the dependent table-- Table2?
A similar thing will come up on deletion. However, i'm not there yet, and might figure out out of this.
TIA.
You can take advantage of JPA cascading. You will have to define a cascade on the owning side (the one with the join column). If you have set the owning side of the relationship and persist the owning side, the inverse side will be persisted as well:
TablePrimary tp = new TablePrimary();
Table2 t2 = new Table2();
t2.setTablePrimaryInst(tp);
entityManager.persist(t2);
The 'mappedBy' element is supposed to be placed on the inverse side. You entities could look like this:
public class Table2 ...
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "tp_id")
private TablePrimary tablePrimary;
public class TablePrimary...
#OneToOne(mappedBy="tablePrimary")
private Table2 table2;

merging entity with onetomany mapping and #version field causes delete of the previous mapping

Hi! All,
I have a mapping issue with two entities. mapped through a #OneToMany unidirectional relation. I have an entity Artifact which can have multiple Revision. Here's how I have mapped them
#Entity
#Table(name = "artifact")
public class Artifact implements Serializable {
private static final long serialVersionUID = 248298400283358441L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Version
private Integer version;
...
#OneToMany(cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.REMOVE })
#JoinTable(name = "artifact_revisions", joinColumns = #JoinColumn(name = "artifact_id"), inverseJoinColumns = #JoinColumn(name = "revision_id"))
private Set<Revision> revisions;
And the revisions entity
#Entity
#Table(name = "revision")
public class Revision implements Serializable {
private static final long serialVersionUID = -1823230375873326645L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
#Column(name = "date_created", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date creationDate;
The revision table saves the filed name that was updated; old value and new value etc.
The problem I face is that when I update the artifact; the last mapping gets deleted and then it inserts a new one, so if effect I only have the last but one revision available not the entire revision history.
Hibernate:
update
artifact
set
description=?,
estimate=?,
name=?,
rank=?,
status=?,
sysId=?,
version=?
where
id=?
and version=?
Hibernate:
delete
from
artifact_revisions
where
artifact_id=?
and revision_id=?
Hibernate:
insert
into
artifact_revisions
(artifact_id, revision_id)
values
(?, ?)
If I remove #version annotation from the artifact it works fine.
Is it because I am mapping the relation in a wrong manner? Should this relation be mapped as an element collection instead?
There is another Entity Task which is to be mapped with the Revision entity. So what will be the best approach here?
Maybe this is not a straight answer to your question but I think you should look into hibernate envers. I think it's doing pretty similar thing. (envers stands for entity versioning). You just annotate entity with #Audited put some listeners into config and the rest magic is done for you.