views:

301

answers:

2

I refactor some classes from standard SQL to JPA/ORM usage. In most cases the Objects have a "real" reference but sometimes the references to other objects are only given by an unchecked database id reference (no foreign key, just simple a string with a reference to another table id).

The code looks like:

@Entity
public final class myEntity implements Serializable {
    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid")
    @Column(name = "ID")
    private String id;

    @OneToOne
    @JoinColumn(name = "OBJREF", nullable = false)
    private otherObject objReference; /* Nice object reference */

    @Column(name = "OTHEROBJREF")
    private String otherObjReference; /* Damn db reference used by legacy code */
}

How should I deal with the otherObjReference? The attribute is used by legacy systems which need the static getter/setter construct with a String! If I stay with the ID I'll have problems with JPA queries and I can't simply assign an object and persist it.

I thought about making the String transient and work with @PrePersist and @PreLoad to load an "real" object reference so I can work with both. But in those methods I've no access to the EntityManager (and the persistence shouldn't be the task of this pojo in this case... it smells like bad design if I'll load references here).

Because the "otherObjReference" is private, I can also use getters and setters to load data from my real reference. But other layers work with the objects, so they'll fail when they call a getOtherObjReference() method because they've no db connection to load the object.

+1  A: 

Have you actually tried your last solution?

I.E. do a normal JPA mapping and then make the getter return the id like this?

public ID getOtherObjId() {
  return otherObj.getId();
}

There is actually a good chance that it will work even after you leave your session boundaries. I am not sure how it works today but at least on some older version of hibernate it would put a lazy proxy into the otherObj reference when it loaded your entity, and that lazy proxy will contain the id of the referenced object. So when you are accessing only the id of the otherObj it would be smart enough not to load the object.

If this doesn't work you can always force eager loading of the objectRef. It's a bit expensive but it may be worth it for you.

(BTW if you are using @OneToOne and nullable=true it will always load the other side eagerly. See here (look for 'one-to-one') for some short explanation)

Gregory Mostizky
The get method is pretty simple! The main problem is the set() method which should inizialize an entity from a given ID. So the entity itself has to load its reference (entitymanager injected) which seems like code smell. What to do if: * otherObj is "null" in get()? * The constructor signature gets also an id? * The client (without db connection) calls setOtherObjId() to assign an otherObj? It fails because there is no connection!
Martin K.
@Martin you are absolutely right, I forgot about the set() use case. I wish I knew how to solve this...
Gregory Mostizky
+2  A: 

It is also possible (at least with toplink essentials) to map the same column to two fields, as long as only one is updateable and insertable.

@Column(name = "OBJREF", nullable = false)
private String otherObjectId;

@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name = "OBJREF", nullable=false, updatable=false, insertable=false)
private OtherObject otherObject;

In your setOtherObject method you can also set otherObjectId. The only problem with this approach is if you only set the String field the object will only get updated when you save and reload the entity from the db.

Jörn Horstmann
wow, I'll give it a try!
Martin K.
Please update if it worked on not, this is very interesting discussion
Gregory Mostizky
@Jorn +1 really interesting
Gregory Mostizky
I'll try it at first with Hibernate as my persistence provider (cause I use Hibernate annotations for my primary key String generation!)
Martin K.