Comment utiliser l'annotation @MapKey avec 2 niveaux?
-
11-10-2019 - |
Question
cette question est semblable à cette autre
La solution Réponse courte est que vous ne pouvez pas le faire. Mais ici est une solution qui peut convenir à votre problème: Dans entité Une relation encore définie à l'entité B sous forme de liste (ou mieux encore comme ensemble, de sorte que le même B ne peut pas être contenu plus d'une fois). Comme vous ne voulez pas exposer la simple liste, getter Omettre et régleurs pour Ensuite, vous pouvez définir un getter pour vous associez la carte qui construit à la volée: La dernière question à aborder est de savoir comment nous ajoutons une nouvelle B à A ou supprimer un. Avec la stratégie de retourner la carte comme non modifiable, nous devons fournir des méthodes d'ajout et décapant en classe A: Une autre option consiste à remplacer @OneToMany(mappedBy="a")
private Set<B> bs;
as
. // a transient field to cache the map
private transient Map<String, B> bsMappedByCName;
public Map<String, B> getBsMappedByCName() {
if(bsMappedByCName == null) {
bsMappedByCName = new HashMap<String, B>();
for(B b : bs) {
mapB(b);
}
}
// return as unmodifiable map so that it is immutable for clients
return Collections.unmodifiableMap(bsMappedByCName);
}
private void mapB(B b) {
// we assume here that field c in class B and likely also field name in class C are not nullable. Further more both of this fields sould be immutable (i.e. have no setter).
if(bsMappedByCName.put(b.getC().getName(), b) != null) {
// multiple bs with same CName, this is an inconsistency you may handle
}
}
public void addB(B b) {
bs.add(b);
mapB(b);
}
public void removeB(B b) {
bs.remove(b);
bsMappedByCName.remove(b.getC().getName());
}
return Collections.unmodifiableMap(...)
avec (inspiré de ObservaleCollection de apache ): return new Map<String, B>() {
// implement all methods that add or remove elements in map with something like this
public B put(String name, B b) {
// check consistency
if(!b.getC().getName().equals(name)) {
// this sould be handled as an error
}
B oldB = get(name);
mapB(b);
return oldB;
}
// implement all accessor methods like this
public B get(String name) {
return bsMappedByCName.get(name);
}
// and so on...
};
Autres conseils
J'ai une solution pour votre générosité. Et cela fonctionne vraiment pour moi.
Ceci est un exemple de jouet fonctionne avec un avertissement de deprecation mineur sur @CollectionOfElements. Vous pourriez surmonta en le remplaçant par une annotation nouvelle qui la remplace.
package com.wladimir.hibernate;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import org.hibernate.annotations.CollectionOfElements;
@Entity
public class A {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@CollectionOfElements
@JoinTable
private Map<String, B> bs = new HashMap<String, B>();
public A() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Map<String, B> getBs() {
return bs;
}
public void setBs(Map<String, B> bs) {
this.bs = bs;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "A [bs=" + bs + ", id=" + id + ", name=" + name + "]";
}
}
package com.wladimir.hibernate;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class B {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@ManyToOne
private C c;
@ManyToOne
private A a;
public B() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public C getC() {
return c;
}
public void setC(C c) {
this.c = c;
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "B [a=" + a.getName() + ", c=" + c.getName() + ", id=" + id
+ ", name=" + name + "]";
}
}
package com.wladimir.hibernate;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class C {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public C() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "C [id=" + id + ", name=" + name + "]";
}
}
package examples.hibernate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
public class PersistenceUtil {
private static final SessionFactory sessionFactory;
static {
Log log = LogFactory.getLog(PersistenceUtil.class);
try {
sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
} catch (HibernateException ex) {
// Make sure you log the exception, as it might be swallowed
log.error("Initial SessionFactory creation failed.", ex);
throw ex;
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
package examples.hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Order;
import com.wladimir.hibernate.A;
import com.wladimir.hibernate.B;
import com.wladimir.hibernate.C;
import java.util.List;
import examples.hibernate.domain.*;
public class Tester{
private SessionFactory factory;
public Tester(SessionFactory factory) {
this.factory = factory;
}
@SuppressWarnings("unchecked")
public void runABC(String operation) {
Session session = factory.getCurrentSession(); // obtain/start unit of
// work
Transaction tx = null;
try {
tx = session.beginTransaction(); // start transaction
if ("Create".equals(operation)) {
A a = new A();
a.setName("A " + System.nanoTime());
C c = new C();
c.setName("C " + System.nanoTime());
session.save(c);
B b = new B();
b.setName("B " + System.nanoTime());
b.setA(a);
b.setC(c);
a.getBs().put(b.getName(), b);
session.save(b);
B b2 = new B();
b2.setName("B " + System.nanoTime());
b.setA(a);
b.setC(c);
a.getBs().put(b.getName(), b);
session.save(a);
} else if ("Read".equals(operation)) {
System.out.println("Reading data set.");
List<A> as = (List<A>) session.createCriteria(A.class)
.addOrder(Order.asc("name")).list();
for (A a : as) {
System.out.println(a);
}
}
tx.commit(); // commit transaction & end unit of work
} catch (RuntimeException ex) {
if (tx != null)
tx.rollback(); // abort transaction
throw ex;
}
}
// main application loop
public static void main(String args[]) throws Exception {
Tester app = new Tester(PersistenceUtil.getSessionFactory());
String operation = null;
if (args.length == 1) {
operation = args[0];
}
app.runABC(operation);
}
}
Si toutes les trois classes sont des entités, vous pouvez essayer de le changer à la carte
class A {
// the map should have c.name as key
@OneToMany
private Map<String, C> cs;
...
// This method is for getting the B for a key, like it was before.
public B getB(String key) {
return cs.get(key).getB();
}
}
class B {
private Long id;
@OneToOne // Or perhaps @ManyToOne.
private C c;
...
}
class C {
private Long id;
private String name;
@OneToOne(mappedBy="c") // Or perhaps maybe @OneToMany
private B b;
...
@ManyToOne(mappedby="cs")
private A a;
}
Je suis en supposant que les trois classes sont des entités. Si C est un intégrable, donc je ne sais pas quoi faire.