Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Complex order-by clause into .hbm hibernate files

I have a question about hibernate and order-by clause. I have a mySQL database containing 3 tables: A, B and C. The Java code corresponding to those tables is hereafter.

Class A code:

package database;

import java.util.Set;

public class A implements java.io.Serializable {

    private Integer id;
    private Set<B> listB;

    public A() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Set<B> getListB() {
        return listB;
    }

    public void setListB(Set<B> listB) {
        this.listB = listB;
    }

}

Class B code:

package database;

public class B implements java.io.Serializable {

    private Integer id;
    private A a;
    private C c;

    public B() {
    }

    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }

    public C getC() {
        return c;
    }

    public void setC(C c) {
        this.c = c;
    }
}

Class C code:

package database;

public class C implements java.io.Serializable {

    private Integer id;
    private int num;

    public C() {
    }

    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public int getNum() {
        return this.num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}

The associated .hbm files are here

Table A mapping:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="database">
    <class name="A" table="A">
        <id
            column="id"
            name="Id"
            type="integer"
        >
            <generator class="increment" />
        </id>
        <set name="listB" cascade="all, delete-orphan" inverse="true" lazy="true" fetch="select" order-by="c.Id asc">
            <key column="a_id"/>
            <one-to-many class="B"/>
        </set>
    </class>
</hibernate-mapping>

Table B mapping:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="database">
    <class name="B" table="B">
        <id
            column="id"
            name="Id"
            type="integer"
        >
            <generator class="increment" />
        </id>
        <many-to-one name="a" class="A" fetch="select">
            <column name="a_id" not-null="true" />
        </many-to-one>
        <many-to-one name="c" class="C" fetch="select">
            <column name="c_id" not-null="true" />
        </many-to-one>
    </class>
</hibernate-mapping>

Table C mapping:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="database">
    <class name="C" table="C">
        <id
            name="Id"
            type="integer"
            column="id"
        >
            <generator class="increment"/>
        </id>
        <property
            name="Num"
            column="num"
            type="integer"
            not-null="true"
            length="10"
        />
    </class>
</hibernate-mapping>

My question is about the "order-by" clause in the A.hbm file. I would like to order not following c.Id, but c.Num

<set name="listB" cascade="all, delete-orphan" inverse="true" lazy="true" fetch="select" order-by="c.Num asc">

Unfortunatly, when I do so, I get the following exception:

Exception in thread "main" org.hibernate.exception.SQLGrammarException: could not initialize a collection: [database.A.listB#1]
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:92)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
    at org.hibernate.loader.Loader.loadCollection(Loader.java:2069)
    at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:62)
    at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:628)
    at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:83)
    at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1853)
    at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:366)
    at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:108)
    at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:186)
    at Test.main(Test.java:36)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'listb0_.c.Num' in 'order clause'
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
    at com.mysql.jdbc.Util.getInstance(Util.java:386)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1054)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4120)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4052)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2503)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2664)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2815)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)
    at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2322)
    at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208)
    at org.hibernate.loader.Loader.getResultSet(Loader.java:1849)
    at org.hibernate.loader.Loader.doQuery(Loader.java:718)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:270)
    at org.hibernate.loader.Loader.loadCollection(Loader.java:2062)
    ... 8 more

I am new to hibernate, I read some things about criteria that could help me, but I don't see how to use them into hbm files.

Thanks for helping,

Lauriane

After trying the ideas you gave me, I still don't have the answer. First, I wrote the following method to enter data into the database:

private static void create() {
    A a = new A();
    HibernateUtil.save(a);

    C c1 = new C();
    c1.setNum(5);
    HibernateUtil.save(c1);

    C c2 = new C();
    c2.setNum(2);
    HibernateUtil.save(c2);

    C c3 = new C();
    c3.setNum(7);
    HibernateUtil.save(c3);

    C c4 = new C();
    c4.setNum(3);
    HibernateUtil.save(c4);

    B b1 = new B();
    b1.setA(a);
    b1.setC(c1);
    HibernateUtil.save(b1);

    B b2 = new B();
    b2.setA(a);
    b2.setC(c4);
    HibernateUtil.save(b2);

    B b3 = new B();
    b3.setA(a);
    b3.setC(c3);
    HibernateUtil.save(b3);

    B b4 = new B();
    b4.setA(a);
    b4.setC(c2);
    HibernateUtil.save(b4);

    A a2 = new A();
    HibernateUtil.save(a2);

    C c5 = new C();
    c5.setNum(13);
    HibernateUtil.save(c5);

    C c6 = new C();
    c6.setNum(11);
    HibernateUtil.save(c6);

    C c7 = new C();
    c7.setNum(10);
    HibernateUtil.save(c7);

    C c8 = new C();
    c8.setNum(14);
    HibernateUtil.save(c8);

    B b5 = new B();
    b5.setA(a2);
    b5.setC(c5);
    HibernateUtil.save(b5);

    B b6 = new B();
    b6.setA(a2);
    b6.setC(c8);
    HibernateUtil.save(b6);

    B b7 = new B();
    b7.setA(a2);
    b7.setC(c7);
    HibernateUtil.save(b7);

    B b8 = new B();
    b8.setA(a2);
    b8.setC(c6);
    HibernateUtil.save(b8);
}

It gives me 2 instances of A, each one with a list of 4 instances of B.

Then, I tried this code:

Criteria crit = HibernateUtil.currentSession().createCriteria(A.class);
Criteria critb = crit.createCriteria("listB");
Criteria critc = critb.createCriteria("c");
critc.addOrder(Order.asc("num"));
List<A> list = critc.list();
    // Code to iterate over listA
    for (A a : list) {

         List<B> listB = a.getListB();
        for (B b : listB) {

             System.out.print(b.getC().getNum() + ", ");

        }
        System.out.println();
    }

The list I obtain contains 4 times each instance of A, and the output is:

2, 7, 3, 5, 
2, 7, 3, 5, 
2, 7, 3, 5, 
2, 7, 3, 5, 
14, 13, 10, 11, 
14, 13, 10, 11, 
14, 13, 10, 11, 
14, 13, 10, 11, 

So I tried to change the getter of listB in the A class, to:

public List<B> getListB() {
             Criteria crit =
     HibernateUtil.currentSession().createCriteria(B.class);
     Criteria critc = crit.createCriteria("c");
     critc.addOrder(Order.asc("num"));
     return critc.list();
}

and run the following code:

 List<A> list = (List<A>) HibernateUtil.getList("from A");
    for (A a : list) {

        List<B> listB = a.getListB();
        for (B b : listB) {

            System.out.print(b.getC().getNum() + ", ");

        }
        System.out.println();

    }

And the output was:

2, 3, 5, 7, 10, 11, 13, 14, 
2, 3, 5, 7, 10, 11, 13, 14, 

There is no difference made between the instances of B refering to one or the other instance of A, and I don't see how it could work...

Thanks for your help,

Lauriane

like image 280
lolo Avatar asked Oct 04 '22 00:10

lolo


1 Answers

Kindly correct property name "Num" to "num" for C class mapping.

  1. Order By Clause in A

    • You will not be able to set order on C from A, as A is not directly connected to C.
    • The reason why it worked for c.id is because B contains column c_id (C class mapping) and order by clause is applied on c_id column on table B and not id column on C
    • When you set c.num, hibernate will try to look-up column in table B which we do not have.
  2. Criteria Query

    • To use criteria query first remove order-by clause from A or use valid one
    • Criteria query is to be created using "session" in java code and not be configured in hbm file.

Sample code

Criteria crit = session.createCriteria(A.class);
Criteria critb = crit.createCriteria("listB");
Criteria critc = critb.createCriteria("c");
critc.addOrder(Order.asc("num"));
List<A> listA = critc.list();
// Code to iterate over listA

  • If b table can be treated as intermediate table in which case there is no class B required and A can be associated to C using join table b (http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/associations.html#assoc-bidirectional-join) then you can use order-by clause in hbm file as following:

A.java

// remove listB and add following
private Set<C> listC;
// Add getters/setters

Mapping for A

// remove listB mapping and add following
<set name="listC" table="b" fetch="select" inverse="true">
    <key column="a_id"/>
    <many-to-many column="c_id" unique="true" class="C" order-by="num asc" />
</set>

Java Code

Criteria crit = session.createCriteria(A.class);
List<A> listA = crit.setFetchMode("listC", FetchMode.JOIN).list();
// iterate or use listA

The only drawback is B cannot be managed independently and you need to remove column "id" from B and make a_id and c_id as composite primary key for insert/update/delete operation, for select query above changes are sufficient.


  • For more detail kindly refer: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querycriteria.html
  • Apart from Criteria Query you can use hbm query: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html
  • You can also start using annotations and JPA
like image 177
Snehal Patel Avatar answered Oct 13 '22 11:10

Snehal Patel