2つのレベルで@mapkeyアノテーションを使用する方法は?
-
11-10-2019 - |
質問
この質問は、この他の質問に似ています 質問 私は尋ねましたが、少し異なります。
私はこれを持っています:
class A{
private List<B> bs;
...
}
class B{
private Long id;
private C c;
...
}
class C{
private Long id;
private String name;
...
}
そして、私はこれを持ちたいです:
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;
...
}
私が何をしたいのかは明らかであるかどうかはわかりませんが、マップの名前をマップの鍵として、bを値と同じようにマップと多くの関係をマッピングするのと同じくらい簡単です。
よろしくお願いします、neuquino
解決
簡単な答えは、これができないということです。
しかし、ここはあなたの問題に適している可能性のある解決策です。
エンティティでは、エンティティBとの関係をリストとして定義されている(または設定されているため、さらに優れているため、同じBを複数回抑えることができません)。
@OneToMany(mappedBy="a")
private Set<B> bs;
プレーンリストを公開したくないので、GetterとSetterを省略します 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
}
}
対処すべき最後の問題は、新しいbをAに追加するか、または削除する方法です。マップを変更できないと返す戦略を使用して、クラスAにいくつかの追加およびリムーバー方法を提供する必要があります。
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(...)
と(からインスピレーション 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...
};
他のヒント
私はあなたの恵みのための解決策を持っています。そして、それは本当に私のために働きます。
これは、@collectionofelementsの軽微な非推奨警告で動作するおもちゃの例です。あなたはそれをそれを置き換える新しい注釈に置き換えることによってそれを克服するかもしれません。
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);
}
}
3つのクラスがすべてエンティティである場合、それを変更してマッピングすることができますu003CString, C>CS、B関係からCに逆フィールドを置き、BA関係をCAに変更します。
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;
}
3つのクラスがエンティティであると思います。 Cが組み込み可能な場合、何をすべきかわかりません。