La rupture d'une grande classe Hibernate
-
19-09-2019 - |
Question
J'ai une classe Hibernate qui est essentiellement juste une enveloppe autour de charges de collections.
Alors quelque chose comme la classe est (massivement simplifiée / pseudo):
@Entity public class MyClass { @OneToMany Map1 @OneToMany Map2 @OneToMany Map3 AddToMap1(); AddToMap2(); AddToMap3(); RemoveFromMap1(); RemoveFromMap2(); RemoveFromMap3(); DoWhateverWithMap1(); DoWhateverWithMap2(); DoWhateverWithMap3(); }
etc. Chacun de ces Maps a alors quelques méthodes qui y sont associés (ajouter / supprimer / interroger / etc).
Comme vous pouvez l'imaginer, le temps que j'ajouté la 10e collection ou alors, la classe devient un peu ridicule taille.
Ce que j'aimerais faire est quelque chose le long des lignes de:
@Entity public class MyClass { ClassWrappingMap1; ClassWrappingMap2; ClassWrappingMap3; }
Avec toutes les différentes méthodes enveloppées dans ces classes:
public class ClassWrappingMap1 { @OneToMany Map AddToMap(); RemoveFromMap(); DoWhateverWithMap(); }
Je pensais que je pourrais utiliser @Embedded
pour cela, mais je ne semble pas être en mesure de faire fonctionner (Hibernate ne suffit même pas essayer de persister la carte à l'intérieur du wrapperClass).
Quelqu'un at-il jamais fait quelque chose comme ça avant? Tous les conseils?
Merci,
Ned
La solution
Hibernate manuel pour les états des annotations suivantes :
Bien que pas pris en charge par la spécification EJB3, Hibernate Annotations vous permet d'utiliser les annotations d'association dans un objet embarquable (ie @ * @ * ToOne ou ToMany). Pour remplacer les colonnes d'association, vous pouvez utiliser @AssociationOverride.
Ainsi, votre approche d'emballage devrait fonctionner.
Tout d'abord, vous devez vérifier tous les fichiers journaux, etc pour les erreurs liées.
Vous pouvez essayer quelque chose comme ceci:
- Dans votre classe maître (MyClass)
@Entity public class MyClass { @Embedded ClassWrappingMap1 map1; }
- Et dans votre classe d'emballage
@Embeddable public class ClassWrappingMap1 { @OneToMany Map map1; }
Notez que ClassWrappingMap1
utilise l'annotation @Embeddable
. Toutefois, selon docs l'annotation @Embeddable ne devrait pas être nécessaire, il devrait être par défaut lors de l'annotation @Embedded est utilisée.
Assurez-vous que toutes les classes de ClassWrappingMap cartes une autre colonne dans la base de données. Aussi les classes ClassWrappingMap ne devraient pas avoir une clé primaire (ou @Id
colonnes @EmbeddedId
).
Autres conseils
Bien que je ne sais pas une stratégie par défaut lors de l'utilisation d'une classe wrapper, vous pouvez utiliser une Hibernate Interceptor pour initialiser votre wrapper de en remplaçant onLoad procédé. Quelque chose comme
public class WrapperInterceptor extends EmptyInterceptor {
private Session session;
public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
if (entity instanceof MyClass) {
MyClass myClass = (MyClass) entity;
Query query = session.createQuery(<QUERY_TO_RETRIEVE_WRAPPED_ENTITY_GOES_HERE>);
WrappedEntity wrappedEntity = query.list().get(0);
myClass.setWrapperClass(new WrapperClass(wrappedEntity));
}
}
public void setSession(Session session) {
this.session = session;
}
}
Prend en charge les éléments suivants:
Un client à l'aide intercepteur doit définir la propriété session
Alors votre code ressemble à celui-ci
WrapperInterceptor interceptor = new WrapperInterceptor();
Session session = sessionFactory().openSession(interceptor);
Transaction tx = session.beginTransaction();
interceptor.setSession(session);
MyClass myClass = (MyClass) session.get(newItem, myClassId); // Triggers onLoad event
tx.commit();
session.close();
Ou utiliser Spring AOP pour faire la même tâche. Voir Domaine Driven Design avec Spring et Hibernate
Si vous connaissez une autre stratégie, la partager avec nous.
Cordialement,