In my application every customer can have several accounts. I have the following data structure (a lot omitted for brevity):
#Entity
#Table(name = "CUSTOMER")
public class Customer {
#Id
#Column(length = 36, name = "CUSTOMER_ID", nullable = false, unique = true)
private String id;
#OneToMany
#JoinColumn(name = "OWNER_ID", referencedColumnName = "CUSTOMER_ID")
private List<Account> accounts;
}
#Entity
#Table(name = "ACCOUNT")
public class Account {
#Id
#Column(length = 36, name = "ACCOUNT_ID", nullable = false, unique = true)
private String id;
#Column(name = "OWNER_ID", nullable = false)
private String ownerId;
}
If I use JPA to delete a Customer, such as
entityManager.remove(customer);
it tries to update the related ACCOUNT.OWNER_ID fields with null. OWNER_ID is not nullable, so it throws a JDBCException and rolls back the transaction.
What I need to achieve is that the related ACCOUNT rows get deleted (if any). How can I do that?
Thank you
Update: I tried it with
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
but it does not change the behavior: still tries to update with null.
I think you need to be using cascading in order to remove the child elements. Try this:
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "OWNER_ID", referencedColumnName = "CUSTOMER_ID")
private List<Account> accounts;
You should also reference the Customer in your account by a ManyToOne relationship and not the String id. I think this should solve your issue:
#Column(name = "OWNER_ID", nullable = false)
#ManyToOne
private Customer owner;
Related
I have two entities BookingLegEntity and BookingEntity which reference each other. But anytime I try to retrieve them from the database (e.g. via findByUuid), BookingLegEntity.belongsTo remains null.
Here are my entities:
#Entity
#Table(name = "BOOKING_LEG")
#SQLDelete(sql = "UPDATE BOOKING_LEG SET deleted = true WHERE id=?")
#Where(clause = "deleted=false")
public class BookingLegEntity {
#Id
#Column(name = "ID", unique = true, updatable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "UUID", nullable = false)
private UUID uuid;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "BELONGS_TO")
private BookingEntity belongsTo;
// ..
#ManyToOne
#JoinColumn(name = "DISTRIBUTOR")
private DistributorEntity distributor;
#Column(name = "TRANSPORT_TYPE")
#Convert(converter = TripTypeEnumConverter.class)
private TripTypeEnum transportType;
// ...
}
#Entity
#Table(name="BOOKINGS")
#SQLDelete(sql = "UPDATE BOOKINGS SET deleted = true WHERE id=?")
#Where(clause = "deleted=false")
public class BookingEntity {
#Id
#Column(name="ID", unique=true, updatable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="BOOKING_ID")
#Convert(converter = BookingIdConverter.class)
private BookingId bookingId;
#ManyToOne
#JoinColumn(name ="BOOKED_BY")
private UserEntity bookedBy;
// ..
#OneToMany(mappedBy = "belongsTo", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<BookingLegEntity> bookingLegs = new HashSet<>();
// ...
}
Here is my repository:
#Repository
public interface BookingLegRepository extends JpaRepository<BookingLegEntity, Long> {
Optional<BookingLegEntity> findByUuid(UUID id);
// ...
}
The values in the database itself look correct:
What is really strange is that this has worked before (belongsTo was not null) but suddenly stopped working. Does anyone has any idea as to what we might do wrong here?
Do not use cascade = CASCADEType.ALL on your ManyToOne annotation, because removing one BookingLeg will cause a removal of all in corresponding Booking
The solution should be to use
cascade = CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}) in its stead.
I would Truncate Cascade or Delete from Bookings where original_itinerary is null before i move on to using the new entities.
Sincerely hope it helps. (No hate if it doesn't pls)
Edit : i didnt see that comment by #dey, its my own. :P saw his comment after posting my ans
I have 2 models with one to one relationship
#Entity
#Table(name = "Form_Item_Production")
public class FormItemProduction {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id", nullable = false)
private Long itemId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "shift_lookup_id", insertable = false, updatable = false)
private AppLookup appLookup;
getter and setter
}
The other one
#Entity
#Table(name = "App_Lookup")
public class AppLookup {
#Id
#GeneratedValue
#Column(name = "Lookup_Id", nullable = false)
private Long lookupId;
#Column(name = "Lookup_Name", length = 30, nullable = false)
private String lookupName;
getter and setter
}
When I try to persist to save the formitemproduction values
public boolean insertItem(List<FormItemProduction> f) {
for (FormItemProduction i : f) {
System.out.println("A" + i.getAppLookup().getLookupId()); // prints the correct id of applookup
i.setItemId(null);
entityManager.persist(i);
}
entityManager.flush();
return true;
}
I get this error
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.mamee.factory.security.entity.AppLookup
From my understanding this is unidirectional one to one mapping so I don't quite understand why I'm getting the error detached?
You have itemId which can't be null.
#Column(name = "item_id", nullable = false)
private Long itemId;
and you actually set itemId to null
i.setItemId(null);
So this line
entityManager.persist(i);
Is not able to persist your data.
Fixed by changing from:
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "shift_lookup_id", insertable = false, updatable = false)
private AppLookup appLookup;
to:
#OneToOne
#JoinColumn(name = "shift_lookup_id", insertable = true, updatable = true)
private AppLookup appLookup;
You have set cascade to ALL:
#OneToOne(cascade = CascadeType.ALL)
Which means you are cascading all operations down to the related field AppLookup.
You can set cascade to none, and you will no longer get your error, but no db operations will be executed for AppLookup field.
Have 2 Entities: Orders and Products. 1 Order can have Many Products and Many Products can belong to 1 Order (Each Product only belongs to 1 Order).
With unidirectional association at Order Entity, I am able to retrieve product details when performing orderRepo.findAll(); In similar fashion, need order details when performing productRepo.findAll();
Tried code:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "order_details")
public class OrderData {
#Id
#Column(name = "order_id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long orderId;
#NotNull
#Column(name = "customer_name", nullable = false)
private String customerName;
#OneToMany(mappedBy = "productId", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<ProductData> products;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "product_details")
public class ProductData {
#Id
#Column(name = "product_id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long productId;
#NotNull
#Column(name = "product_name", nullable = false)
private String productName;
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "order_id", nullable = false)
private OrderData orderData;
}
While inserting at products; we are getting error: "insert or update on table violates foreign key constraint jpa"
While performing productRep.findAll(): infinite loop for hibernate select queries
Tried #JsonIgnore. This not returning child or parent elements.
Tried #JsonManagedReference vs #JsonBackReference - still no luck.
Please guide me on this
The mappedBy attribute points to the wrong field:
#OneToMany(mappedBy = "productId", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<ProductData> products;
This must be the back reference:
#OneToMany(mappedBy = "orderData", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<ProductData> products;
I would like to ignore #OnetoMany field in my entity. fetch data need to get actual fields but don't want to fire query to dependent table. But deleting data from parent table needs deletion from dependent table
I have tried #Transient that ignores but the delete is also being ignored. Is there any other option to tell JPA not to fetch data from childs table when i call the parent entity?
#Entity
Table(name = "User")
public class UserEntity implements Serializable {
#Id
#Column(name = "id")
private int id;
#Column(name = "SERIAL", unique = true, nullable = false)
private String serial;
#OneToMany(mappedBy = "serialBySerialId", cascade = CascadeType.ALL)
private Set<UserActionEntity> userActionsById;
}
#Table(name = "user_action")
public class UserActionEntity implements Serializable {
#Id
#Column(name = "id")
private int id;
#Column(name = "action")
private String action;
#ManyToOne
#JoinColumn(name = "USER_ID", referencedColumnName = "ID", nullable = false)
private UserEntity userByUserId;
If you don't want to fire query to dependent table, you can use (fetch = FetchType.LAZY) on UserActionEntity property.
#OneToMany(mappedBy = "serialBySerialId", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<UserActionEntity> userActionsById;
In my Spring boot app, there are two types of entities: User and Group:
User can own 0 to N groups
Group can have 1 to M members
In the User class there is a list of Group that he/she owns or is a member of, and in the Group class, there is a list of User (i.e. members).
These classes refer to each other using hibernate annotations.
class User {
#ManyToMany(cascade = CascadeType.REFRESH)
private List<Group> groups;
}
class Group {
#ManyToOne(cascade = CascadeType.REFRESH)
#NotNull
#JoinColumn(name="OWNER_ID", referencedColumnName="id")
private User owner;
#ManyToMany
#JoinTable(joinColumns = #JoinColumn(referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(referencedColumnName = "id"))
private List<User> members;
}
In the User service layer there's a Delete method which is supposed to delete a user from the repository. This delete should fire a series of actions: all the groups owned by that user gets deleted, and the deleted groups should be removed from list of groups of their members. All these should be saved to the repository.
If I add other types of entities to this network, this process gets much more complicated.
My question is: Doesn't hibernate handle this automatically ? Should I grab each member and delete the group one by one and save it to the repository ?
CascadeType.REFRESH means Managed objects can be reloaded from the database by using the refresh method.
This will not help you solving your requirement. You need to use “orphanRemoval = true” CascadeType. “orphanRemoval = true” removes an owned object from the database when it’s removed from its owning relationship.
Example:
EmployeeEntity.java
#Entity #Table(name = "Employee")
public class EmployeeEntity implements Serializable
{
private static final long serialVersionUID = -1798070786993154676L;
#Id #Column(name = "ID", unique = true, nullable = false)
private Integer employeeId;
#Column(name = "FIRST_NAME", unique = false, nullable = false, length = 100)
private String firstName;
#Column(name = "LAST_NAME", unique = false, nullable = false, length = 100)
private String lastName;
#OneToMany(orphanRemoval = true, mappedBy = "employee")
private Set<AccountEntity> accounts;
}
AccountEntity.java
#Entity (name = "Account") #Table(name = "Account")
public class AccountEntity implements Serializable
{
private static final long serialVersionUID = 1L;
#Id #Column(name = "ID", unique = true, nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer accountId;
#Column(name = "ACC_NO", unique = false, nullable = false, length = 100)
private String accountNumber;
#ManyToOne
private EmployeeEntity employee;
}
OR You can use CascadeType.ALL too.
For further reading, go through below link:
CascadeTypes