Pregunta

Esta pregunta es similar a esta otra pregunta le he pedido, pero ligeramente diferente.

tengo esto:

class A{

  private List<B> bs;

  ...
}

class B{

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

class C{

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

Y me gustaría tener este aspecto:

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

No sé si está claro lo que me gustaría hacer, pero es tan simple como la asignación de un uno a muchos a un mapa con el nombre de C como la clave del mapa y B como el valor.

Gracias de antemano, Neuquino

¿Fue útil?

Solución

respuesta corta es que no se puede hacer esto.

Sin embargo, una solución aquí es que puede ser adecuado para su problema:

En la entidad A todavía definida relación con la entidad B como la lista (o incluso mejor como conjunto, de manera que el mismo B no puede contenerse más de una vez).

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

A medida que usted no quiere exponer a la lista llano, getter y setter para omitir as.

A continuación, se puede definir un captador para asignar que construye el mapa sobre la marcha:

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

La última cuestión a resolver es cómo se añade un nuevo B a A o quitar uno. Con la estrategia para devolver el mapa como inmodificable, debemos proporcionar algunos métodos add y removedor en la clase A:

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

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

Otra opción es reemplazar con return Collections.unmodifiableMap(...) (inspirado en 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...
};

Otros consejos

Tengo una solución para su recompensa. Y realmente funciona para mí.

Este es un ejemplo de juguete funciona con un aviso menor depreciación en @CollectionOfElements. Es posible que lo superó por su sustitución por una anotación más nuevo que lo reemplaza.

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 todas las tres clases son entidades, se puede tratar de cambiarlo al mapa cs, poner un campo inversa en C de la relación B y cambiar la relación B-A a C-R:


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

Estoy suponiendo que las tres clases son entidades. Si C es un insertable, así que no sé qué hacer.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top