3

I'm developing my first project with JPA, with MySQL as my database and Hibernate 4.3.8 as my JPA provider in a Spring 4 web project.

In my Spring config, I set the database and the dialect:

HibernateJpaVendorAdapter hjpaVA = new HibernateJpaVendorAdapter();
hjpaVA.setDatabase(Database.MYSQL);
hjpaVA.setDatabasePlatform("org.hibernate.dialect.MySQL5Dialect");

I try to get this query that gives me trouble:

TypedQuery<KundeDTO> query = entityManager.createQuery("select new zdb.dto.KundeDTO(k.id, k.firma.firmenname, k.regnr, k.kategorie) 
from Kunde k where k.id = :id", KundeDTO.class);

This is the SQL that Hibernate generates:

select kunde0_.`id` as col_0_0_, firma1_.`firmenname` as col_1_0_, kunde0_.`regnr` as col_2_0_, kunde0_.id_kategorie as col_3_0_ 
from `zdb_e`.`Kunde` kunde0_, `zdb_e`.`Firma` firma1_ 
inner join `zdb_e`.`Kategorie` kategorie2_ on kunde0_.id_kategorie=kategorie2_.`id` 
where kunde0_.id_firma=firma1_.`id` and kunde0_.`id`=1;

Note that there are no parentheses on the inner join!

Running this statement results in the following error:

com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: 
Unknown column 'kunde0_.id_kategorie' in 'on clause'

The reason for the exception is detailed here: mysql-unknown-column-in-on-clause

When I add the parentheses to the where and the inner join clauses and run the statement directly against the database it works:

select kunde0_.`id` as col_0_0_, firma1_.`firmenname` as col_1_0_, kunde0_.`regnr` as col_2_0_, kunde0_.id_kategorie as col_3_0_ 
from (`zdb_e`.`Kunde` kunde0_, `zdb_e`.`Firma` firma1_ )
inner join `zdb_e`.`Kategorie` kategorie2_ on 
( kunde0_.id_kategorie=kategorie2_.`id` ) 
where kunde0_.id_firma=firma1_.`id` and kunde0_.`id`=1;

So, how can I persuade Hibernate to generate the query like that?

update: here are the entities

Kunde

@Entity
public class Kunde implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Integer id;

    private Integer regnr;

    @OneToOne(optional=false)
    @JoinColumn(name="id_firma", nullable = false)
    private Firma firma;

    @OneToOne(optional=false)
    @JoinColumn(name="id_kategorie", nullable = false)
    private Kategorie kategorie;

    @OneToOne(optional=false)
    @JoinColumn(name="id_lieferregion", nullable = false)
    private Lieferregion lieferregion;

    // getters and setters....
}

Firma

@Entity
@Table(name = "Firma")
public class Firma implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue
    private Integer id;

    @Column(name="firmenname")
    private String firmenname;
    @Column(name="uid")
    private String uid;

    @OneToOne(optional=false)
    @JoinColumn(name="id_anschrift", nullable = false)
    private Anschrift anschrift;

    @OneToMany(mappedBy="id_firma", fetch=FetchType.EAGER)
    private List<Person> personen;

    public Firma() {
        personen = new ArrayList<Person>();
    }
    // getters and setters....
}

Kategorie

@Entity
public class Kategorie implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Integer id;

    private Integer nummer;
    private String bezeichnung;

    public Kategorie() {
    }

    public Kategorie(int kategorieId, int kategorieNummer, String kategorieBezeichnung) {
        this.id = kategorieId;
        this.nummer = kategorieNummer;
        this.bezeichnung = kategorieBezeichnung;
    }
    // getters and setters....
}

DB schemas

CREATE TABLE kategorie 
    (id INTEGER NOT NULL AUTO_INCREMENT, 
    nummer INTEGER NOT NULL, 
    bezeichnung VARCHAR(100) NOT NULL,
    PRIMARY KEY (id),        
    UNIQUE (nummer, bezeichnung)
    );

CREATE TABLE firma
    (ID INTEGER NOT NULL AUTO_INCREMENT, 
    firmenname VARCHAR(50) NOT NULL,
    uid VARCHAR(20) NOT NULL,
    url VARCHAR(100) NOT NULL,
    id_anschrift INTEGER NOT NULL,
    id_logo INTEGER,
    PRIMARY KEY (id),
    UNIQUE (uid),
    UNIQUE (firmenname),
    UNIQUE (id_anschrift),
    CONSTRAINT firma_fk1 FOREIGN KEY (id_anschrift) REFERENCES ANSCHRIFT (ID),
    CONSTRAINT firma_fk2 FOREIGN KEY (id_logo) REFERENCES logo (ID));

CREATE TABLE kunde 
    (id INTEGER NOT NULL AUTO_INCREMENT, 
    regnr INTEGER NOT NULL,
    id_kategorie INTEGER NOT NULL,
    id_firma INTEGER NOT NULL,
    id_benutzer INTEGER,
    id_lieferregion INTEGER NOT NULL,
    PRIMARY KEY (id), 
    UNIQUE (regnr, id_kategorie),
    UNIQUE (id_firma),
    UNIQUE (id_benutzer),
    CONSTRAINT kunde_fk1 FOREIGN KEY (id_firma) REFERENCES firma (id),
    CONSTRAINT kunde_fk2 FOREIGN KEY (id_benutzer) REFERENCES benutzer (id),
    CONSTRAINT kunde_fk3 FOREIGN KEY (id_kategorie) REFERENCES kategorie (id),
    CONSTRAINT kunde_fk4 FOREIGN KEY (id_lieferregion) REFERENCES lieferregion (id)
    );

upon further testing

The problem are the missing parentheses on the from clause.

Going directly against the db:

select k.id, k.regnr, f.firmenname from (Kunde k, Firma f) JOIN kategorie kat on k.id_kategorie = kat.id where k.id = 1 and k.id_firma = f.id;

works!

select k.id, k.regnr, f.firmenname from Kunde k, Firma f JOIN kategorie kat on k.id_kategorie = kat.id where k.id = 1 and k.id_firma = f.id;

Doesn't work: Unknown column 'k.id_kategorie' in 'on clause'

Why do I even need the parentheses on the from clause?
And how can I make Hibernate put them in?

Community
  • 1
  • 1
Nordic
  • 61
  • 5
  • The error is not the same as the one you link to. There is no `AND` following the ON clause in your query, and the error clearly talks about a non-existing column. Check your database schema. – JB Nizet May 11 '15 at 17:08
  • @JBNizet the OP says the statement works when he adds the parens ... strong hint that the columns do exist. – Jens Schauder May 11 '15 at 18:26
  • Paste your Kunde and Kategorie classes. You can also try sth like this "select new zdb.dto.KundeDTO(k.id, k.firma.firmenname, k.regnr, kat) from Kunde k JOIN k.kategorie kat where k.id = :id" – jgr May 11 '15 at 18:54
  • @JBNizet The schema seems correct, all the colums exist. – Nordic May 12 '15 at 08:27
  • @jgr Tried it. (edited my question accordingly) The problem are the parentheses on the from clause. – Nordic May 12 '15 at 08:37
  • I think u cant use 'on' in JPQL u shoudl do it my way like this: 'from Kunde k JOIN k.kategorie kat'. Show us your Kundle and Kategorie class how u defined mapping. Also are you sure u have required getters and setters methods? – jgr May 12 '15 at 08:56
  • Wait: your JPQL query's from clause contains a single entity. How comes the SQL query's from clause contains two tables? What's the code of the entity? – JB Nizet May 12 '15 at 11:06
  • @jgr I don't 'use on in JPQL'. My JPQL query is this: "select new zdb.dto.KundeDTO(k.id, k.firma.firmenname, k.regnr, k.kategorie) from Kunde k where k.id = :id" The join is in the SQL that Hibernate generates. The getter an setter methods can't be the problem as the generated SQL fails to execute. – Nordic May 13 '15 at 07:09
  • @jgr I tried your way and it works, see my reply to the original question. – Nordic May 13 '15 at 07:10
  • @JBNizet It is because firma and kategory are seperate tables. I have updated the original post to include the entites. – Nordic May 13 '15 at 07:12
  • Can we see the MySQL schema's? Sounds like the JPA is configured incorrectly. I have done a lot of queries on MySQL with Hibernate and never had a problem like this. – Namphibian May 13 '15 at 07:31
  • @Namphibian Yes, this is my presumption as well. I set the database to MySQL and the dialect to "org.hibernate.dialect.MySQL5Dialect". I don't know what I could do more. I added the db schemas to the original post. – Nordic May 13 '15 at 07:58

1 Answers1

0

I didn't get it to work with JPQL, so I did as jgr suggested in the comments and wrote my own sql native query:

String sql = "select k.id, f.firmenname, k.regnr, kat.id, kat.nummer, kat.bezeichnung from (zdb_e.Kunde k, zdb_e.Firma f) JOIN zdb_e.kategorie kat on k.id_kategorie = kat.id where k.id = :id and k.id_firma = f.id;";
Query query =  entityManager.createNativeQuery(sql, schemaName), "KundenListeRow");
query.setParameter("id", id);
return (KundeDTO) query.getSingleResult();

For the mapping into my Pojo KundeDTO I added @SqlResultSetMapping to the Kunde Entity:

@Entity
@SqlResultSetMapping(name = "KundenListeRow",
classes = {
    @ConstructorResult(targetClass = KundeDTO.class,
            columns = {
                    @ColumnResult(name = "id"),
                    @ColumnResult(name = "firmenname"),
                    @ColumnResult(name = "regnr"),
                    @ColumnResult(name = "kat.id"),
                    @ColumnResult(name = "kat.nummer"),
                    @ColumnResult(name = "kat.bezeichnung")
                    }
            )
  })
public class Kunde implements Serializable {
     ....
}

And the corresponding constructor in the DTO:

public class KundeDTO {
    int id;
    String firmenname;
    Kategorie k;
    int regnr;

    public KundeDTO(int id, String firmenname, int regnr, int kategorieId, int kategorieNummer, String kategorieBezeichnung) {
        this.id = id;
        this.firmenname = firmenname;
        this.regnr = regnr;
        this.k = new Kategorie(kategorieId, kategorieNummer, kategorieBezeichnung);
    }
    public KundeDTO() {
    }
}

As I said, it works. But it is not ideal. With JPQL I could use a TypedQuery and not have to deal with the resultSetMapping.

Of course, I still don't know why my JPQL doesn't work. :-)

Nordic
  • 61
  • 5