Just listen to Alex

July 1, 2009

Fake Java properties and how they improve JPA

Filed under: programming — Tags: , — bosmeeuw @ 5:52 pm

It doesn’t look like Java will be getting real property support in our lifetimes. This is too bad, because being able to refer to properties of an object in a type-safe way is really valuable when developing applications with a large domain. Take a gander a this JavaBean:

@Entity
public class Person {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Let’s say I’ve developed a 50.000 class, 10 million lines of code application using this JavaBean as my domain model. I’m of course using JPA. My business logic is riddled with heavily complex JPQL statements such as this one:

Person leaderOfTheFreeWorld = em.createQuery(
  "FROM Person p WHERE p.name = :name"
).setParameter("name", "Barack Obama").getSingleResult();

Now the users demand the impossible, and tell me they want to keep the first name and last name of people in seperate fields. After adding a “firstName” field to my JavaBean and maybe migrating the data, I need to change my heavily complex JPQL query to this:

Person leaderOfTheFreeWorld = em.createQuery(
   "FROM Person p WHERE p.name = :name AND p.firstName = :firstName"
).setParameter("name", "Obama").setParameter("firstName", "Barack").getSingleResult();

That’s fine really, but remember I have 50.000 classes with 10 million lines of code, and I need to make sure all queries that use the “name” field are updated. What do I do? A text search for “name”? I’m guessing a lot of hits are coming my way. How do I make sure I’ve covered all references to the name field? The answer is, of course: fake properties!

Let’s change the original JavaBean to this:

public class Person {
    
    private String name;
    public static final String NAME = "name";
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Notice the public static final reference to my field. Now look at this type safe way to refer to my model in JPQL:

em.createQuery(
   "FROM " + Person.class.getName()  + " p WHERE p." + Person.NAME + " = :name"
).setParameter("name", "Barack Obama").getSingleResult();

What does that get you?

  1. Autocompletion: Your IDE autocompletes the “Person.class” and “Person.NAME” parts of the query. No having to look at the model class to remember if the field was called “name” or “birthGivenPersonalDiscriminator”
  2. Navigability: you can jump to the declaration of the Person from within your query
  3. Refactor safety: you can rename the Person class, and the name field. Of course for the name field, you will need to modify your static reference as well. But it’s right there under your field declaration, so it’s very hard to forget this
  4. Traceability: you can ask your IDE to show you all instances where the “Person.NAME” property is accessed and make sure you have seen all usages if you need to do some refactoring or just need to know where something is used.
  5. Compile time safety: delete the name field on person (and the static reference), and your compiler will show you were this will cause problems
  6. You can use the property reference for other things, such as form field binding, i18n for field labels, ..

What are the downsides to this approach?

  1. The query looks uglier / harder to read. Of course, if you were using Hibernate Criteria or some kind of JPQL builder to avoid messy string concatenation, the property access would blend in much better and be easier on your fingers.
  2. You will have a static field for every field in your models, which is a little bit cluttering when reading the source code. If this bothers you, you can put the property references in a seperate class like PersonProps.
  3. You need to add the static fields for every field, which is a bit of extra typing and you’re lazy. I’m also lazy, so I made a little eclipse template to automate this somewhat:

eclipse-propref

You can add this to eclipse by going to Java >> Editor >> Templates, and adding a template named “propref” for context “Java” with this pattern:

${line_selection};

public static final String ${newName} = "${word_selection}"

Now when you’re in your model, select the field declaration, press control space, and select “propref”.

Yes, this method is complete overkill for a one-bean application, it’s just an example.

Advertisements

3 Comments »

  1. A more radical choice would be to encapsulate all your queries in Repositories class instead of cluttering the code with JPQL/HQL, because it resolves the problem at the root when you add the :firstName part only in one class, PersonRepository, and maybe in the Repositories that reference it.

    Comment by Piccolo Principe — July 2, 2009 @ 9:33 pm

  2. Thanks for your input! I do think it’s hard to keep this up when you a large domain model. To really take advantage of your relational database, you’ll soon have many queries that span a bunch of your entities, i.e. get a Customer record with Invoice statistics, or an Article record with sales figures. When you need to pull data like that, it can be hard and bad for performance to consolidate all queries for a given entity to one repository.

    Also, check out a link someone has posted as a comment on DZone: http://code.google.com/p/liquidform/
    For just JPA, it looks like a nicer and cleaner solution.

    Comment by bosmeeuw — July 2, 2009 @ 10:01 pm

  3. The string concatenation approach becomes a little more difficult with complex dynamic queries. For a typesafe approach consider using Querydsl : http://source.mysema.com/display/querydsl/Querydsl

    Querydsl is a more compact and expressive alternative to the JPA 2 Criteria API.

    Comment by Timo Westkämper — October 31, 2010 @ 2:37 pm


RSS feed for comments on this post. TrackBack URI

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

Create a free website or blog at WordPress.com.

%d bloggers like this: