Avatar billede obix Nybegynder
09. maj 2010 - 22:13 Der 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?

På forhånd tak.

Obix.
Avatar billede arne_v Ekspert
09. maj 2010 - 22:59 #1
Jeg har aldrig prøvet noget sådant.

Men jeg gætter på at du selv skal lave en klasse som implementerer ResultTransformer og kalde:

query.setResultTransformer(new MySuperResultTransformer());
Avatar billede obix Nybegynder
09. maj 2010 - 23:11 #2
Hej Arne.

Ja jeg er også kommet frem til det du foreslår. Problemet er bare jeg ikke kan se hvordan hibernate regner ud hvilken Type den skal instantiere.
Avatar billede obix Nybegynder
09. maj 2010 - 23:13 #3
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ø.
Avatar billede arne_v Ekspert
09. maj 2010 - 23:17 #4
Lad mig prøve og se om jeg kan bixe et eksempel.
Avatar billede obix Nybegynder
09. maj 2010 - 23:29 #5
Ok :-)
Avatar billede arne_v Ekspert
10. maj 2010 - 02:51 #6
Det her virker hos mig:

        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.
Avatar billede arne_v Ekspert
10. maj 2010 - 04:11 #7
Lidt mere robust:

        System.out.println("Tricky conversion:");
        Query qqq = s.createQuery("SELECT a.id AS id,a.name AS name,a.miceperday AS miceperday,a.litermilkperday AS litermilkperday FROM Animal AS a");
        qqq.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") && vals[i] != null) {
                        miceperday = (Integer)vals[i];
                    }
                    if(nams[i].equals("litermilkperday") && vals[i] != null) {
                        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> a7list = qqq.list();
        for(Animal a7 : a7list) {
            System.out.println(a7);
        }
Avatar billede obix Nybegynder
10. maj 2010 - 11:00 #8
Hejsa.

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.
Avatar billede arne_v Ekspert
10. maj 2010 - 15:03 #9
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.
Avatar billede obix Nybegynder
10. maj 2010 - 20:06 #10
Hej Igen.

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.
Avatar billede Ny bruger Nybegynder

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.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester