I have two entities Travel and Assurance with #OneToOne relationship. Both entities must be created via the same interface with a Save botton. I use this method:
ManagedBean.java:
public String add(){
newTravel = manager.createTravel(arrivalDate, returnDate, lengthToStay, addToStay, visitPurpose);
newAssurance = manager.createAssurance(company, assuranceStart, assuranceEnd, newTravel);
return "Travellers";
}
In the database, I found the Travel_Id associated to the Assurance but The Assurance_Id is null in the Travels Table.
It seems that your relationship is not bilateral (i.e you do not use the mappedBy annotation property). If you used one, you would have only one column (either Travel_Id or Assurance_Id, depending where you put the mappedBy).
Also consider doing the saving inside the same transaction, e.g by using the same manager method and setting both side of the relationship.
Related
entity = MainDao.findByUUID(getEntityManager(), dto.getUuid());
Adress i = new AdressDao().findById(dto.getIdAdress());
i.setPhone(dto.getPhone());
entity.setAdress(i);
return MainDao.update(getEntityManager(), entity);
I have a main Entity in which there is a #ManytoOne relationship to Adress. I want to update the field "phone" inside adress, how do I do it? My code fails to do so.
Hope you can help me out, it seems there is no "patch" method inside JPA. I would love to know the best practices.
By default #ManyToOne doesn't cascade the changes (as it refers to a parent which may be have other child associations).
You can do either of below,
save the changes of Address entity via your AddressDao like addressDao.save(addressEntity)
use #ManyToOne(cascade = CascadeType.ALL).
1st options is preferable.
Read about CascadeType to utilize wisefully.
I have an old database where there are two tables with implicit association between them:
booking
- id
- name
... some other fields...
and
booking_info
- id
- booking_id
... some other fields...
Due to the current database design there no any constraints between these two tables, which means that one booking entry may exist without any booking_info entries and vice versa. booking_id in booking_info table is an implicit foreign key which refers to booking table (column id), but it also may refer to the absent booking.
I have created the following JPA mapping for these tables:
#Entity
public class Booking {
#Id
private Long id;
private String name;
// getters & setters
}
and
#Entity
public class BookingInfo {
#Id
private Long id;
#OneToOne
#JoinColumn(name = "booking_id")
private Booking booking
// getters & setters
}
Now I need to be able to persist a BookingInfo entity even if there's no related Booking entry in the database.
BookingInfo bookingInfo = new BookingInfo();
bookingInfo.setId(1);
Booking booking = new Booking();
booking.setId(182); // let's say that there's no booking with id 182 in my database, but I still need to persist this bookingInfo
bookingInfo.setBooking(booking);
bookingInfoRepository.save(bookingInfo); // using Spring Data JPA
If I run this code then I get javax.persistence.EntityNotFoundException since booking with id 182 is absent.
What would be the proper workaround for my case using JPA or Hibernate.
Btw, I also tried to use Hibernate's #NotFound annotation. As a result, save method doesn't throw javax.persistence.EntityNotFoundException and entity gets persisted int the database, but the problem is that booking_id in the database always null.
Any help would be appreciated.
I am not sure my answer will help you or not, but the result you are getting perfectly make sense. As you are setting a JPA object, and that object is not present, hence the null value is saved.
If you want to save 182 as an integer, you don't do JPA relationship. Instead, you just use booking-id as an integer field in booking-info. And that makes sense because you actually do not have the relationship between those tables which the JPA is trying to achieve.
But I am sure you just want to save 182 and as well as maintain the JPA relationship. And I am sure you already know it, but DB integrity is not being maintained with the approach you are taking. I am sure there is enough reason behind that. But my recommendation would be applying proper constraints in the DB and then in JPA.
I am a bit confused about managing relationship in JPA.
basically I have two entities with a One to Many relationship
A configuration can have have a one or many email list associated with it.
#Entity
public class Config {
#OneToMany(mappedBy="owner",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
private List<Email> emailReceivers;
}
#Entity
public class Email {
#ManyToOne
private Config owner;
}
In an EJB and during update/merge operation wherein I would edit the list of emails associated with a configuration,
I thought that I dont need to explicitly call the delete operation on my email Entity and I would just manage the relationship by deleting the email in my configuration email list.
#Stateless
public class ConfigFacadeImpl implements ConfigFacade{
#EJB
private ConfigDao configDao;
#EJB
private EmailDao emailDao;
#Override
public void update(Config Config, List<Email> emailsForDelete) {
if(emailsForDelete!=null && emailsForDelete.size() > 0){
for(Email emailTemp: emailsForDelete){
Email email = emailDao.find(emailTemp.getId());
emailDao.delete(email); // Do I need to explicitly call the remove??
config.getEmailReceivers().remove(email);
}
}
configDao.update(config);
}
}
If I don't execute the delete and only remove it from the list, it wont erase my table row.
The UI and the database is now not in sync as the UI would not show the email(s) that I have deleted but when you check the database, the row(s) are still there.
Is it required? I thought JPA would handle this for me if I would just remove it in my entities.
UPDATE
I have tweaked my code to get the entity from the database first before making any changes but still it is not deleting my child email entities. I wonder if this is an apache derby issues. (This is the correct way right as I am passing my entities from my JSF managed bean into my EJB so I need to get the sync from the DB first.)
#Override
public void update(Config config, List<Email> emailsForDelete) {
Config configTemp = configDao.find(config.getId());
if(emailsForDelete!=null && emailsForDelete.size() > 0){
for(Email emailTemp: emailsForDelete){
configTemp.getEmailReceivers().remove(emailTemp);
}
}
configDao.update(config);
}
Since you have already defined cascade type = CascadeType.ALL, JPA should take care of the deletion. Explicit Delete statement is not required.
These two statements are not required:
Email email = emailDao.find(emailTemp.getId());
emailDao.delete(email); // Do I need to explicitly call the remove??
Instead, you may want to just find the matching emailReceiver in config.getEmailReceivers() and remove the matching EmailReceivers as you are doing. There is no need to load the Email entity from the database.
EDIT: To delete orphan objects, you may want to include CascadeType.DELETE_ORPHAN cascade attribute along with CascadeType.ALL.
This is the same issue as in Why merging is not cascaded on a one to many relationship
Basically, JPA can only cascade over entities in your collection. So changes to child objects removed from the collection are never putinto the context, and so can't be pushed to the database. In this case, the oneToMany is controlled by the manytones back pointer, so even collection changes won't show up unless the child is also merged. Once a child is pruned from the tree, it needs to be managed and merged individually for changes to it to be picked up.
With JPA 2.0, you can use the option orphanRemoval=true in parent entity
Example:
#Entity
public class Parent {
...
#OneToMany(mappedBy="parentId",cascade=CascadeType.ALL, orphanRemoval=true)
private List<Child> childList;
...
}
I have a question about JPA-2.0 (provider is Hibernate) relationships and their corresponding management in Java. Let's assume i have a Department and an Employee entity:
#Entity
public class Department {
...
#OneToMany(mappedBy = "department")
private Set<Employee> employees = new HashSet<Employee>();
...
}
#Entity
public class Employee {
...
#ManyToOne(targetEntity = Department.class)
#JoinColumn
private Department department;
...
}
Now i know i have to manage the Java relationships myself, as in the following unit test:
#Transactional
#Test
public void testBoth() {
Department d = new Department();
Employee e = new Employee();
e.setDepartment(d);
d.getEmployees().add(e);
em.persist(d);
em.persist(e);
assertNotNull(em.find(Employee.class, e.getId()).getDepartment());
assertNotNull(em.find(Department.class, d.getId()).getEmployees());
}
If i leave out either e.setDepartment(d) or d.getEmployees().add(e) the assertions will fail. So far, so good. What if i commit the database transaction in between?
#Test
public void testBoth() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Department d = new Department();
Employee e = new Employee();
e.setDepartment(d);
d.getEmployees().add(e);
em.persist(d);
em.persist(e);
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
em.getTransaction().begin();
assertNotNull(em.find(Employee.class, e.getId()).getDepartment());
assertNotNull(em.find(Department.class, d.getId()).getEmployees());
em.getTransaction().commit();
em.close();
}
Do i still need to manage both sides of the relation? No, as it turns out, i don't have to. With this modification
e.setDepartment(d);
//d.getEmployees().add(e);
the assertions still succeed. However, if i only set the other side:
//e.setDepartment(d);
d.getEmployees().add(e);
the assertions fail. Why? Is is because the Employee is the owning side of the relation? Can i change that behavior by annotating differently? Or is it just always the "One" side of the "OneToMany" that determines when the foreign key field in the database is filled?
I don't know what your test is trying to demonstrate but the fact is you must handle both sides of the association when working with bidirectional associations. Not doing so is incorrect. Period.
Update: While the spec reference mentioned by axtavt is of course accurate, I insist, you definitely must set both sides of a bi-directional association. Not doing so is incorrect and the association between your entities in the first persistence context is broken. The JPA wiki book puts it like this:
As with all bi-directional relationships it is your object model's and application's responsibility to maintain the relationship in both direction. There is no magic in JPA, if you add or remove to one side of the collection, you must also add or remove from the other side, see object corruption. Technically the database will be updated correctly if you only add/remove from the owning side of the relationship, but then your object model will be out of synch, which can cause issues.
In other words, the only correct and safe way to manage your bidirectional association in Java is to set both sides of the link. This is usually done using defensive link management methods, like this:
#Entity
public class Department {
...
#OneToMany(mappedBy = "department")
private Set<Employee> employees = new HashSet<Employee>();
...
public void addToEmployees(Employee employee) {
this.employees.add(employee);
employee.setDepartment(this);
}
}
I repeat, not doing so is incorrect. Your test only works because you're hitting the database in a new persistence context (i.e. a very particular situation, not the general one) but the code would break in many other situations.
Entity relationships in JPA have owning and inverse sides. Database updates are determined by the state of the owning side. In your case Employee is an owning side due to the mappedBy attribute.
From the JPA 2.0 specification:
2.9 Entity Relationships
...
Relationships may be bidirectional or
unidirectional. A bidirectional
relationship has both an owning side
and an inverse (non-owning) side. A
unidirectional relationship has only
an owning side. The owning side of a
relationship determines the updates to
the relationship in the database, as
described in section
3.2.4.
The following rules apply to bidirectional relationships:
The inverse side of a bidirectional
relationship must refer to its owning
side by use of the mappedBy element of
the OneToOne, OneToMany, or ManyToMany
annotation. The mappedBy element
designates the property or field in
the entity that is the owner of the
relationship.
The many side of
one-to-many / many-to-one
bidirectional relationships must be
the owning side, hence the mappedBy
element cannot be specified on the
ManyToOne annotation.
For
one-to-one bidirectional
relationships, the owning side
corresponds to the side that contains
the corresponding foreign key.
For
many-to-many bidirectional
relationships either side may be the
owning side.
The reason why the second test in a new persistence context succeeds if you only update the owning side in a previous context is that the persistence provider obviously can't know that when persisting you did not update the inverse side as well. It only cares about the owning side for persistence purposes. However, when you get persistent objects from a persistence provider, the provider sets the bidirectional associations properly on both sides (it is simply assumed they were persisted properly, too). However, as many others here have already pointed out, it is not the responsibility of the persistence provider to complete newly created bidirectional associations and you should always properly maintain bidirectional associations in your code.
I have Person entity which has composition with Location Entity
#ManyToOne(fetch = FetchType.EAGER, cascade =
{ CascadeType.PERSIST, CascadeType.MERGE })
#Cascade(
{org.hibernate.annotations.CascadeType.SAVE_UPDATE })
public Location getLocation()
{
return location;
}
And Location Entity has Name as Id
#Id
public String getName()
{
return name;
}
I am getting following Exception when Person's location is changed from L1 to L2 in Spring MVC form where this Person entity is modelAttribute for the form.
org.springframework.orm.hibernate3.HibernateSystemException:identifier of an instance of com.x.y.z.Location was altered from L2 to L1; nested exception is org.hibernate.HibernateException: identifier of an instance of com.x.y.z.Location was altered from L2 to L1
You're confusing Composition with Association.
What you have mapped is an association; composition in Hibernate (JPA) is mapped via #Embeddable / #Embedded annotations. Associations are relationships between separate entities; they are usually connected via entity identifiers (foreign keys in the database).
In your particular case, Person entity points to Location entity which means in the database PERSONS table has a LOCATION_ID foreign key (names may differ) to LOCATIONS table. What you're trying to do is to update that key on Location end which is illegal because it would sever Hibernate's relationship (the other end still holds the previous key value internally).
Primary keys should generally be surrogate and not updatable to begin with; if you do need to "update" it you'll have to either disassociate Location from Person, update Location and assign it to Person again OR create a brand new Location instance and assign that to your Person.
All that said, if you're really trying to model Composition relationship, you need to replace #ManyToOne with #Embedded and change your table schema accordingly. Here's a link to
Hibernate Annotations documentation on mapping components.
Also, specifying cascade types in two separate annotations (JPA vs Hibernate extension) is not a good thing. If you really need the Hibernate extension one (which you don't in this case), just use it and leave cascade attribute in JPA annotations empty.
I done same thing in standalone application . The thing works. I think it should be some problem with #modelAttribute.
In your Location entity attribute id type has been changed in your model class.Please refer the id and mapping attribute id types are same.Make sure that id attribute getter and setter function return types.