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.

Saturday, February 04, 2012

Visual GC Error: Not Supported for this JVM

The Visual GC plugin for Visual VM graphically displays garbage collection, class loader, and HotSpot compiler performance data.

If you install this plugin and, like me, get the error "Not Supported for this JVM", when you try to start Visual GC, try the following steps:

  1. Start jstatd on the remote host. You need to specify a policy file, otherwise you will get an AccessControlException: access denied exception. Create a policy file e.g. /tmp/tools.policy containing:
    grant codebase "file:${java.home}/../lib/tools.jar" {
       permission java.security.AllPermission;
    };
    
    Then start jstatd using the command below:
    jstatd -J-Djava.security.policy=/tmp/tools.policy
    
  2. Start Visual VM. Add a Remote Host and then add a "jstatd connection" to it. Your JVM should appear in the list. You can then click on it and look at the "Visual GC" tab for garbage collection information.

Saturday, January 21, 2012

Play My Code: Wacky Cannon

I have always wanted to make a projectile game and now I have! I've just published Wacky Cannon on Play My Code. The aim of the game is to control the cannon using your mouse and click to shoot the purple targets. See how many levels you can complete!

Here's Wacky Cannon!

Play My Code is a place where you can create your own online games easily using the site's Quby language. The games are compiled into native JavaScript, compatible with all modern HTML5-compliant browsers. Once you've written a game, you can embed it on your website or blog, like I have done above. It's like YouTube, but for games! So, if you think you've got a game in you, head over to Play My Code and write one.

Click here to see all my games, including the popular Chain Reaction!

Did you like Wacky Cannon? Share your thoughts in the comments below.

Related posts:
Play My Code: Chain Reaction