Helló,
csak elsőre hangzik bonyolultan, viszonylag könnyű megérteni a problémát:
Van egy szintetikus teszt entitásosztályom1:
@Entity
public class MyClass implements Serializable {
private static final long serialVersionUID = 1L;
public MyClass() { }
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@ElementCollection(fetch=FetchType.EAGER)
private Map<String, String> testMap;
public Map<String, String> getTestMap() { return testMap; }
public void setTestMap(Map<String, String> testMap) { this.testMap = testMap; }
}
És az alábbi GWT-RPC szervizem:
@Override
public MyClass getMyClass() {
MyClassJpaController controller = new MyClassJpaController();
MyClass m = controller.findMyClass(1L);
return m;
}
Ahol a
MyClassJpaController
a NetBeans által generált wrapper osztály az
EntityManager
persist, find, merge, remove metódusaihoz. Konkrétan a create függvény egy entitásobjektumot perzisztál az adatbázisban.
Maga a getMyClass() metódus kliensoldalra juttatja a MyClass típusú objektumot. Ez a legtöbb esetben működik is. Kipróbáltam, ha az entitásosztálynak Long, Integer, String, List<String>, List<SajátTípus>, Date, stb. mezői vannak, akkor szépen megjelenik kliens oldalon a várt osztály.
Azonban, a fent bemutatott osztállyal nem működik, méghozzá a Map miatt. A következő hibaüzenetet dobja a GlassFish:
com.google.gwt.user.client.rpc.SerializationException: Type 'java.util.Hashtable' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = {MasodikKey=MasodikValue, ElsoKey=ElsoValue}
Nyílván a MasodikKey=MasodikValue, ElsoKey=ElsoValue értékek és kulcsok tesztadatok a testMap mezőben.
Ám ha a GWT-RPC szervizt így valósítom meg:
@Override
public MyClass getMyClass() {
MyClassJpaController controller = new MyClassJpaController();
MyClass m = controller.findMyClass(1L);
Map<String, String> map = new HashMap<String, String>(controller.findMyClass(1L).getTestMap());
m.setTestMap(map);
return m;
}
Azaz kimásolom a Map-et a kapott entitásobjektumból egy HashMapbe, majd visszaírom, akkor működik2.
A kérdésem a következő: Hogyan lehetne a GWT-t rábírni, hogy mégis serializálja a Hashtable-t, vagy a GlassFish JPA megvalósítását (vagy az EntityManagerFactoryt, vagy az EntityManagert, vagy bármit) rábírni, hogy amint az entitásosztály megjárja a JPA-rétegeket, ugyanaz a HashMap legyen a mezeje, mint előtte?
Előre is köszönöm a válaszokat.
- 1:Természetesen az entitásosztályok tudnak utazni GWT-RPC-n, azt már megoldottam, kipróbáltam különféle mezőket tartalmazó entitásosztályokkal, és működik, a probléma lényege nem ez.
- 2:Illetve akkor is működik, ha EntityManager és adatbázis nélkül, magam felépítek egy objektumot, és beállítok neki egy HashMapet, és azt akarom visszaadni GWT-RPC-n keresztül
Workaround a problémára
atomheart javasolta, hogy egy @PostLoad annotációval ellátott függvényben játszam el a HashMapbe kimásolás, majd ezen változó betétele az entitásosztályba játékot, így született meg az alábbi snippet:
@PostLoad
public void fix() {
try {
Map<String, String> fixMap_String_String
= new HashMap<String, String>(this.mapStringTypeStrigTypeField);
this.mapStringTypeStrigTypeField = fixMap_String_String;
Map<TestReferencedType, String> fixMap_TestReferencedType_String
= new HashMap<TestReferencedType, String>(this.mapReferencedTypeStrigTypeField);
this.mapReferencedTypeStrigTypeField = fixMap_TestReferencedType_String;
} catch (Exception e) {
System.out.println("Problem during fixing Entity for GWT-RPC: " + e.toString());
}
}
Megjegyzés: Ez a @PostLoad azért is jó, mert pl.: EXT-GWT-vel dolgozom, és ott egy Gridben történő megjelenítéshez szükség van ModelData-t kibővítő osztályok implementálására. Ha az entitásom extendálja a BaseModelDatat, majd a specifikus függvényeit meghívja egy @PostLoad függvényben, akkor egyből az entitásosztályt használhatom mindenhol. Ezt csak azért írtam le, hátha valaki idetéved hasonló problémákkal.
- 1738 megtekintés
Hozzászólások
elso ranezesre az a baja, hogy konkret tipusokhoz van serialization policy, viszont a JPA-ba bevont map nem HashMap, hanem csak az interfesze latszodik, interfeszt szerializalni meg izgi ;)
ha igy akarod megoldani, akkor irj az interfeszhez policyt, szerintem mukodik.
- A hozzászóláshoz be kell jelentkezni
Ha esetleg ebben az interfacehez policy írásban segíteni tudnál, azt nagyon meg tudnám köszönni
(Ja, ofkoz Java newbie, EE+GWT wannabe, stb. vagyok)
- A hozzászóláshoz be kell jelentkezni
ha nagyon nem megy, osszedobok valami tesztkornyezetet, de most nincs fent semmi :)
- A hozzászóláshoz be kell jelentkezni
igen, ezt az oldalt én is néztem, ez legalább akkora workaround, mint HashMapbe átpakolni, majd visszamenteni a dolgokat. Mindjárt leírom, végül mire jutottam.
- A hozzászóláshoz be kell jelentkezni
GWT csak bizonyos alap java osztályokat tud átküldeni. Itt le vannak írva melyikeket. A baj az, hogy a java.util.Hashtable nincs közöttük a java.util.HashMap meg igen. Amikor HashMap-et használsz azt ismeri, viszont a Glassfish-es JPA implementáció (TopLink talán?) Hashtable-t rak be. A kézi átforgatáson kívül (amit csinálsz is) én nem sok egyéb esélyt látok a megoldásra. A JPA kezelt collection-ökkel szerialázáláskor (nem csak gwt hanem pl jaxb esetén is) elég sok baj van, főleg ha még lazy-ben is lennének kiszedve (nálad ez eager úgyhogy ez neked most nem okoz problémát).
Egy másik lehetőséged, hogy a TopLink helyett Hibernate-t használsz, az talán HashMap-et tesz be ilyen esetben.
- A hozzászóláshoz be kell jelentkezni
Igen, én is erre jutottam. Valószínűleg minden entitásosztályom egy GwtRpcFixNeeded absztrakt interfészből fog származi, amely interfésznek public Típus fix(); függvényét kell implementálni. Az implementáció majd megcsinálja a Map-et, majd visszatér this-zel. Illetve az összes nested entitásosztály fix()-ét is meghívja.
- A hozzászóláshoz be kell jelentkezni
Inkabb modositsd a gettert, hogy hozza letre elso hivaskor a HashMap-et es irja felul a Hashtable adattagot. Vagy annotalj @PostLoad-al egy metodust az entitasodban, ami elvegzi ezt betoltes utan!
----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"
- A hozzászóláshoz be kell jelentkezni
Ezt találtam hirtelen:
http://www.java-forums.org/new-java/31396-obsolete-collection-import-ja…
Nem tudom, hogy ennek van-e köze a problémához - pl. hogy a Google csak a HashMap-et támogatja, mert csak. Az is lehet, hogy mivel néhol HashMap-et írsz, Hashtable-t - nem mindenhol ugyanaz van kódodban, és az egyik helye a Map mögé Hashtable-t raksz. Próbáld ki, ha teheted, hogy minden Hashtable használatot HashMappel váltasz ki!
- A hozzászóláshoz be kell jelentkezni
A kód kb. ennyi. Csak egy szintetikus teszt kód, sehol nem használk HashTablet.
- A hozzászóláshoz be kell jelentkezni
neked nem is kell; ugye mivel JPA kezeli az entitasod fieldjeit is, igy o injektal oda valami kollekciot; alapbol a Mapre HashMapet (a Setre HashSetet) injektal.
- A hozzászóláshoz be kell jelentkezni
Én azt próbálnám ki hogy nem Map-t adok meg hanem HashMap-et:
private HashMap testMap;
Illetve kipróbálnám Set-tel.
- A hozzászóláshoz be kell jelentkezni
Már nem emlékszem pontosan, de HashMap-pel valami gondja volt a JPA-nak.
- A hozzászóláshoz be kell jelentkezni
Köszönöm szépen mindenkinek az építő ötleteket, javaslatokat, linkeket, különösen persicsb-nek az IM-en keresztüli ötletelést.
- A hozzászóláshoz be kell jelentkezni
Szivesen, bar en nem adtam ra megoldast.
- A hozzászóláshoz be kell jelentkezni