Defining Join Column Property in JPA

With JSF 2.0, EJB 3.1 Java EE has become easier to code than ever. However, JPA relationship remains a thorn in my shoes. I find it counter intuitive and just plain difficult to work with. Today, I will talk about a specific issue.

Consider two related JPA entities Employee and Organization. Employee has an unidirectional many-to-one relationship to Organization.

@Entity
public class Employee {
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private int id;
 private String name;
 private String phone;
 //uni-directional many-to-one association to Organization
 @ManyToOne
 @JoinColumn(name="organizationId")
 private Organization organization;
 //Getters and setters omitted...
}

@Entity
public class Organization {
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private int id;
 private String logo;
 private String name;
 //Getters and setters omitted...
}

And the schema (MySQL in this case):

CREATE TABLE Organization (
 id integer NOT NULL AUTO_INCREMENT,
 name varchar(128) DEFAULT NULL,
 logo varchar(128) DEFAULT NULL,
 UNIQUE KEY OrganizationU1 (cn),
 PRIMARY KEY (id)
);
CREATE TABLE Employee (
 id integer NOT NULL AUTO_INCREMENT,
 name varchar(128) NOT NULL,
 phone varchar(45) DEFAULT NULL,
 organizationId integer DEFAULT NULL,
 PRIMARY KEY (id),
 CONSTRAINT UserRegistryFK1 FOREIGN KEY (organizationId) 
  REFERENCES Organization (id)
);

This works just fine. But, I have a bone to pick. The Employee entity doesn’t have a property defined for the organizationId column. To get the organization ID, we have to load the organization and then get the ID from there.

Employee employee = em.find(Employee.class, id);
int orgId = employee.getOrganization().getId();

This seems wasteful. (In many-to-one, eager fetching is used by default. So, the code above will use a single SQL statement to load both employee and organization. Still, that is wasteful, if the Organization table has large rows).

Another problem appears when we need to create a new employee. Since, there is no way to store the organization ID within the Employee object, we must pass it separately to the DAO class:

public void addEmployee(Employee e, int orgId) throws Exception {
 e.setOrganization(
  getOrganization(orgId));
 em.persist(u);
}

This is just messy. Things will be a lot cleaner if we could store the organization ID directly within the Employee entity. As it turns out, this can be done.

@Entity
public class Employee {
 //Existing code omitted...
 //...
 @Column(name="organizationId", insertable=false, updatable=false)
 private int organizationId;
}

Basically, we are adding the organizationId property like any other mapped property. Except, we must set insertable and updatable to false. We need to do that because for insert and updates involving relationship, the related object property is used during persistence. In our case, that will be the Organization property of the Employee.

For example, now, our insert logic becomes cleaner.

public void addEmployee(Employee e) throws Exception {
 e.setOrganization(
  getOrganization(e.getOrganizationId()));
 em.persist(u);
}

Also, now, we can employ lazy loading and get the organization ID of an employee without loading the Organization.

On a side note, it just pains me to have to load the Organization before I can insert an employee. There is absolutely no business case for it. Not only is this unnecessary coding, it also adds load on the database for no good reason. For now, I will live with this to get the “convenience” of ORM.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s