2009-08-31 4 views
2

Je refactorise certaines classes de l'utilisation de SQL standard à JPA/ORM. Dans la plupart des cas, les objets ont une référence "réelle" mais parfois les références à d'autres objets ne sont données que par une référence d'identifiant de base de données non contrôlée (pas de clé étrangère, simplement une chaîne avec une référence à un autre ID de table).refactoring de référence JPA

Le code ressemble à:

@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 */ 
} 

Comment dois-je faire face à la otherObjReference? L'attribut est utilisé par les systèmes hérités qui ont besoin de la construction getter/setter statique avec une chaîne! Si je reste avec l'ID, j'aurai des problèmes avec les requêtes JPA et je ne peux pas simplement assigner un objet et le conserver.

J'ai pensé à rendre le String transitoire et à travailler avec @PrePersist et @PreLoad pour charger une référence d'objet «réel» afin que je puisse travailler avec les deux. Mais dans ces méthodes, je n'ai pas accès à l'EntityManager (et la persistance ne devrait pas être la tâche de ce pojo dans ce cas ... ça sent mauvais design si je vais charger des références ici). Comme "otherObjReference" est privé, je peux également utiliser des getters et setters pour charger des données à partir de ma référence réelle. Mais les autres couches fonctionnent avec les objets, elles échouent donc lorsqu'elles appellent une méthode getOtherObjReference() car elles n'ont pas de connexion DB pour charger l'objet.

Répondre

2

Il est également possible (au moins avec les éléments essentiels de toplink) pour mapper la même colonne sur deux champs, à condition qu'un seul soit modifiable et insérable.

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

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

Dans votre méthode setOtherObject vous pouvez également définir otherObjectId. Le seul problème avec cette approche est que si vous ne définissez que le champ String, l'objet ne sera mis à jour que lorsque vous enregistrez et rechargez l'entité à partir de la base de données.

+0

wow, je vais essayer! –

+0

S'il vous plaît mettre à jour si cela a fonctionné sur non, c'est une discussion très intéressante –

+0

@Jorn +1 vraiment intéressant –

1

Avez-vous déjà essayé votre dernière solution?

I.E. faire un mappage JPA normal, puis rendre le getter retourner l'ID comme ça?

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

Il y a de bonnes chances que cela fonctionne même après avoir quitté les limites de votre session. Je ne suis pas sûr comment cela fonctionne aujourd'hui, mais au moins sur une ancienne version d'hibernate il mettrait un proxy paresseux dans la référence otherObj quand il a chargé votre entité, et ce proxy paresseux contiendra l'id de l'objet référencé. Donc, lorsque vous accédez seulement à l'id de l'autreObj, il serait assez intelligent de ne pas charger l'objet.

Si cela ne fonctionne pas, vous pouvez toujours forcer le chargement de l'objectRef. C'est un peu cher mais ça vaut le coup pour vous.

(BTW si vous utilisez @OneToOne et annulable = true, il chargera toujours de l'autre côté avec impatience. Voir here (regardez pour « one-to-one ») pour une courte explication)

+1

La méthode get est assez simple! Le principal problème est la méthode set() qui devrait initialiser une entité à partir d'un ID donné. L'entité elle-même doit donc charger sa référence (entitymanager injecté) qui ressemble à une odeur de code. Que faire si: * otherObj est "null" dans get()? * La signature du constructeur obtient aussi un identifiant? * Le client (sans connexion db) appelle setOtherObjId() pour affecter un autre objet? Il échoue parce qu'il n'y a pas de connexion! –

+1

@Martin vous avez absolument raison, j'ai oublié le cas d'utilisation set(). J'aurais aimé savoir comment résoudre ça ... –