Hibernate Typesafe

 

 

Jeder der Hibernate verwendet kennt das Problem.

Man erstellt seine Query und man weiß eigentlich gar nicht was man retour bekommt.

OK eine List mit Object[].

Toll und dann fängt man an wie wild zu casten. Wäre es nicht viel schöner, wenn man eine List mit definierten Objects erhält?

Geht nicht, Hibernate ist nicht Typensicher!!

Naja dann schreiben wir uns eben etwas das dies für uns erledigt!!

 

 

Manager

 

Fangen wir mit dem Manager an.

Dieser verwaltet unsere Beans und returnt eine List<Beans>.

Wir können den Manager leer initialisieren oder mit unseren Beans.

Wir können von außen Parameter setzen und wieder löschen.

Weiters können wir auch andere Sessions übergeben um uns auf verschiedene Datenbanken zu verbinden.

HqlBeanManager
HqlBeanManager.java.txt
Text Document 5.8 KB

**
 * Init the Manager with <i>YOUR</i> bean and the class of the bean.
 * After that you can set Parameters, enhance the default query etc.
 * And at last just call <code>hbm.list()</code> and you get your typesafe List.
 *
 * @param <T> Your Bean
 * @author Mario Böck
 */
public class HqlBeanManager<T extends IHqlBean<T>>
{
    private Query query;
    private T myBean = null;
    private Map<String, Object> parameters;
    private Session session = null;

    /**
     * Nur zu verwenden wenn man eine Query absetzt die nur eine Rückgabewert hat. <br>
     * Zum initialisieren verwendet man dann <code>EmptyBean</code> <br>
     * Und zum ausführen hbm.list( Class )
     */
    public HqlBeanManager()
    {
        // default Konstruktor
    }

    /**
     * Konstruktor mit Angabe der Bean-Klasse.
     *
     * @param myBean
     * @throws MyException
     */
    public HqlBeanManager( final Class<T> myBean ) throws MyException
    {
        try
        {
            if( myBean != null )
            {
                this.myBean = myBean.newInstance();
            }
        }
        catch( IllegalAccessException e )
        {
            throw new MyException( "Fehler beim instanzieren der BeanKlasse ", e );
        }
        catch( InstantiationException e )
        {
            throw new MyException( "Fehler beim instanzieren der BeanKlasse ", e );
        }
        if( this.myBean != null )
        {
            createQuery( this.myBean.getQuery() );
        }
    }

    /**
     * Hilfsmethode die es ermöglicht auf Grund von einem String eine Query zu erstellen.
     *
     * @param s
     * @throws HibernateException
     * @throws MyException
     */
    private void createQuery( final String s ) throws HibernateException, MyException
    {
        if( session == null )
        {
            session = MySession.getInstance();
        }
        query = session.createQuery( s );
    }

    /**
     * Liefert eien List<Beans> retour. Bean wurde beim instanzieren initialisiert.
     *
     * @return <code>List<T></code> Typensichere Liste von Beans
     * @throws HibernateException
     */
    public List<T> list() throws HibernateException
    {
        setParameters();
        return myBean.buildBean( query.list() );
    }

    /**
     * Bind a value to a named query parameter, guessing the Hibernate
     * type from the class of the given object.
     *
     * @param name the name of the parameter
     * @param value the non-null parameter value
     */
    public void setParameter( final String name, final Object value )
    {
        getParamMap().put( name, value );
    }

    /**
     * If you use the emptyBean sometimes it is necessary to clear the parameter map. So you can <br>
     * use the same Manager several times.
     */
    public void clearParameters()
    {
        getParamMap().clear();
    }

    private Map<String, Object> getParamMap()
    {
        if( parameters == null )
        {
            parameters = new HashMap<String, Object>();
        }
        return parameters;
    }

    private void setParameters()
    {
        for( Iterator<String> iter = getParamMap().keySet().iterator(); iter.hasNext(); )
        {
            String name = iter.next();
            query.setParameter( name, getParamMap().get( name ) );
        }
    }

    /**
     * Enable caching of this query result set.
     *
     * @param cacheable Should the query results be cacheable?
     */
    public void setCacheable( final boolean cacheable )
    {
        if( query != null )
        {
            query.setCacheable( cacheable );
        }
    }

    /**
     * Ermöglicht es, dass man auch ohne einem Bean typensicher weiterarbeiten kann.
     *
     * @param <S>
     * @param c
     * @return List<S> typensichere List
     */
    @SuppressWarnings( "unchecked" )
    public <S> List<S> list( final Class<S> c )
    {
        setParameters();
        return query.list();
    }

    /**
     * Convenience method to return a single instance that matches
     * the query, or null if the query returns no results.
     * Common use for counts.
     *
     * @return the single result or <tt>null</tt>
     * @throws NonUniqueResultException if there is more than one matching result
     */
    public Object getUniqeResult()
    {
        if( query != null )
        {
            setParameters();
            return query.uniqueResult();
        }
        return null;
    }

    /**
     * If you want to change the DEFAULT query of the HqlBean <br>
     * BE CAREFUL, YOU MUST KNOW HOW THE BEAN IS WORKING!!! <br>
     * Example for use: Take the Manager and call <code>Manager.getBeanQuery()</code> now you can add a WHERE_CLAUSE!!!
     *
     * @param q
     * @throws HibernateException
     * @throws MyException
     */
    public void setQuery( final String q ) throws HibernateException, MyException
    {
        createQuery( q );
    }

    /**
     * If you want to change the DEFAULT query of the HqlBean <br>
     * BE CAREFUL, YOU MUST KNOW HOW THE BEAN IS WORKING!!! <br>
     * Um eine andere Session als die von gpla_synergie von außen zu setzen.
     * Example for use: Take the Manager and call <code>Manager.getBeanQuery()</code> now you can add a WHERE_CLAUSE!!!
     *
     * @param q
     * @param sess Übergeben Session. Falls nicht SynergieDB
     * @throws HibernateException
     * @throws MyException
     */
    public void setQuery( final Session sess, final String q ) throws HibernateException, MyException
    {
        this.session = sess;
        createQuery( q );
    }

    /**
     * @return HQL :String HQL from the given bean
     */
    public String getBeanQuery()
    {
        return this.myBean != null ? this.myBean.getQuery() : "";
    }
}

 

Interface

 

Unser Interface IHqlBean sieht dagegen ganz einfach aus und hat kaum was zu bieten!

IHqlBean
IHqlBean.java.txt
Text Document 510 Bytes

public interface IHqlBean<T>
{
    /**
     * Hier muss die entsprechende Query von jedem einzlnen Bean returnt werden.
     * @return String Query
     */
    public String getQuery();

    /**
     * Hier wird aus der <code>List<Object[]></code> oder <code>List&ltObject&gt</code> das wir von Hibernate.List()
     * erhalten das jeweilige Bean befüllt.
     * @param obj
     * @return List mit dem jeweiligen Beans
     */
    public List<T> buildBean( List<?> obj );
}

 

Beans

 

Ok das hätten wir mal geschaft, und wie sieht nun eigentlich das Bean aus??

Und was ist dieses EmptyBean.

 

Fangen wir mit dem EmptyBean an wie es verwendet wird, erkläre ich später bei der Implementierung

EmptyBean.zip
Compressed Archive in ZIP Format 446 Bytes

/**
 * @author Mario Böck
 */
public class EmptyBean implements IHqlBean<EmptyBean>
{
    private static final StringBuilder EMPTY_HQL = new StringBuilder( "" );

    /**
     * @see at.sozvers.gpla.db.hql.IHqlBean#getQuery()
     */
    @Override
    public String getQuery()
    {
        return EMPTY_HQL.toString();
    }

    /*
     * (non-Javadoc)
     * @see at.sozvers.gpla.db.hql.IHqlBean#buildBean(java.util.List)
     */
    @Override
    public List<EmptyBean> buildBean( final List<?> obj )
    {
        return new ArrayList<EmptyBean>();
    }
}

 

Ok dieses EmptyBean macht nicht viel, sehen wir uns mal eine etwas vernünftigeres Bean an:

/**
 * @author Mario Böck
 */
public class MyFirstBean implements IHqlBean<MyFirstBean>
{
    private static final String HQL_GET_DATEN = " select user.name, p "
                                                      + " from Wohnort ort"
                                                      + " join ort.userCollection user "
                                                      + " join user.produktCollection p"
                                                      + " where ort.plz = :"
                                                      + HQL_PARAMETER_PLZ;

    private String userName;
    private Produkt prod;

    /**
     * @return the userName
     */
    public String getUserName()
    {
        return userName;
    }

    /**
     * @param userName the userName to set
     */
    private void setUserName( final String userName )
    {
        this.userName = userName;
    }

    /**
     * @return the prod
     */
    public Produkt getProd()
    {
        return prod;
    }

    /**
     * @param prod the prod to set
     */
    private void setProd( final Produkt prod )
    {
        this.prod = prod;
    }

    @Override
    public String getQuery()
    {
        return HQL_GET_DATEN;
    }

    /*
     * (non-Javadoc)
     * @see xx.xxx.xxxxx.hql.IHqlBean#buildBean(java.util.List)
     */
    @Override
    public List<MyFirstBean> buildBean( final List<?> obj )
    {
        List<MyFirstBean> retList = new ArrayList<MyFirstBean>();

        for( Iterator<?> iter = obj.iterator(); iter.hasNext(); )
        {
            Object[] objects = (Object[]) iter.next();
            MyFirstBean bean = new MyFirstBean();
            bean.setName( (String) objects[0] );
            bean.setProd( (Produkt) objects[1] );
            retList.add( bean );
        }
        return retList;
    }
}

So sieht das doch schon ganz vernünftig aus.

 

Man kann sich vermutlich schon vorstellen was dieses Konstrukt so machen soll.

Aber sehen wir uns doch auch noch kurz die konkrete Implementierung an.

 

Zuerst wie das mit diesem "EmptyBean" funktioniert.

//Die List in der wir die Ergebnismenge speichern wollen

List<User> l = new ArrayList<User>();

//der auszuführende HQL

String hql = "Select usr from User usr where usr.name = 'Test Person' ";

//Wir instanzieren unseren Manager und initialisieren ihn mit dem EmptyBean
HqlBeanManager<EmptyBean> hbm = new HqlBeanManager<EmptyBean>();

//Wir setzen hier unsere Abfrage
hbm.setQuery( hql );

//Und so erhalten wir unsere Typensichere List retour.
l = hbm.list( User.class );

//Die List in der wir die Ergebnismenge speichern wollen

List<User> l = new ArrayList<User>();

//der auszuführende HQL

String hql = "Select usr from User usr where usr.name = 'Test Person' ";

//Wir instanzieren unseren Manager und initialisieren ihn mit dem EmptyBean
HqlBeanManager<EmptyBean> hbm = new HqlBeanManager<EmptyBean>();

//Wir setzen hier unsere Abfrage
hbm.setQuery( hql );

//Und so erhalten wir unsere Typensichere List retour.
l = hbm.list( User.class );

 

Das war doch schon mal nicht schwer.

Jetzt sehen wir uns noch kurz unser MyFirstBean an und dann dürfte alles klar sein.

//Map in die wir unsere Daten putten wollen 1 User kann mehrere Produkte haben

MultiMap myMap = new MultiValueMap();

//Unser HqlBeanManager mit unserem Bean

HqlBeanManager<MyFirstBean> hbm = new HqlBeanManager<MyFirstBean>( MyFirstBean.class );

//Hier setzen wir den Parameter von außen!!

//Es können beliebig viele Parameter verwendet werden
hbm.setParameter( HQL_PARAMETER_PLZ, 1234 );

//Nun können wir über unsere Liste iterieren und in die Map adden.
for( MyFirstBean bean : hbm.list() )
{
      myMap.put( bean.getName(), bean.getProd() );
}

 

Wenn euch die Seite gefällt oder der Inhalt hilfreich ist/war dann "+1'd" doch mal.