JPA: how to change reference id without creating of new object - jpa

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);

Related

Entity Framework 6 - Form relationship using object which doesn't exist in the DB yet

Suppose a Company has Employees. Company 'Solutions' has Employee 'James'.
These entities are both saved in the DB, and their relationship is expressed through a foreign key.
At the application level, the Employee class has a Company object property, to define the relationship.
Suppose a new company 'Better Solutions' is created, which doesn't exist in the DB yet, and James now moves to this company.
How do I tell EF to handle this? Currently I:
Save the new company 'Better Solutions' (object created with a GUID ID) to the DB:
db.Companies.Add(newCompany);
Change the Company property on Jame's instance:
james.Company = newCompany;
Tell EF that a property on James's instance has changed and needs updating:
db.Employees.Attach(james);
db.Entry(james).State = System.Data.Entity.EntityState.Modified;
But when this happens, the newCompany object doesn't have its new database ID yet (even though it's been added to the database, the object still holds the GUID ID), so when saving EF tries to do this:
UPDATE [dbo].[Employee]
SET [CompanyID] = SomeGUID,
WHERE ([EmployeeID] = JamesID)
Which of course throws an exception because no CompanyID matches that GUID:
The UPDATE statement conflicted with the FOREIGN KEY constraint
In this scenario, do I need to first push the newCompany object to the DB, then retrieve it from the DB (to get the new ID), then set this retrieved object as James's Company property?
Or does EF have a cleaner way of taking care of all this?
try to do like below. Save company first then assign it to james that will update existing employee.
db.Companies.Add(newCompany);
db.SaveChanges();
james.Company = newCompany;
db.SaveChanges();

JPA: Unidirectional ManyToMany with existing object to be inserted

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.

History in database using entity framework

I want to store history in my table.
I have table Employee.
Employee
{
[Id],
[name],
[phone],
[isActive],
[createdDate],
[closedDate](default '23:59:59 9999-12-31'),
[DepartmentId] References Department(id)
}
When Employee is changed, I retrieve original values by Id and do isActive=False, closedDate=DateTime.Now and then I save modified value as new Employee with modified original values.
void UpdateEmployee(Employee employee)
{
ContextDB db=new ContextDB();
var employeeToUpdate=db.Employees.Find(it=>it.Id==employee.Id);
employeeToUpdate.isActive=false;
employeeToUpdate.closeDate=DateTime.Now;
var newEmployee=new Employee
{
Name=employee.Name,
Phone=employee.Phone,
....
}
db.Employees.AddObject(newEmployee);
// How I can do this with EF
db.Employees.Modify(employeeToUpdate);
db.SaveChanges();
}
How can I do this? And another question, what I need do if I have reference to another table Department and also want store history in this table. How should I do if changes Department in Employee object.
P.S. I use Self-Tracking Entities.
It should simply work without calling any Modify. You loaded entity from the database and you modified its fields while it is attached to the context so the context should know about changes and persist them to the database.
What I find totally bad about your architecture is that each change to your employee will result in active record with another Id. So if you are using and relation with employee table, foreign keys will not point to active record any more. If you want to do it this way you should not create a new record for active record but you should instead create a new record for deactivated record.

Entity Framework Code First - Table Per Type Inheritance - Insertion Issue?

I'm having an issue inserting an instance of a subclass that inherits from a base class.
Consider the following code snippets from these POCOs:
public abstract class EntityBase<T>
{
private T _id;
[Key]
public T ID
{
// get and set details ommitted.
}
}
public abstract class PersonBase<T> : EntityBase<T>
{
// Details ommited.
}
public class Applicant : PersonBase<int>
{
// Details ommitted for brevity.
}
public class Employee : Applicant {}
Pretty standard inheritance right now. In our system, when an applicant finally becomes an employee, we collect extra data. If not hired, they remain an applicant with a limited set of information.
Now consider the fluent API that sets up the table-per-type inheritance model:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Set up table per type object mapping for the Visitor Hierarchy.
modelBuilder.Entity<Employee>().ToTable("Employees");
}
So far, so good...
If I look at the database this creates, I have a table named Applicants with an Id column of type int, auto-incrementing ID and I have an Employees table with an ID field as the primary key (non auto incrementing).
Basically, the ID field in the Employees table is a foreign key to the Applicants table.
This is what I want. I don't want a record into the Employees table corresponding to the Applicants table until they actually become an Employee.
The problem comes when I try to insert an Employee which comes down to this code:
public void PersistCreationOf(T entity)
{
DataContextFactory.GetDataContext().Set(typeof(T)).Add(entity);
}
The problem: It inserts a brand new applicant and Employee. I hooked it up to the Sql Profiler and looked at both insert queries that come down.
I want to just insert the Employee record with the ID it already has (the foreign key from the Visitors table).
I understand by default it needs to this: Obviously if you create a subclass and insert it, it needs to insert into both tables.
My question is is possible to tell the Framework - the base table already has information - just insert into the child table?
Thanks in advance!
Aside from sending raw SQL commands to insert the Employee minus Applicant properties fragment into the Employees table I believe it's impossible. You can either update or insert an entity. What you want is basically to update the base part of the Employee (or do nothing if nothing changed) and insert the derived part which is not possible.
Imagine what an ORM does: It maps key identities in the database to object identities in memory. Even in memory you couldn't achieve what you want: If you have an object in memory which is a Applicant, it is always an applicant. You cannot magically "upgrade" it to an Employee. You would have to create a new object of type Employee, copy the properties of the Applicant into the base properties of your new Employee and then delete the Applicant. The result is a new object with a new object identity.
I think you have to follow the same procedure in EF. Your Employee will be a new entity with new rows in both Applicant and Employee table and you need to delete the old Applicant. If you have autogenerated keys it will be a new identity with a new ID. (If you hadn't autogenerated keys you could supply the old ID again after deleting the old Applicant, thus "faking" an unchanged identity.) This will of course create big potential trouble if you have references to the old applicant with FK constraints.
Perhaps inheritance is not optimal for this scenario to "upgrade" an applicant into an employee. An optional navigation property (1-to-0...1 relationship) inside of the Applicant which refers to another entity containing the additional properties which make the applicant an employee would solve the problem. This navigation property could be set or not, letting you distinguish between an applicant and applicant which is also an employee. And you would not need to delete and change the ID of the applicant when you make it an employee.
(As said, "I believe". Maybe there is a hidden way, I didn't see.)

Clear an EntityCollection and then Add to it within the same transaction doesn't seem possible

I have 2 entities a Department and an Employee. 1 Department can have many Employees. I would like to clear all the Employees from an existing Department, and also add a new Employee to that same department and then save the changes. It must be within a single transaction.
However when I try execute the code below I get a key violation error on the database. It seems that the clear is not deleting the items in the DepartmentEmployee table, and then inserting the new Employee.
Employee newEmployee = GetNewEmployee();
department.Employees.Clear();
department.Employees.Add(newEmployee);
EntityContext.ApplyPropertyChanges("SetName", department);
EntityContext.SaveChanges();
Any help with this would be greatly appreciated. Thanks.
I don't think you can do this in one call to SaveChanges. The Entity Framework does not guarantee any specific order of operations. So I don't think there is any way to force the DELETE to come before the INSERT without an additional call to SaveChanges.
On the other hand, you probably can do it in one database transaction. Just do it inside a new TransactionScope.
Try:
using (var t = new TransactionScope())
{
Employee newEmployee = GetNewEmployee();
department.Employees.Clear();
EntityContext.SaveChanges();
department.Employees.Add(newEmployee);
EntityContext.ApplyPropertyChanges("SetName", department);
EntityContext.SaveChanges();
t.Complete();
}