I have one to many relationships between person class and car class. A person can own many cars and vice versa. I am using restful API to post data. My annotations and Get service is working fine but my post service throws " java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL" error whenever I try to insert new data. Child table foreign key is being inserted as null.
Here is part of my code.
Person.java
private List<Car> cars = new ArrayList<Car>();
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="person")
#JsonManagedReference
public List<Car> getCars() {
return cars;
}
Car.java
private Person person;
#ManyToOne
#JoinColumn(name = "PERSON_ID", nullable = false, updatable = true, insertable = true)
#JsonBackReference
public Person getPerson() {
return person;
}
My service class:
#POST
#Path("/PersonRegistration")
#Consumes(MediaType.APPLICATION_JSON)
public Response postPersonCars(Person person) throws Exception{
Session session = null;
ObjectMapper mapper = new ObjectMapper();
//Person per = new Person();
//Car cars = new Car();
try{
session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
//per.setCars(person.getCars());
session.save(person);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
throw e;
}finally{
if(null != session){
session.close();
}
}
return Response.status(201).entity(mapper.writeValueAsString(person)).build();
}
This annotation:
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="person")
has two consequences:
mappedBy implies that Car is the owning side of the relationship. This means that whenever you want to establish a relationship between Car and Person, you need to do it by setting the Car.person property to the appropriate value. Changes to Person.cars will be ignored by Hibernate.
cascade=CascadeType.ALL means that whenever you save a Person, Hibernate will also invoke the save operation on all entities contained in Person.cars
Result: you are calling Session.save() on a bunch of Car entities that do not have the Car.person property set properly.
Solution: either change the owning side of the relationship (be aware that you will also need a #JoinColumn on Person.cars if you do not want an extra database table to be created) or loop through Person.cars and set the Car.person property properly in each of them.
cascade=CascadeType.ALL suggests the first solution fits your use case better.
Related
I have one to many relationships between person class and car class. A person can own many cars and vice versa. I am using restful API to post data. My annotations and Get service is working fine but my post service throws " java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL" error whenever I try to insert new data. Child table foreign key is being inserted as null.
Here is part of my code.
Person.java
private List<Car> cars = new ArrayList<Car>();
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="person")
#JsonManagedReference
public List<Car> getCars() {
return cars;
}
Car.java
private Person person;
#ManyToOne
#JoinColumn(name = "PERSON_ID", nullable = false, updatable = true, insertable = true)
#JsonBackReference
public Person getPerson() {
return person;
}
My service class:
#POST
#Path("/PersonRegistration")
#Consumes(MediaType.APPLICATION_JSON)
public Response postPersonCars(Person person) throws Exception{
Session session = null;
ObjectMapper mapper = new ObjectMapper();
//Person per = new Person();
//Car cars = new Car();
try{
session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
//per.setCars(person.getCars());
session.save(person);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
throw e;
}finally{
if(null != session){
session.close();
}
}
return Response.status(201).entity(mapper.writeValueAsString(person)).build();
}
This annotation:
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="person")
has two consequences:
mappedBy implies that Car is the owning side of the relationship. This means that whenever you want to establish a relationship between Car and Person, you need to do it by setting the Car.person property to the appropriate value. Changes to Person.cars will be ignored by Hibernate.
cascade=CascadeType.ALL means that whenever you save a Person, Hibernate will also invoke the save operation on all entities contained in Person.cars
Result: you are calling Session.save() on a bunch of Car entities that do not have the Car.person property set properly.
Solution: either change the owning side of the relationship (be aware that you will also need a #JoinColumn on Person.cars if you do not want an extra database table to be created) or loop through Person.cars and set the Car.person property properly in each of them.
cascade=CascadeType.ALL suggests the first solution fits your use case better.
I am trying to delete a Patient entity without deleting the associated OutboundMessage entities as i want to keep them in the database for reporting/historical purposes. This is the relevant code :
Patient entity
#OneToMany (mappedBy="patient", fetch = FetchType.EAGER,orphanRemoval = false)
public Set<OutboundMessage> getOutboundMessages()
{
return outboundMessages;
}
OutboundMessage entity
#ManyToOne (fetch=FetchType.EAGER)
#JoinColumn(name = "id_patient")
public Patient getPatient()
{
return patient;
}
When i set a cascade type on the Patient side the records are deleted which is not what i want. When trying as shown in the code above (without the cascade type) I get the following exception :
The DELETE statement conflicted with the REFERENCE constraint "FKqjpga9w6wp3qk26ox9pg252d9". The conflict occurred in database "MDHIS", table "dbo.tblOutboundMessage", column 'id_patient'.
What settings does the owning entity need to allow deletion without cascading to children entities and without cleaning orphaned records?
Thanks!
You need to allow null values in your foreign key column (#JoinColumn) as follows:
#ManyToOne (fetch=FetchType.EAGER)
#JoinColumn(name = "id_patient", nullable = true)
public Patient getPatient() {
return patient;
}
Then in your DAO you need to set null values for all the OutboundMessage related to the Patient you're about to delete and only then delete it, as follows:
public void deletePatient(int patientId) {
Session currentSession = sessionFactory.getCurrentSession();
// get patient with primary key
Patient patient = currentSession.get(Patient.class, patientId);
Set<OutboundMessage> messages = patient.getOutboundMessages();
//set patient id null
for(OutboundMessage message : messages) {
message.setPatient(null);
}
//delete the patient
currentSession.remove(patient);
}
I have 2 entities : Account and AccountRole.
public class Account {
private AccountRole accountRole;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
public AccountRole getAccountRole() {
return accountRole;
}
.
public class AccountRole {
private Collection<Account> accounts = new ArrayList<Account>();
#OneToMany(mappedBy = "accountRole", fetch = FetchType.EAGER)
public Collection<Account> getAccounts() {
return accounts;
}
Problem comes when I take the accountRole from database and try to persist my Account. At this point I just created my account and role already exists in db.
AccountRole role = accountService.getRoleFromDatabase(AccountRoles.ROLE_USER);
account.setAccountRole(role);
//setting both ways, as suggested
public void setAccountRole(AccountRole accountRole) {
accountRole.addAccount(this);
this.accountRole = accountRole;
}
entityManager.persist(account); // finally in my DAO
I read this : JPA/Hibernate: detached entity passed to persist And what I understood, I must set the entities values from both direction, so that what I am doing in my setter.
Still getting error.
org.hibernate.PersistentObjectException: detached entity passed to persist: foo.bar.pojo.AccountRole
Just replace the
entityManager.persist(account);
with:
entityManager.merge(account);
And allow merge cascading:
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.EAGER)
public AccountRole getAccountRole() {
return accountRole;
}
Because merge does this:
If your entity is new, it's the same as a persist().
But if your entity already exists, it will update it.
It looks like you leave the transaction during your processing, so the accountRole gets detached, or it is already detached for other reasons.
A call to entityManager.merge(accountRole) before calling entityManager.persist(account) should fix it.
EDIT: Unfortunately, if you cannot be sure if the accountRole already exists in the DB, you will have to check it by querying. If it exists - merge, if not - persist. It is indeed a hassle, but I have not yet seen a better workaround.
EDIT2: The entity you pass to the merge method will remain detached - the managed entity will be returned by the merge, so you would need to merge first, then set the reference on the account to the return value of the merge.
You cannot pass a datached entity to persist, there is no way. But you don't need to.
You want to persist an Account independently of the AccountRole(which is already persisted). In order to achieve this, simply remove cascading from #ManyToOne in the child entity (Account in this case):
public class Account {
private AccountRole accountRole;
#ManyToOne // no cascading here!
public AccountRole getAccountRole() {
return accountRole;
}
See my explanation here, why: https://stackoverflow.com/a/54271569/522578
I'm having a problem deleting a child entity item. Everytime I delete it nothing happens and the association between the parent and the child is still there. I've searched through the net and some people suggest using orphanremoval but I've tried it and it didn't work. Appreciate if any could advise.
My codes as below:
ClientProfile Entity (PARENT)
Collapse | Copy Code
#Entity (name="ClientProfile")
public class ClientProfile implements Serializable {
#OneToMany(orphanRemoval = true)
private List<Address> address;
#OneToMany(orphanRemoval = true)
private List<ClientJob> clientJob;
#OneToMany(orphanRemoval = true)
private List<Asset> clientAsset;
...
}
Asset Entity (CHILD)
is a uni-directional relationship so asset entity doesnt contain any #ManyToOne
In my SQL Database table my relationship is CLIENTPROFILE_CLIENTASSET
adn they are connected by the clientid to the assetid
In my session bean this is my remove method:
#Override
public void removeAsset(Long assetId) throws DoesNotExistsException{
Query query = em.createQuery("SELECT as FROM Asset as WHERE as.assetId = :assetid");
query.setParameter("assetid", assetId);
if (query.getResultList().isEmpty()){
throw new DoesNotExistsException("Asset does not exist!");
} else {
em.remove(query.getSingleResult());
}
}
the assetid is being parsed into from the managedbean.
I'm not sure if the remove method is wrong because this is the method I used to remove other entities items without relationship.
This should help you. It is along the same lines. I usually utilize Hibernate instead of JPA to get automatic deletion through cascade attributes.
JPA OneToMany not deleting child
I have a m:n relation beetwen objects (Meeting, Person) as many persons can be participant of many meetings.
I've set it like this
Meeting
#ManyToMany(mappedBy = "meetings")
protected Set<Person> participants = new HashSet<Person>();
Person
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "person_meeting",
joinColumns = {#JoinColumn(name = "person_id")},
inverseJoinColumns = {#JoinColumn(name = "meeting_id")}
)
protected Set<Meeting> meetings = new HashSet<Meeting>();
Id DB hibernate created me table meeting_participants with two fields: meeting_id, person_id. Cool, just as I wanted.
Now problematic case. I've create Meeting object and I saved it to DB. Than I create set of users, I add it to meeting
this.saveMeeting.setParticipants(set);
Hibernate displays:
Hibernate: update Meeting set duration=?, meetingDate=?, room=? where meeting_id=?
Nothing added to association. What do I need to change ?
// EDIT
I've changed in Meeting definition of the field
#ManyToMany(mappedBy = "meetings", cascade = {CascadeType.ALL})
protected Set<Person> participants = new HashSet<Person>();
Now I get error
org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
It is in this method
public static Long add(Meeting meeting) {
SessionFactory sf = null;
Session session = null;
sf = HibernateUtil.getSessionFactory();
session = sf.openSession();
session.beginTransaction();
try{
session.save(meeting);
session.getTransaction().commit();
session.flush();
} catch(HibernateException e){
session.getTransaction().rollback();
e.printStackTrace();
return new Long(-1);
}
session.close();
return meeting.getId();
}
The line that is causing the problem is:
session.save(meeting);
EDIT
Ok I've closed session properly. Everything works find BUT only when I'm creating new objects. When I want to update association it doesn not work. So the question is. How to update association ??
This question is asked every two days. You just initialized one side of the association, and you chose the one that Hibernate ignores. A bidirectional association has an owner side, and an inverse side. Hibernate only considers the owner side. And the owner side is the one which doesn't have the mappedBy attribute.
So you need to initialize the other side of the association:
for (Participant p : set) {
p.getMeetings().add(saveMeeting);
}
I've solved the problem. I didn't think about that so I didn not wrote it. Person was a top class and it was extended by two others. I've changed update action so that it took an instance of Object class. That way I'm able to update association