Feature 2 – updating the details of an employee

Next feature we are going to implement is updating details of an employee. Possible updates to an employee entity could be changes to properties such as first name, last name, and so on, or addition or removal of one or more benefits or communities. Remember that all updates work in almost the same way so it is more important to understand how updates work than what is being updated.

NHibernate offers multiple different mechanisms for making changes to persistent entities. Before we look into actual implementation of modification of an employee record, I want to spend some time discussing two important concepts that dictate how this feature could be implemented. Clarity around these two concepts should help you determine which mechanism you can use to update entities:

  • Partial updates versus full updates: When you want to change values of some of the properties on a persistent entity, then that will need a partial update. An example of this would be updating first name or last name on employee. Contrast that with a situation when you want to modify every other property on an entity. That would be a full update. I have rarely seen business requirements that need full updates. Most business logics require a partial update.
  • Updating detached entities versus updating persistent entities: In Chapter 5, Let's Store Some Data into the Database, we learned about entity states, particularly about detached entities. Detached entities are the entities which are present in database and in application memory but are not associated with any session object. On the other hand, persistent entities are present in both database and application memory and are also associated with session object. Being associated with a session is an important characteristic because then session is capable of tracking changes made to the entity.

Persistent entities can be updated using transitive persistence feature of NHibernate. To work with detached entities, we can make use of the Update and Merge methods available on ISession. Let's look into both the options in little more detail.

Updates using transitive persistence

In Chapter 6, Let's Retrieve Some Data from the Database, we discussed transitive persistence. Any changes made to a persistent entity are automatically tracked by session. These changes are synchronized with database either when transaction is committed or session is flushed. Nature of transitive persistence makes it suitable for both partial and full updates. We would implement a partial update where we update residential address of an employee. As with onboarding of an employee, we would add a domain service class where we would implement update employee logic. Following code shows the basic skeleton of that class:

public class UpdateEmplyeeService
{
  public void Execute(int employeeId, Address newAddress)
  {
  }
}

We have got the Execute method that takes two parameters. First one is an identifier of the employee entity whose residential address needs to be updated. Second parameter is the new residential address. We want to use transitive persistence to update the residential address. For that, we first need to load the persistent instance of employee from database. As we have done during employee onboarding feature, we would delegate this task to repository. Let's assume that we have a GetById method available on repository then UpdateEmployeeService can take a dependency on IRepository<Employee> and use the repository to load the persistent instance. Once we have persistent instance available then updating any detail is as easy as setting the relevant property to new value. Following is how the complete code looks:

public class UpdateEmplyeeService
{
  private readonly IRepository<Employee> repository;

  public UpdateEmplyeeService(IRepository<Employee> repository)
  {
    this.repository = repository;
  }

  public void Execute(int employeeId, Address newAddress)
  {
    var employee = repository.GetById(employeeId);
    employee.ResidentialAddress = newAddress;
  }
}

One important thing to remember here is that the previous mechanism works when the session that loaded the persistent entity is still open while entity is being updated (in other words, the entity being updated has not become detached). Our "session per request" implementation ensures that the session is kept open during processing of the request. Also note that the updates made to the employee instance are synchronized with database when transaction is committed. In our case, that happens at the last moment towards the end of request processing and hence actual database update happens at the point only.

For the sake of completeness, following is the implementation of the GetById method on the Repository<T> class:

public T GetById(int id)
{
  return session.Load<T>(id);
}

Note the use of the Load<T> method instead of the Get<T> method. If you recall from Chapter 5, Let's Store Some Data into the Database, Load<T> would not go to database to load the entity but only return an in-memory proxy instance with identifier set to what was passed to the method. On the other hand, Get<T> would actually hit the database to load the entity. There is no need to load the entity in order to update some properties on it.

Note

We made an assumption that identifier of the entity to be updated is known. We then used the identifier value to retrieve the persistent instance of the entity. Loading the persistent instance and having some method on repository to do is the important point here. You could have used any other property on the previous employee in place of identifier.

Updating the detached entities

Transitive persistence only works with persistent entities and is capable of performing both partial and full updates. Transitive persistence should be your preferred mechanism for implementing entity updates. We will see why after we look at the other mechanism for updating entities.

Note

I encourage you to avoid detached entities if you can. I want to cover this topic so that you know how to deal with detached entities. But the code samples that I would use will be primitive with just enough details required to support the theory.

NHibernate supports synchronizing changes made to detached entities through two different methods available on ISession. We have briefly looked at both of these methods in Chapter 6, Let's Retrieve Some Data from the Database, but let's look at them again with little more detail. First method is Merge. This method takes in a detached instance of an entity, associates it with session, and returns the persistent instance of the entity. Internally, NHibernate checks if the entity being asked to merge exists in session. If it does, then NHibernate will update the entity in session with the property values from the entity being passed. It then returns the entity in session. After this point, because you are dealing with a persistent entity, any changes made to the properties of the entity would be tracked by NHibernate. If the entity was not present in the session, then NHibernate will load the entity from database and then carry out the merge operation. Following code listing shows this in action:

using (var tx = Session.BeginTransaction())
{
  var emp = new Employee
  {
    Id = (int) id,
    Firstname = "Hillary"
  };
  emp.AddBenefit(new Leave
  {
    AvailableEntitlement = 25,
    RemainingEntitlement = 23,
    Type = LeaveType.Paid
  });
  var emp2 = Session.Merge(emp);

  emp2.EmailAddress = "[email protected]";
  tx.Commit();
}

Here, we have got an instance of the Employee class with its identifier set to some value and some other properties set. We pass this instance to the Merge method which returns another instance of the Employee class. If a record is present in database with identifier value of original instance, then this new instance represents that record in database. At the same time, state of the entity in the session is overwritten with state of the entity that we passed to the Merge method. We then updated the EmailAddress property of the persistent instance. In the end, we commit the transaction, at which point, all the changes made to the persistent entity are synchronized to database. Preceding code would generate the following SQL:

INSERT INTO Benefit (NAME, Description,
Employee_Id, Id)
VALUES (@p0, @p1, @p2, @p3 );
@p0 = NULL, @p1 = NULL, @p2 = 11, @p3 = 65537

INSERT INTO Leave (Type, AvailableEntitlement, RemainingEntitlement, Id)
VALUES (@p0, @p1, @p2, @p3);
@p0 = 0, @p1 = 25, @p2 = 23, @p3 = 65537

UPDATE Employee 
SET Firstname = @p0, 
        Lastname = @p1, 
        EmailAddress = @p2 
WHERE  Id = @p3;
@p0 = 'Hillary', @p1 = NULL, @p2 = '[email protected]', @p3 = 11

DELETE
FROM SeasonTicketLoan
WHERE Id = @p0;
@p0 = 65536

DELETE
FROM Benefit
WHERE Id = @p0;
@p0 = 65536

A major issue with Merge is that it always attempts to do a full update. What that means is, if you try to merge an entity with only few properties set to a value you want to update to and leave the remaining properties to null, then Merge will update those null properties as well thinking you intend to update those properties to null values. Following unit test explains this in detail:

[Test]
public void PartialUpdatesUsingMergeResultInInconsistentState()
{
  object id = 0;
  using (var tx = Session.BeginTransaction())
  {
    id = Session.Save(new Employee
    {
      Firstname = "John",
      Lastname = "Smith"
    });

    tx.Commit();
  }

  Session.Clear();

  using (var tx = Session.BeginTransaction())
  {
    var emp = new Employee
    {
      Id = (int)id,
      Firstname = "Hillary"
    };
    var emp2 = Session.Merge(emp);

    emp2.EmailAddress = "[email protected]";
    tx.Commit();
  }

  Session.Clear();

  using (var tx = Session.BeginTransaction())
  {
    var employee = Session.Get<Employee>(id);
    Assert.That(employee.Lastname, Is.Null);
    tx.Commit();
  }
}

In this test, we first saved an employee instance having only the Firstname and Lastname properties set to some value. We then created another instance in-memory having its Firstname property set to a new value and the Id property set to the identifier of the previously saved instance. We then merged this new instance by passing it to the Merge method. Instance returned by the Merge method, emp2, is the new persistent instance. We update the EmailAddress property on this instance hoping that it would be persisted to database. When the changes are synchronized to the database, NHibernate checked that the original entity has Lastname but merged entity has Lastname set to null so it generates following SQL statement to update Lastname to null along with Firstname and EmailAddress being updated to correct value.

UPDATE employee 
SET Firstname = @p0, 
        Lastname = @p1, 
        EmailAddress = @p2 
WHERE  Id = @p3;
       @p0 = 'Hillary', 
       @p1 = NULL, 
       @p2 = '[email protected]', 
       @p3 = 11

So use Merge carefully as it may result in properties set to null inadvertently, resulting in subtle issue and data loss.

Why transitive persistence is better

We have seen how using detached entities for update operations may lead to subtle defects resulting in data loss at times. The Merge and Update methods work perfectly fine only when they are passed with a fully hydrated entity. In other words, they only work reliably in case of full updates. Reality of building software for businesses is that on most occasions we need to deal with partial updates. For instance, in case of employee benefit management system, do you think we would have one screen where all details of an employee including employee's name to her benefits and communities can be updated? I highly doubt that. An elegant user journey would be the one where end user can add a benefit to an employee independently of updating any other details on the employee's profile. In order to facilitate such a use case using detached entities, you would need to pass all details of the employee from UI to backend so that backend can have a fully hydrated employee instance without having to go to database. This is not only a contrived way of fulfilling simple task but is also inefficient. Instead of passing the whole employee profile from UI to backend, why not just pass the identifier and load the detail from database? And if you are loading the employee instance from database then why not use transitive persistence?

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset