User {
Set<Book> _books;
#ManyToMany(cascade={CascadeType.MERGE})
#JoinTable(
name="BLETAG_LABEL",
joinColumns={#JoinColumn(name="TAG_ID", referencedColumnName="ID")},
inverseJoinColumns={#JoinColumn(name="LABEL_ID", referencedColumnName="ID")})
Set<Book>getBooks();
}
Book {
}
. If I set the CascadeType to PERSIST and I try to create a User with an existing book (book1) like this, an unique constraint is fired...
Book book1 = new Book("book1");
user.getBooks().add(book1);
. My current solution is to the the CascadeType to MERGE and check for all user.getBooks() if the book already exist. If the book does not exist, I need to create it (because not CascadeType.PERSIST)
Is it the right solution ?
Is there any better "automatic" solution available for this kind of use case ?
As long as you use the Book from the current persistence context, cascade persist should work fine, and only persist the new Books. If your Book is detached, then you first need to merge or find it in the current persistence context.
Related
for example, I have entity Employee that refers to Department
#ManyToOne(fetch=FetchType.LAZY,
cascade={CascadeType.PERSIST, CascadeType.MERGE})
private Department department;
I want to save new Employee, but I already know Department id (it is 9L). Right now I save it like
em.getTransaction().begin();
final Employee emp = new Employee();
emp.setFirstName("A");
emp.setLastName("L");
emp.setDepartment(em.getReference(Department.class, 9L));
em.persist(emp);
em.getTransaction().commit();
So, each time I need to find Department in DB, or, at least, get object from proxy, and then call setDepartment.
Is it possible to set department_id of Employee instance without creating new Department object?
Thanks.
The short answer is no. Your cascading rules require a reference to an existing Department or a new one will be created.
http://howtodoinjava.com/2014/09/25/hibernate-jpa-cascade-types/
CascadeType.PERSIST : means that save() or persist() operations cascade to related entities.
CascadeType.MERGE : means that related entities are merged into managed state when the owning entity is merged.
If you want to avoid the look up, you'll need to change your cascade rules.
Yes, but it comes at a cost. You would need to make the Employee->Department reference read-only (insertable=false, updatable=false) to allow you to add a basic mapping to the Employee to use to set the department_ID directly. You then will be responsible for setting the department ID manually in each case, and note that the Employee.department reference may become out of synch if you do not maintain it. That means if you do not set the relationship, it may be null even though the basic mapping for the field has a value until you force a refresh.
Ie:
em.getTransaction().begin();
final Employee emp = new Employee();
emp.setFirstName("A");
emp.setLastName("L");
emp.setDepartment_id(9L);
em.persist(emp);
em.getTransaction().commit();
and if needed:
em.refresh(emp);
Say i have class called Record, with a many-to-one relationship to a class called Artist.
If i define a cascade option as such:
class Record{
#ManyToOne(cascade = CascadeType.ALL)
private Artist artist;
...
}
Say i have a number of records managed by my EntityManager and some of these share the same Artist.
What happens when i call, update, merge, detach, remove and so on on the Record objects?
Will the artist be removed for example? Will it be detached? If so, what happens to the other Record classes that references that Artist?
Since you configured the association with cascade = CascadeType.ALL, all the operations done on a record will also be done on the associated Artist. Removing the record will thus remove the artist. This will fail with a foreign key constraint exception (if they're correctly configured in the database) if another record also references the same artist.
Configuring such a cascade on a ManyToXxx associations doesn't make much sense.
I know there have been a number of similar posts about this, but I couldn't find a clear answer to my problem.
To make it as simple as possible, say I have such an entity:
#Entity
public class Person implements Serializable {
#Id
private Long id; // PK
private String name; // business key
/* getters and setters */
/*
override equals() and hashCode()
to use the **name** field
*/
}
So, id is the PK and name is the business key.
Say that I get a list of names, with possible duplicates, which I want to store.
If I simply create one object per name, and let JPA make it persistent, my final table will contain duplicate names - Not acceptable.
My question is what you think is the best approach, considering the alternatives I describe here below and (especially welcome) your own.
Possible solution 1: check the entity manager
Before creating a new person object, check if one with the same person name is already managed.
Problem: The entity manager can only be queried by PK. IS there any workaround Idon't know about?
Possible solution 2: find objects by query
Query query = em.createQuery("SELECT p FROM Person p WHERE p.name = ...");
List<Person> list = query.getResultList();
Questions: Should the objects requested be already loaded in the em, will this still fetch from database? If so, I suppose it would still be not very efficient if done very frequently, due to parsing the query?
Possible solution 3: keep a separate dictionary
This is possible because equals() and hashCode() are overridden to use the field name.
Map<String,Person> personDict = new HashMap<String,Person>();
for(String n : incomingNames) {
Person p = personDict.get(n);
if (p == null) {
p = new Person();
p.setName(n);
em.persist(p);
personDict.put(n,p);
}
// do something with it
}
Problem 1: Wasting memory for large collections, as this is essentially what the entity manager does (not quite though!)
Problem 2: Suppose that I have a more complex schema, and that after the initial writing my application gets closed, started again, and needs to re-load the database. If all tables are loaded explicitly into the em, then I can easily re-populate the dictionaries (one per entity), but if I use lazy fetch and/or cascade read, then it's not so easy.
I started recently with JPA (I use EclipseLink), so perhaps I am missing something fundamental here, because this issue seems to boil down to a very common usage pattern.
Please enlighten me!
The best solution which I can think of is pretty simple, use a Unique Constraint
#Entity
#UniqueConstraint(columnNames="name")
public class Person implements Serializable {
#Id
private Long id; // PK
private String name; // business key
}
The only way to ensure that the field can be used (correctly) as a key is to create a unique constraint on it. You can do this using #UniqueConstraint(columnNames="name") or using #Column(unique = true).
Upon trying to insert a duplicate key the EntityManager (actually, the DB) will throw an exception. This scenario is also true for a manually set primary key.
The only way to prevent the exception is to do a select on the key and check if it exists.
I am developing a web application using JSF and JPA(Eclipse link). I have two entities with bidirectional OneToMany relationship. The owner entity is contact and target entity is customer. Single customer can have multiple contacts, like email, phone, etc. When the end user is adding a new customer, he also adds the contacts straight away. There is a need to cancel the saving of a new customer, even after adding contacts to that customer. I tried to add that functionality, but failed in the following way.
Can that senario be achieved directly by persistence?
Contact Entity
....
public class Contact implements Serializable {
....
#ManyToOne
Customer customer;
....
Customer Entity
....
public class Customer implements Serializable {
....
#OneToMany(mappedBy = "customer")
private List<Contact> contacts;
....
Adding a new contact to Customer (current is an object of Customer class)
Contact contact = new Contact();
contact.setCustomer(current);
....
current.getInstitutionContacts().add(contact);
This works when the current is already a persisted one. If I tried to add a contact to yet to persist one, there is a java.lang.NullPointerException.
I can work around to achieve the functionality, but is there any way we can just collect the contacts to the array and persist them only when (and if only) the customer is persisted? By using cascade persist or lazy fetch, etc?
Sounds like you want Contacts to be Components, not Entities.
The difference is that an entity has it's own lifecycle; it lives outside the scope of its association, and deleting the parent does NOT necessarily have to delete the child. Also, if a child is an Entity, other classes can also have relationships with that child.
Components are completely bound to the parent. They automatically go away if the parent goes away. They cannot be referenced by other associations or by other Entities. It's like they are simple properties of the parent class.
The only caveat is that I don't know if all JPA implementations support having a collection of components.
See this documentation. Particularly the part that says: "You can also use association annotations in an embeddable object (ie #OneToOne, #ManyToOne, #OneToMany or #ManyToMany). To override the association columns you can use #AssociationOverride."
If the JPA implementation you are using does, you can use the #Embeddable annotation and #OneToMany
Edit: -- I also found info here http://en.wikibooks.org/wiki/Java_Persistence/Embeddables#Collections.
I am working with Entity Framework ... I have a database table for Patient which has a non-enforced foreign key relationship to the Employee table so I can associate a manager to a patient.
I created my entity in EF for the Patient, the Employee and an association between Patient and Employee, which I name to ManagerEmployee.
I also created another partial class for Patient that will allow me to easily get at the name of the employee from my business object class, also called Patient.
public string ManagerName
{
get { return this.ManagerEmployee == null ? string.Empty : this.ManagerEmployee.Name; }
}
So I have:
Patient Entity
Patient Partial Class
(to help with some of the data
retrieval)
Patient DTO (reads from
the Patient Entity)
The problem that I am having is that if the ManagerId (in this case is a Guid) does not related to an employee, or is not set (Guid.Empty) ... even though I am eager loading, it still makes another hit on the database.
IQueryable<Data.Patient> query = ctx.ObjectContext.Patients.Include("ManagerEmployee");
So if I have a 1000 records, that have this value set, all is well, but if the value for ManagerId is NOT set on any of these records, it makes 1+1000 database hits.
Wondering if anyone else has had this problem? There may be some bigger problem with the construction of my EF entities and/or associations, so I'm open to other suggestions.
Thanks!
This is now pretty old but in case you haven't already found the solution, my suggestion is to turn off lazy loading. What is most likely happening is that when you try to access a property that is null, lazy loading is happening. See
http://www.asp.net/entity-framework/tutorials/maximizing-performance-with-the-entity-framework-in-an-asp-net-web-application
if you're using database first, or
http://www.asp.net/entity-framework/tutorials/reading-related-data-with-the-entity-framework-in-an-asp-net-mvc-application
for MVC Code First.