views:

142

answers:

2

Can you describe the pros and cons of including an OID (typically a database row identifier) in a POJO representing an entity in your model?

In fact I'm not talking about issues related to equals/hashcode and so on, I should have described better my problem (my bad :) )...

We've got some of those entity classes which represent business objects (like Product, Catalog and so on...). Sometime they have a 'business id', for example Product can be found by its unique ProductId (which has 3 fields : id, type, repository).

In our database, the Product table has a surrogate primary key column (OID) in addition to the 3 business columns ( id, type, repository) to facilitate foreign keys references and to have less joins clauses.

The Product/ProductId classes are part of the API that we expose to other applications. So for example they can call :

productManager.findProductById(ProductId productId);

The question is, should or should not the OID be included in the Product or in the ProductId class knowing that our clients are expected to use the ProductId identifier.

Pros :

  • I can use the OID to do another lookup like

    Product p = productManager.findProductById(ProductId productId);
    Catalog c = productManager.findAllCatalogsContainingProduct(p.getOid());
    

We're used to lookup a lot in the application by ProductId so this saves each time a roundtrip to the database to avoid to find the OID matching a ProductId.

Cons :

  • I've just exposed the OID to a client (let's hope he doesn't use it instead of the business key!!)

Can you list other pros and cons?

+1  A: 

Database row identifier = Primary key? If so, there is no pro or con, you have to have it otherwise you can't relate the POJO back to its corresponding database row.

To retrieve Products and Catalogs, the standard SQL way is to do a Join. For example, with my DAL I can do:

SearchCriteria sc = new SearchCriteria();
sc.AddBinding("ProductId", productId);
List<Entity> Products = SQL.Read(sc, new Product(new Catalog());

or

List<Entity> Products = SQL.Read(sc, new Catalog(new Product());

This way there is no need to reveal anything to the caller, nor for a roundtrip.

Otávio Décio
I updated my question with more informations :)
Gilles Philippart
A: 

You can run into problems if your implementation of equals() or hashCode() is based off the identifier since it will likely be null initially and then change later once the object is persisted. See below:

http://java.sun.com/javase/6/docs/api/java/util/Set.html

Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set. A special case of this prohibition is that it is not permissible for a set to contain itself as an element.

Let's assume that your implementation of hashCode() is based off the identifier and equals() uses hashCode() in its comparison. If you add the object to a Set and its identifer is null the equals comparisons will perform one way. If you then persist the object in the set, its identifier value will likely change, thus changing the behavior of equals() and hashCode(). This breaks the "contract" of Set as described above.

It's a bit of an edge case but one worth noting.

cliff.meyers
Sure, you're right, but this wasn't the point of my question, sorry about me being too sparse about it...
Gilles Philippart