09. maj 2010 - 22:13Der er
9 kommentarer og 1 løsning
Hibernate hql
Hejsa.
Jeg har en abstract super klasse dyr med flere subklasser kat ko osv.
Jeg har en hql
select n from dyr as n
Hvis jeg fyrer ovenstående af får jeg et fint resultat hvor alle objekterne i resultsettet er af de rigte dyr typer. Men hvis jeg f.eks. skriver
select n.id, n.navn from dyr as n og kører query.setResultTransformer(Transformers.aliasToBean(Dyr.class));
Så forsøger hibernate at instantiere Dyr klassen men det dur jo ikke eftersom dyr er abstract og jeg vil også hellere have at resultatet har de rigtige typer. I mit eksempel selecter jeg mere ud end bare id så jeg har brug for at objekterne instantieres rigtigt.
Er der nogle der ved hvordan Hibernate løser problemet når man bare selecter alt? Eller evt. har et andet løsningsforslag. Er der en magisk attribut jeg skal tilføje i mit select der gør at hibernate automatisk instantierer de rigtige klasser uden en transformer?
Jeg har set man kan skrive select n.class, osv... from dyr as n så skulle n.class fortælle hvilken klasse det er men her får jeg et nummer retur og jeg tør ikke rigtigt kode op imod det hvis det nu ændrer sig fra miljø til miljø.
Query qq = s.createQuery("SELECT a.id AS id,a.name AS name,a.litermilkperday AS litermilkperday FROM Animal AS a WHERE name LIKE :name"); qq.setParameter("name", "K%", Hibernate.STRING); qq.setResultTransformer(new ResultTransformer() { @Override public List transformList(List lst) { return lst; } @Override public Object transformTuple(Object[] vals, String[] nams) { int id = -1; String name = null; int miceperday = -1; int litermilkperday = -1; for(int i = 0; i < nams.length; i++) { if(nams[i].equals("id")) { id = (Integer)vals[i]; } if(nams[i].equals("name")) { name = (String)vals[i]; } if(nams[i].equals("miceperday")) { miceperday = (Integer)vals[i]; } if(nams[i].equals("litermilkperday")) { litermilkperday = (Integer)vals[i]; } } if(miceperday >= 0) { Cat res = new Cat(); res.setId(id); res.setName(name); res.setMiceperday(miceperday); return res; } else if(litermilkperday >= 0) { Cow res = new Cow(); res.setId(id); res.setName(name); res.setLitermilkperday(litermilkperday); return res; } else { throw new RuntimeException("WTF"); } }
}); List<Animal> a6list = qq.list();
Det skal nok tilrettes lidt fordi dit setup er nok anderledes.
Det fungerer jo fint. Det eneste der ireterer mig grusomt er at vi bruger @Inheritance(strategy = InheritanceType.JOINED) så vi har ikke nogen discriminator column jeg kan læse ud og spørge hvilken type jeg skal instantiere. Jeg har endda prøvet at tilføje den men den bliver fuldstændig ignoreret pga. joined strategien.
Så jeg har løst det ved at tilføje en kolonne jeg selv vedligeholder og den kan jeg så bruge til at vælge hvilken type der skal instantieres.
En lille ændring i forhold til dit eksempel.
public Object transformTuple(Object[] tuple, String[] aliases) { if(tuple[0].equals(DyrType.Kat)){ return new AliasToBeanResultTransformer(Kat.class).transformTuple(tuple, aliases); } else if(tuple[0].equals(DyrType.Hund)) { Object o = new AliasToBeanResultTransformer(Hund.class).transformTuple(tuple, aliases); return o; } else {throw new RuntimeException("WTF"); } }
Tak for hjælpen. Vil du lave et svar så du kan få point.
Ah ja. Naar du har fundet ud af hvad type det er, saa kan du naturligvis falde tilbage til AliasToBeanResultTransformer.
Medmindre alle subklasserne har de samme felter (og hvorfor saa bruge subklasser ??), saa kan du vel bruge min teknik med at teste for null og undgaa den diskriminator.
Jeg har tilføjet kolonnen for at undgå at hibernate joiner med de underlæggende tabeller. Udsøgningen er til en liste og det er først når man går til detail mode at man har brug for resten af data.
Mvh. Obix.
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.