Why is @OneToMany so strange?

I’ve been working with EJB3 these days in a big way. I have to say I do like it. One of the reasons is that it’s given me a change to really play with annotations, which I’ve neglected so far.

I moved to Java SE 5 some time ago for my regular work. Mostly I was attracted to the generics capability and the enhanced for loops. The loop replaces code like


for (int i = 0; i < arr.length; i++) {
Employee emp = emps[i];
// do whatever with the emp
}

or even

for (Iterator<Employee> iter = emps.iterator(); iter.hasNext(); ) {
Employee emp = iter.next(); // assuming emps is a collection with generics
// do whatever with the emp
}

with a simple loop of the form

for (Employee emp : emps) {
// do whatever with the emp
}

Nice. Short and sweet and works for either arrays or any linear collection. I like it. The only down side is that you don’t have any way to find out where you are in the loop because the index is not available. If that’s not an issue, fine, but sometimes it is and I have to go back to the old style.

Generics is a nice idea, too, but the syntax is very ugly and awkward once you go beyond the trivial. For me, code like

Map<Integer, List<Employee>> empMap = new HashMap<Integer, List<Employee>>();

borders on the unreadable, though I’m getting used to it. I’m also sick of adding @SuppressWarning("unchecked") to all my methods that work with legacy APIs that don’t support generics.

That annotation, by the way, is one of the few I used on a regular basis. Well, that and @Override, which Eclipse adds automatically when I override the toString method (or equals(), or hashCode(), or whatever).

(It’s also sweet, by the way, that Eclipse now automatically generates appropriately-overridden equals and hashCode methods for you. I know the prescription given by Joshua Bloch in his Effective Java book. Apparently, so do the Eclipse developers.)

(Okay, one more aside. It looks like that autogenerated code is one more step for Java developers on the path to becoming Eclipse developers instead. ;))

Other than those annotations, I didn’t use annotations much. I didn’t really have the need. Even when I was working with Hibernate, I configured everything in the XML mapping files instead of using annotations.

But EJB3 is all about annotations. Your entire Object-Relational Mapping layer can be created using annotations in a manner much quicker, simpler, and easier than using XML. Those are good words, by the way. I’ll jump on the bandwagon of almost anything that is quicker, simpler, and easier.

I understand the controversy, of course. Putting annotations for the mapping properties into the source code is arguably not the proper place for it. After all, how are you going to change them later if your mapping changes? You’ll have to recompile everything. And what if you don’t have the original source, because you acquired it from a third party?

The EJB3 spec (or, more specifically, its Java Persistence API subset) allows you to override annotations through XML. I haven’t done it yet, but it looks easy enough. When developing, though, nothing seems simpler than the new JPA annotations.

For example, if I have classes Department and Employee, and the relationship between them is one-to-many (i.e., one department for many employees, but only one department for each individual employee), what could be simpler than:


@Entity
public class Employee {

    @Id private int id;

@ManyToOne
private Department department;

    // ... other stuff, including getters and setters for the attributes...
}


@Entity
public class Department {
@Id private int id;


@OneToMany(mappedBy="department")
private Collection<Employee> emps;

// ... lots of other code, with getters and setters ...

}

I mean, that’s really sweet. I’m relying on the default conventions, too, which means I’m assuming that the database schema has tables named EMPLOYEE and DEPARTMENT, each of which has a column named ID of type int. I’m also assuming that the EMPLOYEE table has a foreign key to the DEPARTMENT table in a column called “DEPARTMENT_ID”. If any of those things aren’t true, it’s certainly easy enough to add @Table, @Column, and @JoinColumn annotations as necessary.

Here’s the bizarre part, though. The relationship above works just fine, but it really relies on the Employee class having an attribute of type Department. In other words, this relationship needs to be bi-directional, at least on the Employee side. The Employee side is the “owner” of the relationship, because its associated table is the one with the foreign key in it.

For departments and employees, the relationship is quite likely to be bi-directional. But what about (as in an example based on one from the book Pro EJB3 by Keith and Schincariol) adding a Phone class for the employee that doesn’t have a reference back to the Employee?

@Entity
public class Phone {

@Id private int id;

private String number;

// … etc …

}

Now the Employee class is going to have another attribute:

private Collection<Phone> phones;

which needs an annotation, but you know what? It’s like the guy in Maine giving directions — “you can’t get theyah from heyah.” The @OneToMany annotation used in the Department to point to the Employees uses a "mappedBy" argument to tell the persistence provider to look at the annotation on Employee’s “department” attribute for the foreign key. Now there is no attribute on Phone pointing to Employee (why would there be?), so what do you do?

The JPA spec actually recommends using a link table named EMPLOYEE_PHONES, with columns EMPLOYEE_ID and PHONE_ID, and then doing the annotation on the one to many as

@JoinTable(name="EMPLOYEE_PHONES",
joinColumns=@JoinColumn(name="EMPLOYEE_ID"),
inverseJoinColumns=@JoinColumn(name="PHONE_ID"))
private Collections<Phone> phones;

Blech, and blech again. First, that’s just ugly, and second, why should I have to introduce a link table when this is a one-to-many relationship with a simple foreign key, not a many-to-many relationship? I just can’t understand why the authors of the spec decided to go that way, but go that way they did.

As much as I like JPA, I don’t get this decision at all. If anybody knows why they did that, I’d really appreciate it if they’d let me know. In the meantime, either you have to build the extra link table, or add the needed Emloyee attribute to the Phone class so everything becomes simple again.

Of course, once you do that (add the attribute), you’ve made the relationship bi-directional. That means whenever a Phone is added to an Employee, you have to remember also to add the Employee to the Phone:

public void addPhone(Phone phone) {
phones.add(phone);
phone.setEmployee(this);
}

or you’ll have a referential integrity problem.

Still, even with this issue, JPA has a great feel to it. As soon as application servers that can support EJB3 become pervasive (JBoss is already there, WebLogic has a version on the way, where the heck are you WebSphere?????), I believe this will take over the ORM space. Everybody will use these annotations instead of XML.

It really is the future.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.