Tuesday, October 27, 2015

An alternative to Hibernate Criteria and HQL



An alternative to Hibernate Criteria, Stay in object in notation for querying database,Express your query with object relations.

*** Below logic is like ,
"org.hibernate.criterion.Example"
But more advance and also good for omni searches in all fields of beans.(String fields)

When you use hibernate Example you still define projections,criteria...
Below notation is pure object notation.



We can use HQL or criteria api for querying database. This is hibernate and we are dealing with objects.
We taught why not query database by objects.(Probably lots of people did this by time
when they advance in hibernate)
For example :
I want users with name John having organization 'ABC Company'

hql = ' Select dao FROM User dao where dao.name like '%John%' and dao.organization.name like '%ABC Company%' "

Criteria


Full Objecte Notation
Create detached object representing what you look for
User sample = new User();
sample.setName("John");

Organization org = new Organization();
org.setName("ABC Company");
sample.setOrg(org);

If you have builder pattern

(New User()).setName("John").getOrganization().setName("ABC Company");

This is abusing Hibernate or Just going up one level in Object notation.
Express your query with object relations.

Of course this is translated into HQL or Criteria for querying. We wrote both versions. They both seemed very nice to us.
We love our this piece of code so much :) We wrote this at 2009. At that time there was nothing like this(as we search).
Maybe now there is.

If ultimate goal of Hibernate is to stay in Object notation and ease of use, I think a notation like this
must be alternative like HQL or Criteria.


Below code enables deep search on multiple beans and fields also.

--- Find relations for this user
--- Hql equivalent = " Select dao FROM YRelUserHorg dao where dao.user.id = this.id "
YRelUserHorg beanExample = new YRelUserHorg( ) ;
beanExample.setUser( this ) ;
List beanList = HibernateUtils2.execOmni( null, beanExample, "%", 0, null ) ;

--Discussion is a self referencing table
--Find replies to this thread
--- Hql equivalent = " Select dao FROM YDiscussion dao where dao.reply.id = " + this.mainDiscussion.getId( ) ;
YDiscussion sample = new YDiscussion( ) ;
YDiscussion reply = new YDiscussion( ) ;
reply.setId( this.mainDiscussion.getId( ) ) ;
sample.setReply( reply ) ;

List beanList = HibernateUtils2.execOmni( null, sample, null, null, null ) ;

--- This is a nice one,u have a bean with a sub bean say u want to search "xyz" on any files
--- In our database everyone working in company is Person and system users are User so, there is a person field in User
--- Hql equivalent = " Select dao FROM YUser dao
-- where dao.title like '%xyz%' or dao.address1 like '%xyz%' or dao.person.name like '%xyz%' or dao.person.surname like '%xyz%' .... think this is
much more(and it really is)
YUser beanExample = new YUser( ) ;
beanExample.setPersonel( new YPerson( ) ) ;

List beanList = HibernateUtils2.execOmni( null, beanExample, "%" + query + "%", 0, this.loadUserItemCount ) ;

--- users having role admin(an object refernce attached object) and having John in any of fields
YRelUserHorg beanExample = new YRelUserHorg( ) ;
beanExample.setRole( adminRole ) ;
bean.setUser(new YUser());


List beanList = HibernateUtils2.execOmni( null, beanExample, "%John%", 0, null ) ;


Of course this code is not full and needs more features but it's talents are really impressive.
Deep searching,and staying at Object notation makes us feel doing something elegant. Because we express our need
with object notation. we can change implementation any time(even to JDBC).

@Transient
    @Override
    public YRelOrganizationOrganization getCriteriaTemplate( String key )
    {
        if( HibernateUtils2.TKEY_MYSUPPLIERS.equals( key ) )
        {
            YRelOrganizationOrganization roo = new YRelOrganizationOrganization( ) ;
            roo.setRelationType( DRelOrganizationOrganization.TYPE_BUYER_SUPPLIER ) ;
            roo.setOtherOrganization( new YOrganization( ) ) ;

            return roo ;
        }
                YRelOrganizationOrganization beanExample = new YRelOrganizationOrganization( ) ;
        beanExample.setStatus( 1 );
        //beanExample.setOtherOrganization( new YOrganization( ) );

        //List<Integer> beanList = HibernateUtils2.execOmni( HibernateUtils2.TKEY_MYSUPPLIERS, beanExample, query + "%", 0, 30, "otherOrganization.interchangeUser.personel.name", SortOrder.ASCENDING ) ;
        List<Integer> beanList = HibernateUtils2.execOmni( HibernateUtils2.TKEY_MYSUPPLIERS, beanExample, query + "%", 0, 30, "otherOrganization.name", SortOrder.ASCENDING ) ;

        
            
            
            
                        if( "String".equals( simpleTypeName ) )
            {
                Object val = subBean.getFieldValue( fieldName ) ;

                if( subBean.query_isForceIncludedField( fieldName ) || ( ( val == null && !IGNORE_IN_SEARCH.equals( val ) ) && !subBean.query_isForceExcludedField( fieldName ) ) )
                {
                    disjunction.add( Restrictions.like( ( alias != null ? alias + "." : "" ) + fieldName, omni ) ) ;
                }
            }
            
                        if( "String".equals( simpleTypeName ) )
            {
                if( subBean.getFieldValue( fieldName ) != null )
                {
                    String val = ( String )subBean.getFieldValue( fieldName ) ;

                    if( subBean.query_isForceIncludedField( fieldName ) || ( ( !YClientUtils.isBlankTrim( val ) && !IGNORE_IN_SEARCH.equals( val ) ) && !subBean.query_isForceExcludedField( fieldName ) ) )
                    {
                        criteria.add( Restrictions.like( ( alias != null ? alias + "." : "" ) + fieldName, val ) ) ;
                    }
                }
            }
            


Logic : we can have template beans (like you are using a type of user select query so much ,so create a template) and also can send an override beanExample
Think like this,
You have a template of users having type Manager this is templateBean
Then you want ones with name Max, you send a sample with name Max.

You can also use this with null template and a sample with both name Max and type Manager












public static List<Integer> execOmni( String key, YBean beanSend, String somnis, Integer firstResult, Integer maxResult, String sort, SortOrder order, boolean isForceOrgId )
    {

        String omnis = YClientUtils.notNullTrim( somnis, "" ) ;

        if( "%%".equals( omnis ) )
        {
            omnis = "" ;
        }

        YBean templateBean = key == null ? null : beanSend.getCriteriaTemplate( key ) ;

        if( templateBean == null )
        {
            templateBean = beanSend ;
        }

        HashMap<String, Criteria> aliasSet = new HashMap<String, Criteria>( ) ;

        Criteria criteria = HibernateManager.getSession( ).createCriteria( beanSend.getClass( ) ) ;

        // Some beans are only readable by their related organizations so a default organization is added every query
        // Some beans are shared like product. A buyer can see supplier product so isForceOrgId is for omitting this.
        if( isForceOrgId && !YServerSession.isTopAdmin( ) )
        {
            if( !( beanSend instanceof YOrganization ) && !( beanSend instanceof YCatalogDetail ) )
            {
                String organizationField = getOrganizationField( beanSend ) ;
                if( organizationField != null )
                {
                    criteria.add( Restrictions.eq( organizationField + ".id", YServerSession.getSession( ).getOrganizationId( ) ) ) ;
                }
            }
        }

        String[ ] beanName = beanSend.getBeanNames( ) ;

        //ADD EXACT VALUES OF BOTH IF THEY ARE NOT EQUAL
        addExactValues( criteria, templateBean, null ) ;
        if( !beanSend.equals( templateBean ) )
        {
            addExactValues( criteria, beanSend, null ) ;
        }

        //Check sub beans ,and if they have value add exact values there
        // (new User()).getOrganization().setName("ABC");
        for( String s : beanName )
        {
            YBean subBean = ( YBean )beanSend.getFieldValue( s ) ;
            //-------------- ADD  EXACT VALUES FROM SEND BEAN
            if( subBean != null )
            {
                String subAlias = "dao_" + s + "_" ;
                if( aliasSet.get( subAlias ) == null )
                {
                    Criteria sub = criteria.createCriteria( s, subAlias, CriteriaSpecification.LEFT_JOIN ) ;
                    aliasSet.put( subAlias, sub ) ;
                }

                addExactValues( criteria, subBean, subAlias ) ;
            }

            //-------------- ADD  EXACT VALUES FROM TEMPLATE BEAN
            subBean = ( YBean )templateBean.getFieldValue( s ) ;
            if( subBean != null )
            {
                String subAlias = "dao_" + s + "_" ;
                if( aliasSet.get( subAlias ) == null )
                {
                    Criteria sub = criteria.createCriteria( s, subAlias, CriteriaSpecification.LEFT_JOIN ) ;
                    aliasSet.put( subAlias, sub ) ;
                }

                addExactValues( criteria, subBean, subAlias ) ;
            }
        }

        // Send your omni query it will be AND or ORs
        //like u write "Europe Max "
        // ( a like '%Europe%' OR b like '%Europe%' ) AND ( a like '%Max%' OR b like '%Max%' ) 
        if( omnis != null && !YClientUtils.isBlankTrim( omnis ) )
        {
            String[ ] splits = omnis.split( " " ) ;

            for( int i = 0; i < splits.length; i++ )
            {
                String split = splits[ i ] ;

                if( YClientUtils.isBlankTrim( split ) )
                    continue ;

                split = split.trim( ) ;

                if( !split.startsWith( "%" ) )
                    split = "%" + split ;

                if( !split.endsWith( "%" ) )
                    split = split + "%" ;

                Disjunction disjunction = Restrictions.disjunction( ) ;

                criteria.add( disjunction ) ;

                setOmniValues( disjunction, templateBean, split, null ) ;
                setOmniValues( disjunction, beanSend, split, null ) ;

                //Do this for also not null sub beans ( going deeper !!! )
                for( String s : beanName )
                {
                    YBean subBean = ( YBean )beanSend.getFieldValue( s ) ;
                    if( subBean != null )
                    {
                        String subAlias = "dao_" + s + "_" ;
                        setOmniValues( disjunction, subBean, split, subAlias ) ;
                    }
                    //----------------------
                    subBean = ( YBean )templateBean.getFieldValue( s ) ;
                    if( subBean != null )
                    {
                        String subAlias = "dao_" + s + "_" ;
                        setOmniValues( disjunction, subBean, split, subAlias ) ;
                    }
                }
            }
        }

        
        String criteriaProjection = beanSend.getCriteriaProjection( key ) ;

        //You can specify what to return, id is default, object is not returned for performance,u can still
        //load all of these objects batch ,never load objects from a query, 100000 results not rare!
        if( !YClientUtils.isBlankTrim( criteriaProjection ) )
        {
            criteria.setProjection( Projections.projectionList( ).add( Projections.property( criteriaProjection ), "id" ) ) ;
        }
        else
        {
            criteria.setProjection( Projections.projectionList( ).add( Projections.property( "id" ), "id" ) ) ;
        }

        //criteria.addOrder( Order.asc( "id" ) ) ;

        if( YClientUtils.isBlankTrim( sort ) )
        {
            criteria.addOrder( Order.desc( "id" ) ) ;
        }
        else
        {
            String realSortPart = sort ;

            if( sort.indexOf( "." ) > -1 )
            {
                String[ ] chainBeans = realSortPart.split( "\\." ) ;

                //If sort path not present in query create it,
                //Example you wanted only users with name John ,
                // (new User()).setName("John")
                //But want to sort by organization name
                // sort = user.deparment.organization
                // so this path must be created if not already exists
                
                for( int z = 0; z < chainBeans.length - 1; z++ )
                {
                    String subAlias = "dao_" + chainBeans[ z ] + "_" ;

                    if( aliasSet.get( subAlias ) != null )
                    {
                        continue ;
                    }

                    Criteria parent = z == 0 ? criteria : aliasSet.get( "dao_" + chainBeans[ z - 1 ] + "_" ) ;

                    Criteria lastCriteria = parent.createCriteria( chainBeans[ z ], subAlias, CriteriaSpecification.LEFT_JOIN ) ;

                    aliasSet.put( subAlias, lastCriteria ) ;

                }

                String subAlias = "dao_" + chainBeans[ chainBeans.length - 2 ] + "_" ;

                if( SortOrder.ASCENDING.equals( order ) )
                {
                    criteria.addOrder( Order.asc( subAlias + "." + chainBeans[ chainBeans.length - 1 ] ) ) ;
                }
                else
                {
                    criteria.addOrder( Order.desc( subAlias + "." + chainBeans[ chainBeans.length - 1 ] ) ) ;
                }
            }
            else
            {
                if( SortOrder.ASCENDING.equals( order ) )
                {
                    criteria.addOrder( Order.asc( sort ) ) ;
                }
                else
                {
                    criteria.addOrder( Order.desc( sort ) ) ;
                }

            }

        }

        if( firstResult != null )
        {
            criteria.setFirstResult( firstResult ) ;
        }

        if( maxResult != null )
        {
            criteria.setMaxResults( maxResult ) ;
        }

        criteria.setCacheable( true ) ;

        List<Integer> results = criteria.list( ) ;

        return results ;

    }
    
    protected static void addExactValues( Criteria criteria, YBean subBean, String alias )
    {
        if( subBean.getId( ) != null && subBean.getId( ).intValue( ) > 0 )
        {
            criteria.add( Restrictions.eq( ( alias != null ? alias + "." : "" ) + "id", subBean.getId( ) ) ) ;
            return ;
        }

        String[ ] fieldNames = subBean.getFieldNames( ) ;

        for( String fieldName : fieldNames )
        {
            String simpleTypeName = ReflectionUtils.getField( subBean.getClass( ), fieldName ).getType( ).getSimpleName( ) ;

            if( "String".equals( simpleTypeName ) )
            {
                if( subBean.getFieldValue( fieldName ) != null )
                {
                    String val = ( String )subBean.getFieldValue( fieldName ) ;

                    if( subBean.query_isForceIncludedField( fieldName ) || ( ( !YClientUtils.isBlankTrim( val ) && !IGNORE_IN_SEARCH.equals( val ) ) && !subBean.query_isForceExcludedField( fieldName ) ) )
                    {
                        criteria.add( Restrictions.like( ( alias != null ? alias + "." : "" ) + fieldName, val ) ) ;
                    }
                }
            }

            if( "int".equals( simpleTypeName ) || "Integer".equals( simpleTypeName ) )
            {
                if( subBean.getFieldValue( fieldName ) != null )
                {
                    Integer ival = ( ( Integer )subBean.getFieldValue( fieldName ) ) ;
                    if( subBean.query_isForceIncludedField( fieldName ) || ( ival != null && ival.intValue( ) > 0 && !subBean.query_isForceExcludedField( fieldName ) ) )
                    {
                        criteria.add( Restrictions.eq( ( alias != null ? alias + "." : "" ) + fieldName, subBean.getFieldValue( fieldName ) ) ) ;
                    }
                }
            }
        }

    } ..............................
    //query_isForceIncludedField  means , we are checking if a value
    protected static void setOmniValues( Disjunction disjunction, YBean subBean, String omni, String alias )
    {
        //This is for sample (unpersistent) beans 
        // Say u created a sample like
        // (new User()).setOrganization( Yorganization.getById(1))
        // you want all users with organization 1 but since organization is attached to session
        //All of its non-lazy values are readen. we are dealing with those ones so omit them
        
        if( subBean.getId( ) != null && subBean.getId( ).intValue( ) > 0 )
        {
            return ;
        }

        String[ ] fieldNames = subBean.getFieldNames( ) ;

        for( String fieldName : fieldNames )
        {
            String simpleTypeName = ReflectionUtils.getField( subBean.getClass( ), fieldName ).getType( ).getSimpleName( ) ;

            if( "String".equals( simpleTypeName ) )
            {
                Object val = subBean.getFieldValue( fieldName ) ;

                if( subBean.query_isForceIncludedField( fieldName ) || ( ( val == null && !IGNORE_IN_SEARCH.equals( val ) ) && !subBean.query_isForceExcludedField( fieldName ) ) )
                {
                    disjunction.add( Restrictions.like( ( alias != null ? alias + "." : "" ) + fieldName, omni ) ) ;
                }
            }
        }

    } 

No comments:

Post a Comment