Saturday, March 31, 2012

Composite IDs in Hibernate

Consider the following database table which contains a list of users and the times they last accessed a certain website:
FirstNameLastNameWebsiteAccessTime
ArthurDentGoogle2012-03-30 07:09:00.0
PeterGriffinYahoo!2012-03-30 10:36:00.0
Let's say that you want to use both the FirstName and LastName fields to uniquely identify records in the table and perform searches/updates using Hibernate. There are different ways to define a "composite primary key" and, in this post, I will show you an approach I have used successfully in the past, which involves creating an EmbeddedId.

The @EmbeddedId approach:
The code is shown below and is pretty self-explanatory. It consists of a User class which contains a static nested UserId class. The latter holds the first name and the last name attributes and thus represents the composite ID.

import java.io.Serializable;

import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;

import org.hibernate.annotations.Type;
import org.joda.time.DateTime;

/**
 * The User entity which contains an embedded UserId.
 */
@Entity
@Table(name = "Users")
public class User {

  @EmbeddedId
  private UserId id;

  private String website;

  @Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
  private DateTime accessTime;

  /**
   * Default constructor required by Hibernate.
   */
  public User() {
  }

  /**
   * @param id the user id
   */
  public User(UserId id) {
    this.id = id;
  }

  /**
   * @return the id
   */
  public UserId getId() {
    return id;
  }

  /**
   * @param id the id to set
   */
  public void setId(UserId id) {
    this.id = id;
  }

  /**
   * @return the website
   */
  public String getWebsite() {
    return website;
  }

  /**
   * @param website the website to set
   */
  public void setWebsite(String website) {
    this.website = website;
  }

  /**
   * @return the accessTime
   */
  public DateTime getAccessTime() {
    return accessTime;
  }

  /**
   * @param accessTime the accessTime to set
   */
  public void setAccessTime(DateTime accessTime) {
    this.accessTime = accessTime;
  }

  /**
   * This represents a "composite primary key" for the Users table.
   * It contains all the columns that form a unique id.
   * Must implement equals() and hashcode() and be serializable.
   * https://community.jboss.org/wiki/EqualsAndHashCode
   */
  @Embeddable
  public static class UserId  implements Serializable {

    private static final long serialVersionUID = 1L;

    private String firstName;
    private String lastName;

    /**
     * Default constructor required by Hibernate.
     */
    public UserId() {
    }

    /**
     * @param firstName the first name
     * @param lastName the last name
     */
    public UserId(String firstName, String lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
    }

    /**
     * @return the firstName
     */
    public String getFirstName() {
      return firstName;
    }

    /**
     * @param firstName the firstName to set
     */
    public void setFirstName(String firstName) {
      this.firstName = firstName;
    }

    /**
     * @return the lastName
     */
    public String getLastName() {
      return lastName;
    }

    /**
     * @param lastName the lastName to set
     */
    public void setLastName(String lastName) {
      this.lastName = lastName;
    }

    /** (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
      result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
      return result;
    }

    /** (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      UserId other = (UserId) obj;
      if (firstName == null) {
        if (other.firstName != null)
          return false;
      } else if (!firstName.equals(other.firstName))
        return false;
      if (lastName == null) {
        if (other.lastName != null)
          return false;
      } else if (!lastName.equals(other.lastName))
        return false;
      return true;
    }
  }
}
Usage:
The code snippet below shows how you would search for a user and update its access time:
UserId userId = new UserId("Peter", "Griffin");

// search for a user
User user = entityManager.find(User.class, userId);
if (user == null) {
    // the user doesn't exist, so create one
    user = new User(userId);
}
user.setAccessTime(new DateTime());

// update the user
entityManager.merge(user);

Sunday, March 25, 2012

Mocking with Mockito

Here is an example of using Mockito to mock a service. The service used in this example, is a fictitious PersonService which returns Person objects based on their name. It might do this by connecting to a external database. In our unit tests, we don't want to connect to an external database, hence the reason for creating a mock. The mocked version shown below always returns a new Person object with the name that was passed into the service.
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;
import static org.junit.Assert.*;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class PersonServiceTest {

  @Mock private PersonService personService;

  @Before
  public void setUp() {
    MockitoAnnotations.initMocks(this);
    when(personService.getPerson(anyString())).thenAnswer(
      new Answer<Person>() {
        @Override
        public Person answer(InvocationOnMock invocation) throws Throwable {
          Object[] args = invocation.getArguments();
          return new Person((String)args[0]);
        }
      });
  }

  @Test
  public void testGetPerson(){
    assertEquals("Alice", personService.getPerson("Alice").getName());
  }
}

Saturday, March 24, 2012

Eclipse: Writing Better, Faster Java Documentation

JAutodoc:
I've started using JAutodoc, which is an Eclipse plugin for automatically adding Javadoc to your source code. It helps generate the initial Javadoc for methods which don't have them and can even complete existing Javadoc by adding missing parameters and return types. I have found this plugin very useful in writing documentation fast.

Let's say you have the following method which adds two ints:

public int add(int x, int y) {
    return x + y;
}
To invoke JAutodoc, all you have to do is hit Ctrl+Alt+J inside the method and the following Javadoc template will be automatically generated, which you can then complete.
/**
 * Adds the.
 *
 * @param x the x
 * @param y the y
 * @return the int
 */
public int add(int x, int y) {
    return x + y;
}
Later on, if you decide to change this method by adding another parameter to it, you can press Ctrl+Alt+J again and JAutoDoc will add the new parameter to the Javadoc but leave the rest of it unchanged.

Enabling Eclipse Javadoc warnings:
I've also found it useful to turn on Eclipse warnings for missing or malformed Javadoc comments. You can do this by going to Window > Preferences and then selecting Java > Compiler > Javadoc. Tick the box for processing Javadoc comments and select your desired severity levels. I've got mine set to Warnings for everything. You can get my Eclipse preferences from my git repository.