Tuesday, October 27, 2015

Hibernate Audit By keeping order of change


There are lots of samples in internet creating audit data for hibernate with Interceptors.

With these samples (maybe I missed some of them) you do not know the order of changes.
We needed to know the order for some reasons.

Luckily our hibernate code was generated with a tool we wrote. We put a method as 1st
line in our setter methods. We were calling a method to understand if this field is changed.
(By this way u can also get full path (stacktrace) of change )

public class User {
 public void setPersonel( YPerson personel )
    {
        this.setEditedFieldValue( "personel", this.personel, personel, true ) ; 
        this.personel = personel ;
    }
 
 public void setCode( String code )
    {
        this.setEditedFieldValue( "code", this.code, code, true ) ; 
        this.code = code ;
    }
 
 }

 



Below is the generic code to check if field is changed.
There are cases like
1)Object created with new,we are assigning necessary fields (You do not need to keep ,
because objects former values are null before creation so if an object does not have any audit data,it is same from beginning)
2)Object is being read from database ( We are attaching object so all values will seem null,be careful)
Hibernate creates object with null constructor and calls setters.
3)Object attached , in the flow we changed a field.


protected void setEditedFieldValue( String fieldName, Object oldValue, Object newValue, boolean isNullable )
    {
        //Beans can override isIntercepted method to skip being audited
        if( !this.isIntercepted( ) )
        {
            return ;
        }
        
        //Do not need to audit initially null ones, or you can skip at time of attaching
        if( oldValue == null )
        {
            return ;
        }

        //for not null int values our code has 0. So if something is not nullable (we know at time of code generation )
        // but it is 0 it means null for us return without auditing
        if( !isNullable )
        {
            if( ( oldValue instanceof Integer ) && ( ( Integer )oldValue ).intValue( ) == 0 )
            {
                return ;
            }

            if( ( oldValue instanceof BigDecimal ) && ( ( BigDecimal )oldValue ).compareTo( BigDecimal.ZERO ) == 0 )
            {
                return ;
            }
        }

        //Do not audit if value did not change,
        //Example : You opened edit page, change some values and some not.So audit only changed ones.
        if( oldValue == newValue )
        {
            return ;
        }

        

        if( oldValue instanceof YBean )
        {
            String oldDisp = "[N/A]" ;
            String newDisp = "[N/A]" ;

            if( YServerConstants.IS_AUDIT_DEBUG_MODE )
            {
                //want to know when if beans are proxy or no.
                    if( HibernateUtils.isproxy( ( YBean )oldValue ) )
                    {
                        oldDisp = "##BeanProxy##;" + oldValue.getClass( ).getSimpleName( ) + "; id:" + ( ( YBean )oldValue ).getId( ) ;
                    }
                    else
                    {
                        oldDisp = "##YBean##;" + ( ( YBean )oldValue ).getInstanceBeanName( ) + "; id:" + ( ( YBean )oldValue ).getId( ) + "; disp:" + ( ( YBean )oldValue ).getShortDisplay( ) ;
                    }

                    if( newValue != null )
                    {
                        if( HibernateUtils.isproxy( ( YBean )newValue ) )
                        {
                            newDisp = "##BeanProxy##;" + newValue.getClass( ).getSimpleName( ) + "; id:" + ( ( YBean )newValue ).getId( ) ;
                        }
                        else
                        {
                            newDisp = "##YBean##;" + ( ( YBean )newValue ).getInstanceBeanName( ) + "; id:" + ( ( YBean )newValue ).getId( ) + "; disp:" + ( ( YBean )newValue ).getShortDisplay( ) ;
                        }
                    }
                

                if( !oldDisp.equals( newDisp ) )
                {
                    if( this._editedFieldValuesPool == null )
                    {
                        this._editedFieldValuesPool = new YAuditFieldNameValuePairPool( ) ;
                    }

                    this._editedFieldValuesPool.setFieldValue( fieldName, oldDisp ) ;
                }
            }
        }
        else
        {
            //We do not know if any of objects are null so can not call , o1.equals(02) 
            if( isObjectEquals( oldValue, newValue ) )
            {
                return ;
            }

            if( this._editedFieldValuesPool == null )
            {
                this._editedFieldValuesPool = new YAuditFieldNameValuePairPool( ) ;
            }

            this._editedFieldValuesPool.setFieldValue( fieldName, oldValue ) ;
        }
    }
    
    
    public static boolean isObjectEquals( Object o1, Object o2 )
    {
        if( o1 == o2 )
        {
            return true ;
        }

        if( o1 == null || o2 == null )
        {
            return false ;
        }

        return o1.equals( o2 ) ;
    }

    
    
    //This class is holding audit data. Since audits are hold  at Arraylist you do not loose order.
    //Also you can even add stack traces with conditions to monitor your system.
    
    
    
    
public class YAuditFieldNameValuePairPool implements Serializable
{
    protected ArrayList<String> nameList = new ArrayList<String>( 0 ) ;

    protected ArrayList<Object> valueList = new ArrayList<Object>( 0 ) ;
    
    
    public int setFieldValue( String fieldName, Object value )
    {
        boolean isFound = false ;

        //check If you have same record do not save again
        //This is a decision you can think this is usefull to see if
        //a change is being applied mutiple time.
        for( int i = this.nameList.size( ) - 1; i >= 0; --i )
        {
            String name = this.nameList.get( i ) ;
            Object val = this.valueList.get( i ) ;

            if( name.equals( fieldName ) )
            {
                if( YClientUtils.isObjectEquals( value, val ) )
                {
                    isFound = true ;
                    break ;
                }
            }
        }

        if( !isFound )
        {
            this.nameList.add( fieldName ) ;
            this.valueList.add( value ) ;

            return this.nameList.size( ) - 1 ;
        }

        return -1 ;
    }

No comments:

Post a Comment