2011-01-16 8 views
3

diese Frage ist ähnlich zu diesem anderen question Ich habe gefragt, aber etwas anders.Wie verwendet man die Annotation @MapKey mit 2 Ebenen?

Ich habe dies:

class A{ 

    private List<B> bs; 

    ... 
} 

class B{ 

    private Long id; 
    private C c; 
    ... 
} 

class C{ 

    private Long id; 
    private String name; 
    ... 
} 

Und ich möchte diese haben:

class A{ 

    // the map should have b.c.name as key 
    private Map<String,B> bs; 

    ... 
} 

class B{ 

    private Long id; 
    private C c; 
    private A a; 
    ... 
} 

class C{ 

    private Long id; 
    private String name; 
    ... 
} 

Ich weiß nicht, ob es klar ist, was würde ich mag es tun, aber es ist so einfach wie das Zuordnen einer eins zu vielen Beziehung zu einer Karte mit dem Namen von C als Schlüssel der Karte und b als Wert.

Vielen Dank im Voraus, Neuquino

+0

Verwenden von Anmerkungen oder XML-Konfiguration? – skaffman

+0

Der Fragetitel sagt Anmerkungen ... – Neuquino

Antwort

4

Kurze Antwort ist, dass Sie dies nicht tun können.

Aber hier ist Lösung für Ihr Problem geeignet sein kann:

In Einheit A noch definierte Beziehung zu Einheit B als Liste (oder sogar besser als Set, so dass die gleiche B nicht mehr als einmal enthalten sein).

@OneToMany(mappedBy="a") 
private Set<B> bs; 

Wie Sie wollen nicht auf die Ebene Liste belichten, lassen Sie Getter und Setter für as.

Dann können Sie einen Getter definieren für Sie Karte, dass die Karte im laufenden Betrieb baut:

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

Das letzte Problem zu lösen ist, wie wir ein neues B nach A hinzufügen oder ein entfernen. Mit der Strategie, die Karte als nicht änderbar zurückzukehren, müssen wir einige Add- und Entferner Methoden in der Klasse A bieten:

public void addB(B b) { 
    bs.add(b); 
    mapB(b); 
} 

public void removeB(B b) { 
    bs.remove(b); 
    bsMappedByCName.remove(b.getC().getName()); 
} 

Eine andere Möglichkeit ist, zu ersetzen return Collections.unmodifiableMap(...) mit (inspiriert von ObservaleCollection from 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... 
}; 
0

Ich habe eine Lösung für dein Kopfgeld. Und es funktioniert wirklich für mich.

Dies ist ein Spielzeugbeispiel, das mit einer kleinen Verwarnungswarnung für @CollectionOfElements arbeitet. Sie könnten es überwinden, indem Sie es durch eine neuere Anmerkung ersetzen, die es ersetzt.

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); 

    } 
} 
+0

Das Problem ist nur, dass Neuquino 'b.c.name' als Schlüssel in der Karte und nicht nur' b.name' wollte – lweller

+0

nur ändern a.getBs(). Put (b.getName(), b); bis a.getBs(). Put (b.getC(). GetName(), b); – Wladimir

+0

, aber bei dieser Lösung wird die Information 'name' zweimal gespeichert (eine als Schlüssel in Tabelle B und einmal in Entität C), was eine Redundanz darstellt, die zu Dateninkonsistenz führen kann wie'! A.getBs(). Get ("myKey") .equals (a.getBs(). get ("myKey"). getC(). getName()) ' – lweller

0

Wenn alle drei Klassen Einheiten sind, können Sie versuchen, es zu ändern < String auf der Karte, C > cs, ein inverses Feld in C von der B-Beziehung setzen und den BA Bezug auf CA ändern:


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; 
} 

Ich nehme an, dass die drei Klassen Entitäten sind. Wenn C ein Embeddable ist, weiß ich nicht, was ich tun soll.

Verwandte Themen