how to update Bidirectional #OneToMany relationship - jpa

I have Employee and Functions, Bank classes
Employee and Function have #OneToMany relationship and
Employee and Bank have also #OneToMany relationship.
if the user edits the form and change the function and/or bank
I want to update the relationship. but when I change the relationship
I get Duplicate entry exception due to the uniqueness of a column because
the Employee object persisted as a new entity
I tried to remove the employee from the function and set the employee's function to null and get a new function and add the employee to it
and set the new function but it doesn't work. any idea, please
#Entity
public class Employee extends GeneratedIdEntity<Long> {
#ManyToOne(optional = false)
private Functions function;
#ManyToOne(optional = false)
private Bank bank;
#OneToMany(
mappedBy = "employee",
fetch = LAZY,
cascade = ALL,
orphanRemoval = true
)
private List<RubricValue> rubricsValues = new ArrayList<>();
#OneToMany(
mappedBy = "employee",
fetch = LAZY,
cascade = ALL,
orphanRemoval = true
)
List<EmployeeStatus> employeesStatus=new ArrayList<>();
}
#Entity
public class Functions extends GeneratedIdEntity<Long>{
#OneToMany(
mappedBy = "function",
fetch = LAZY,
cascade = ALL,
orphanRemoval = true
)
private List<Employee> employees=new ArrayList<>();
public void addEmployee(Employee employee ){
employees.add(employee);
}
public void removeEmployee(Employee employee){
employees.remove(employee);
}
}
#Entity
public class Bank extends GeneratedIdEntity<Long> {
#OneToMany(
mappedBy = "bamk",
fetch = LAZY,
cascade = ALL,
orphanRemoval = true
)
private List<Employee> employees = new ArrayList<>();
public void addEmployee(Employee employee ){
employees.add(employee);
}
public void removeEmployee(Employee employee){
employees.remove(employee);
}
}
#Stateless
public class EmployeeService extends BaseEntityService<Long, Employee> {
#Inject
FunctionService functionService;
#Inject
BankService bankService;
public void update(Employee employee, String newFunctionName, String newBankName) {
if (!employee.getBank().getName().equals(newBankName)) {
employee.getBank().removeEmployee(employee);
employee.setBank(null);
Bank newBank = bankService.getByName(newBankName);
newBank.addEmployee(employee);
employee.setBank(newBank);
}
if (!employee.getFunction().getName().equals(newFunctionName)) {
employee.getFunction().removeEmployee(employee);
employee.setFunction(null);
Functions newFunction = functionService.getByName(newFunctionName);
newFunction.addEmployee(employee);
employee.setFunction(newFunction);
}
}
}
the exception stack trace Caused by:
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry
'dkfhks32' for key 'REGISTRATIONNUMBER' at
com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:115)
at
com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
at
com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at
com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:960)
at
com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1116)
at
com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1066)
at
com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1396)
at
com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1051)
at
com.sun.gjc.spi.base.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:127)
at sun.reflect.GeneratedMethodAccessor54.invoke(Unknown Source) at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498) at
com.sun.gjc.spi.jdbc40.ProfiledConnectionWrapper40$1.invoke(ProfiledConnectionWrapper40.java:437)
at com.sun.proxy.$Proxy268.executeUpdate(Unknown Source) at
org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:898)

You don't have to change the bank for an employee by removing the reference of existing bank first. You can simply go:
if (!employee.getBank().getName().equals(newBankName)) {
Bank newBank = bankService.getByName(newBankName);
//You must also do an entity validation/null check here. The newBank might not be present after all.
employee.setBank(newBank);
}
This will update the mappings correctly. Same goes for updating function of an employee

Related

Cannot delete or update a parent row: a foreign key constraint fails Spring JPA

I have this query
DELETE
FROM bookings as b
WHERE b.check_out = CURRENT_DATE;
and I get
Cannot delete or update a parent row: a foreign key constraint fails (online_booking_app.booked_rooms, CONSTRAINT FK3x1lpikb2vk75nx41lxhdicvn FOREIGN KEY (booking_id) REFERENCES bookings (id))
My Booking entity has CascadeType.ALL and mapped by matches the other side - from my research these are some of the mistakes that could lead to this message.
Here is the BookingEntity:
#Entity
#Table(name = "bookings")
public class BookingEntity extends BaseEntity {
#OneToMany(mappedBy = "booking",cascade = CascadeType.ALL, orphanRemoval = true)
private List<BookedRoomsEntity> bookedRooms = new ArrayList<>();
private String firstName;
private String lastName;
public List<BookedRoomsEntity> getBookedRooms() {
return bookedRooms;
}
public BookingEntity setBookedRooms(List<BookedRoomsEntity> bookedRooms) {
this.bookedRooms = bookedRooms;
return this;
}
BookedRoomsEntity
#Entity
#Table(name = "booked_rooms")
public class BookedRoomsEntity extends BaseEntity {
#ManyToOne()
private BookingEntity booking;
public BookingEntity getBooking() {
return booking;
}
public BookedRoomsEntity setBooking(BookingEntity booking) {
this.booking = booking;
return this;
}
The CascadeType does only apply to EntityManager operations.
You therefore have two options:
Load the entities to be deleted first and then use EntityManager.remove
Remove the referencing entities first with a separate JPQL statement.

Spring Data Specification orderBy subquery

On my MySql project I got this particular model with 3 entities: Prodotto with many childs QuotaIngrediente, that in turn is Many-to-One child of Ingrediente too. All my relationships are bi-directional.
All of them got an autogenerated integer Id and other fields removed to focus on the interesting ones.
#Entity
public class Prodotto {
private List<QuotaIngrediente> listaQuoteIng = new ArrayList<QuotaIngrediente>();
#OneToMany(mappedBy = "prodotto", cascade = CascadeType.ALL, orphanRemoval = true)
public List<QuotaIngrediente> getListaQuoteIng() {
return listaQuoteIng;
}
#Entity
public class QuotaIngrediente{
private Prodotto prodotto;
private Ingrediente ing;
private Double perc_ing;
#ManyToOne
#JoinColumn(name = "prodotto")
public Prodotto getProdotto() {
return prodotto;
}
#ManyToOne
#JoinColumn(name = "ing")
public Ingrediente getIng() {
return ing;
}
#Entity
public class Ingrediente {
private Set<QuotaIngrediente> quoteIng = new HashSet<QuotaIngrediente>();
#OneToMany(mappedBy = "ing", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<QuotaIngrediente> getQuoteIng() {
return quoteIng;
}
I'm using SpringData Specification and I can build a query to get Prodotto based on Ingrediente criteria, this way:
public static Specification<Prodotto> getProdottoByIngSpec (String ing) {
if (ing != null) {
return (root, query, criteriaBuilder) -> {
query.groupBy(root.get(Prodotto_.id));
return criteriaBuilder.like(((root.join(Prodotto_.listaQuoteIng))
.join(QuotaIngrediente_.ing))
.get(Ingrediente_.nome), "%"+ing+"%");
};
It works as expected, but now I want to sort it by the QuotaIngrediente perc_ing field OF THAT SPECIFIC INGREDIENTE.
Obviously I'm asking how to do it on DB, not in business logic.
I was struggling with a false problem due to a wrong assumption of mine. Solution was the simplest. Just sort by orderBy CriteriaQuery method. The query I used to search already filtered the QuotaIngrediente returning just the lines that match my search criteria. Then this is the only line I had to add to my Specification:
query.orderBy(builder.desc((root.join(Prodotto_.listaQuoteIng))
.get(QuotaIngrediente_.perc_ing)));

NullPointer Exception when access Newly Merged Records JPA, Entity Manager

I have Two Entities , Entity Applicants, Entity JobApplications. The Relationship is
//Entity Applicants
public class AppPers implements Serializable {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "appPers",fetch = FetchType.EAGER)
private Collection<JobApplications> jobApplicationsCollection;
.........................
}
//Entity JobApplications
public class JobApplications implements Serializable {
#JoinColumn(name = "app_id", referencedColumnName = "APP_ID", insertable = false, updatable = false)
#ManyToOne(optional = false,fetch = FetchType.EAGER)
private AppPers appPers;
So after i do a merger
AppPers ap =em.find(AppPers.class,appId);
ap.setJobApplicationsCollection(c);
em.merge(ap);
em.refresh(ap);
And try to execute
//fetch the newly created list
List<JobApplications> list = new ArrayList<JobApplications>();
list = em.createNamedQuery("JobApplications.findAll").getResultList();
//list size is 3 in my case
for( JobApplications ja: list) {
String a = ja.getAppPers().getSurName();
}
surName being a column of AppPers, I get NullPointer Exception. This happens untill i restart Glassfish then the Null Pointer Does not Happen. I have tried setting the fetch to Eager but still no joy. What could i be missing
I don't see how this relates to JPA. It's plain old Java. You have a class defined as
public class JobApplications implements Serializable {
private AppPers appPers;
...
}
The default value of a field is null.
So if you do
JobApplications ja = new JobApplications ();
AppPers ap = ja.getAppPers();
ap will be null. So if you do
JobApplications ja = new JobApplications ();
String a = ja.getAppPers().getSurName();
you'll get a NullPointerException.

EntityExistsException When Attempting Bulk Delete With OpenJPA

I have three classes: Location, MTFCC, and BorderPoint.
Location has a unidirectional #ManyToOne relationship with MTFCC, which is intended only as a Lookup table. No cascading is defined.
Location also has a bidirectional #ManyToOne/#OneToMany with BorderPoint. Since I want all associated BorderPoint objects to delete when I delete a Location, I set cascadetype.ALL on the Location side of the relationship.
Unfortunately, an EntityExistsException is being thrown when I attempt to delete a location:
org.apache.openjpa.persistence.EntityExistsException: Cannot delete or update
a parent row: a foreign key constraint fails (`mapmaker`.`BORDERPOINT`,
CONSTRAINT `BORDERPOINT_ibfk_1` FOREIGN KEY (`LOCATIONID`) REFERENCES `LOCATION`
(`LOCATIONID`)) {prepstmnt 21576566 DELETE t0, t1 FROM LOCATION t0 INNER JOIN
MTFCC t1 ON t0.MTFCCID = t1.MTFCCID WHERE (t0.STATEFP = ? AND t1.MTFCCCODE = ?)
[params=?, ?]} [code=1451, state=23000]
[ERROR] FailedObject: DELETE t0, t1 FROM LOCATION t0 INNER JOIN MTFCC t1 ON
t0.MTFCCID = t1.MTFCCID WHERE (t0.STATEFP = ? AND t1.MTFCCCODE = ?)
[java.lang.String]
It looks like it's attempting to delete the associated MTFCC object which I do NOT want to happen. I do, however, want the associated BorderPoint objects to be deleted.
Here is the code (chopped down a bit):
#SuppressWarnings("unused")
#Entity
#Table(name="LOCATION")
#DetachedState(enabled=true)
public class Location implements Serializable, IsSerializable, Cloneable {
private Long id;
private String stateGeoId;
private MTFCC mtfcc;
private List<BorderPoint> borderPointList;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="LOCATIONID")
public Long getId() {
return id;
}
#ManyToOne
#JoinColumn(name="MTFCCID")
public MTFCC getMtfcc() {
return mtfcc;
}
#OneToMany(cascade = CascadeType.ALL, mappedBy = "location", fetch = FetchType.EAGER)
public List<BorderPoint> getBorderPointList() {
return borderPointList;
}
}
#Entity
#Table(name = "BORDERPOINT")
#DetachedState(enabled = true)
public class BorderPoint implements Serializable, IsSerializable {
private Long id;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="BORDERID")
public Long getId() {
return id;
}
#ManyToOne(targetEntity = Location.class)
#JoinColumn(name="LOCATIONID")
public Location getLocation() {
return location;
}
}
#Entity
#Table(name = "MTFCC")
public class MTFCC implements Serializable, IsSerializable {
private Long id;
private String mtfccCode;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "MTFCCID")
public Long getId() {
return id;
}
// etc
}
And, for good measure, here is the deletion code:
#Override
#Transactional
public int removeByStateGeoIdAndMtfcc(String stateGeoId, String mtfccCode) throws RepositoryException {
EntityManager em = entityManagerFactory.createEntityManager();
String jpaQuery = "DELETE FROM Location L where L.stateFP = ?1 AND L.mtfcc.mtfccCode = ?2";
int affectedRows = 0;
Query query = em.createQuery(jpaQuery).setParameter(1, stateGeoId).setParameter(2, mtfccCode);
try {
em.getTransaction().begin();
affectedRows = query.executeUpdate();
em.getTransaction().commit();
} catch (Exception e) {
//log.debug("Exception: ", e);
throw new RepositoryException(e);
}
em.close();
return affectedRows;
}
Hopefully I copied all relevant parts... can anyone assist?
You aren't reading the error message correctly. It says that the deletion is forbidden because of the foreign key constraint between BorderPoint and Location.
The cascade delete would work if you used em.remove(location) to delete your Location. Using a delete query like you're doing won't automagically delete the BorderPoints before deleting the location.
Either load them and remove them using em.remove, or execute other delete queries before to delete the BorderPoints.

Delete object from ManyToOne relationship doesn't update a parent collection

I have ManyToOne relationship between Parent and Child. I want to Delete multiple Child entities that answer a certain query.
The problem is that after I run a Delete query, Parent.getChildren() still returns the deleted children.
Can't I use Delete queries in such case?
#Entity
#Table(name = "CHILD_DATA")
public class Child {
private Parent parent;
}
#Entity
#Table(name = "PARENT")
public class Parent{
private Set<Child> children;
#Column(name = "CHILDREN")
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
public Set<Child> getChildren() {
return children;
}
}
public class ChildDAO{
public int removeServiceFrontPageData(Parent parent, long serviceID){
String query = "DELETE FROM Child WHERE parent =:parent";
Query q = em.createQuery(query);
q.setParameter("parent", parent);
return q.executeUpdate();
}
}
To refresh a parent entity, I use the following function:
public class ParentDAO{
public Parent getParent(String parentID){
final String select = "FROM Parent WHERE parentID = :parentID";
Query q = em.createQuery(select);
q.setParameter("parentID", parentID);
if(q.getResultList().isEmpty()){
return null;
}
return (Parent) q.getSingleResult();
}
}
thanks
My solution is based on another post
I found 2 ways to solve it:
1) update a parent if I remove children:
select child entities that should be removed.
remove these entities from parent.getChildren()
delete the children from database
2) add orphanRemoval=true flag on parent.getChildren(). Remove children from set will remove them from database
#Entity
#Table(name = "PARENT")
public class Parent{
private Set<Child> children;
#Column(name = "CHILDREN")
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
public Set<Child> getChildren() {
return children;
}