My Quotes


When U were born , you cried and the world rejoiced
Live U'r life in such a way that when you go
THE WORLD SHOULD CRY






Tuesday, September 17, 2013

Result Set Mapper for hibernate and JPA

There were times when I had to use an Entity without a primary Key in the DB.

I was trying to get it work thru the Result Set Mapper functionality which was provided for Hibernate. I had faced some issues and here are my alternatives.

Step-1
Create a transformer which extends the org.hibernate.transform.ResultTransformer
import java.lang.reflect.Field;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.property.ChainedPropertyAccessor;
import org.hibernate.property.PropertyAccessor;
import org.hibernate.property.PropertyAccessorFactory;
import org.hibernate.property.Setter;
import org.hibernate.transform.ResultTransformer;
public class AdvancedSearchResultTransformer implements ResultTransformer {
 private final Class resultClass;
 private final PropertyAccessor propertyAccessor;
 private Setter[] setters;
 private Field[] fields;

 private static final long serialVersionUID = -3489302728091710568L;

 public AdvancedSearchResultTransformer(Class resultClass) {
  if ( resultClass == null ) {
   throw new IllegalArgumentException( "resultClass cannot be null" );
  }
  this.resultClass = resultClass;
  propertyAccessor = new ChainedPropertyAccessor(
    new PropertyAccessor[] {
      PropertyAccessorFactory.getPropertyAccessor( resultClass, null ),
      PropertyAccessorFactory.getPropertyAccessor( "field" )
    }
  );
 }

 public Object transformTuple(Object[] tuple, String[] aliases) {
  Object result;
  try {
   fields = resultClass.getDeclaredFields();
   if ( setters == null ) {
    setters = new Setter[aliases.length];
    for ( int i = 0; i < aliases.length; i++ ) {
     String alias = aliases[i];
     if ( alias != null ) {
      Setter setter;
      try {
                       setter = propertyAccessor.getSetter(resultClass, alias);
                    } catch (PropertyNotFoundException e) {
                       for (Field field : this.fields) {
                          String fieldName = field.getName();
                          if (fieldName.equalsIgnoreCase(alias)) {
                             alias = fieldName;
                             break;
                          }
                       }
                       setter = propertyAccessor.getSetter(resultClass, alias);
                    }
      setters[i] = setter;
     }
    }
   }
   result = resultClass.newInstance();
   for ( int i = 0; i < aliases.length; i++ ) {
    if ( setters[i] != null ) {
     setters[i].set( result, tuple[i], null );
    }
   }
  }
  catch ( InstantiationException e ) {
   throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
  }
  catch ( IllegalAccessException e ) {
   throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
  }
  return result;
 }
 public List transformList(List collection) {
  return collection;
 }
 public int hashCode() {
  int result;
  result = resultClass.hashCode();
  result = 31 * result + propertyAccessor.hashCode();
  return result;
 }
}

Step-2
Look at the place where I catch the PropertyNotFoundException. There is a reason for the same

Step-3
There are 2 ways of implementing the ResultSet Transformer
Create the POJO which resembles the DB Table
Create an SQL Query as

sqlQuery.append(" SELECT column1 as col1, column2 as cl2 ");
sqlQuery.append(" FROM table tbl ");

col1 and col2 should be defined as variables (case sensitive)
private String col1;
private String col2;
in yur POJO with SETTER and GETTER methods.

If you are using Hibernate here is the way
 public List executeReadAllSQLQuery(String queryString, Object[] values,
   Object[] aliasInputs, Class transformerClass) throws ORMException {
  SQLQuery sqlQuery = null;
  try {
   sessionObj = getHibSession();
   sqlQuery = sessionObj.createSQLQuery(queryString);
   sqlQuery.setResultTransformer(new AdvancedSearchResultTransformer(transformerClass));
   if(aliasInputs != null){
    for (int i = 0; i < aliasInputs.length; i++) {
     sqlQuery.addScalar(aliasInputs[i].toString());
    }
   }
   if (values != null) {
    for (int i = 0; i < values.length; i++) {
      sqlQuery.setParameter(i, values[i]);
    }
   }
   return sqlQuery.list();
  } 


If you are using JPA mode then there is n direct way to invoke ResultSetTransformer. So what I have dne is as follows
Query sqlQuery = null;
  try {
   entityMgrObj = getHibJpaEntityManager();
   sqlQuery = entityMgrObj.createNativeQuery(queryString);
   sqlQuery.unwrap(SQLQuery.class).setResultTransformer(new AdvancedSearchResultTransformer(transformerClass));
   if(aliasInputs != null){
    for (int i = 0; i < aliasInputs.length; i++) {
     sqlQuery.unwrap(SQLQuery.class).addScalar(aliasInputs[i].toString());
    }
   }
   if (values != null) {
    for (int i = 0; i < values.length; i++) {
     sqlQuery.setParameter(i, values[i]);
    }
   }
   return sqlQuery.getResultList();
  }
Look at the place where I tweak JPA to forcibly use Hibernate ResultSet Mapper.

Thats all. Now instetad of those annoying Object[] which comes as RESULT SET, we will be able to get POJO objects just like resulr Hibernate / JPA modes.

Happy hunting.

Tuesday, July 16, 2013

Find errors in XML tags before unmarshalling






1.Create a JAXB2ValidationEventCollector-- File Atached herewith. Idealy you can put this in the same folder as that of your JAXB classes.
 2.Attach this Validator to the Unmarshaler.
 3.Now if you see any errors in the XSD with that of the field you are getting in as an XML, the Validator will spill out the same.
 4.If not assume that the XML is perfect to that of the XSD.
 5.I have tested this by manually changing the XSD.
 6.My XSD has the following
 





















1.My XML has the following EPH1035303164649
 2.I get the following 
 
msg-->cvc-length-valid: Value 'EPH1035303164649' with length = '16' is not facet-valid with respect to length '10' for type '#AnonType_ReferenceNumberOpfTriggerMessage'.

msg-->cvc-type.3.1.3: The value 'EPH1035303164649' of element 'xs:ReferenceNumber' is not valid.




XML message



   


 


 


 


  EPH1035303164649 


  


  


  




XSD











  


   


    


     


    


   


  




  


   


    


    


   


  




  


   


    


    


    


     


      


       


      


     


    


    


    


   


  


 


  


   


    


     


      


       


      


     


    


   


  





 




 Validator


import javax.xml.bind.ValidationEvent;



import javax.xml.bind.util.ValidationEventCollector;


public class JAXB2ValidationEventCollector extends ValidationEventCollector {


    @Override


    public boolean handleEvent(ValidationEvent event) {


        super.handleEvent(event);


        return true;


    }


}




Test File


import java.io.File;





import javax.xml.XMLConstants;


import javax.xml.bind.JAXBContext;


import javax.xml.bind.Unmarshaller;


import javax.xml.bind.ValidationEvent;


import javax.xml.bind.util.ValidationEventCollector;


import javax.xml.validation.Schema;


import javax.xml.validation.SchemaFactory;




import com.test.trigger.GoldenCopyTrigger;


import com.test.trigger.JAXB2ValidationEventCollector;




public class CheckXMLAgainstXSD {




  public static void main(String[] args){


   CheckXMLAgainstXSD classObj = new CheckXMLAgainstXSD();


   try {


    classObj.checkXML();


   } catch (Exception e) {


    // TODO Auto-generated catch block


    e.printStackTrace();


   }


  }


 


  private void checkXML() throws Exception{


        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 


         Schema schema = sf.newSchema(new File("C:/Data//jaxb/MilestoneTrigger.xsd")); 




         JAXBContext jc = JAXBContext.newInstance(GoldenCopyTrigger.class);




         Unmarshaller unmarshaller = jc.createUnmarshaller();


         unmarshaller.setSchema(schema);


         ValidationEventCollector validationCollector = new JAXB2ValidationEventCollector();


         unmarshaller.setEventHandler(validationCollector);




         GoldenCopyTrigger customer = (GoldenCopyTrigger) unmarshaller.unmarshal(new File("C:/Data/jaxb/sample.xml"));




         if(validationCollector.hasEvents())


         {


             for(ValidationEvent event:validationCollector.getEvents())


             {


                 String msg = event.getMessage();


                 System.out.println("msg-->"+msg);


             }


         } 


 


  }


}

Thursday, April 18, 2013

Manage a composite primary key with an incremental id using JPA and Oracle


The regular way of doing this is to  have a Composite Key Class and mapping the field to use oracle sequence. In most of the scenarios this will fail.
The alternative is to 
1. Create the Composite Primary key as a regular java POJO with no @Column mappings.
2.Create/Override the equals() and hashCode() methods in this class.
3. Now go to the main POJO and instead of defining this composite key as @EmbeddedId, declare the composite primary key attributes your entity, associate the ID class with the entity and define the ID generation strategy,
o   @Id
o   @Column mappings
o   and for the sequence number field alone add like the following
  @Column(name="<>")   @GeneratedValue(strategy=GenerationType.AUTO, generator="<>")   @SequenceGenerator(name="<>", sequenceName="<>") 4.When you create the records, you don’t need to set this field at all. 5. DB will generate the sequence for you. 6.It’s a known defect from JPA that composite keys cannot accept sequence numbers  

Get DB Date and Time Hibernate / JPA



1.If you fire the query " SELECT sysdate param FROM dual;" (or) " SELECT systimestamp param FROM dual;"Hibernate will complain " org.hibernate.MappingException: No Dialect mapping for JDBC type: -101". Not sure of the JPA error
2.To address this, you need to ensure that Hibernate is educated to identify the sysdate (or) systimestamp as a registered field
3.The only way to do this is to addScalar to the SQLQuery
4.But here again if you say addScalar("param",Hibernate.TIMESTAMP), Type fields in org.hibernate.Hibernate are deprecated (and actually removed) as result of HHH-5196
5.So the other way is to say as follows
6.sqlQuery.addScalar("param", org.hibernate.type.TimestampType.INSTANCE);
7.Now that this is done for Hibernate, I was wondering the alternative for JPA.
8.So the thIrd hack was to hack the JPA Entity Manager to use Hibernate scalar as follows
9.query = entityMgrObj.createNativeQuery(queryString);
10.query.unwrap(SQLQuery.class).addScalar("param", org.hibernate.type.TimestampType.INSTANCE);


So here we go





For hibernate

  /**


  * public method to get the database Date and Time


  */


  public Timestamp getCurrentDBTime() throws ORMException { 


   SQLQuery sqlQuery = null;


   String queryString=null;


   Timestamp dbTimeStamp = null;


   try {


    queryString = "SELECT systimestamp param FROM DUAL";


    sessionObj = getHibSession();   


    sqlQuery = sessionObj.createSQLQuery(queryString);


    sqlQuery.addScalar("param", org.hibernate.type.TimestampType.INSTANCE);


    dbTimeStamp = (Timestamp)sqlQuery.uniqueResult(); 


    return dbTimeStamp;


   } catch (HibernateException ex) {


    throw new ORMException(ex, IORMErrorCode.LQM_ORM_E_GETCURRENT_DATE_TIME);


   } catch (RuntimeException ex) {


    throw new ORMException(ex, IORMErrorCode.LQM_ORM_E_GEN_EXCEPTION);


   } finally {


    if (sessionObj != null) {


     sessionObj.close();


    }


   }


   


  }


  /**


  * public method to get the database Date


  */




  public Date getCurrentDBDate() throws ORMException {


   SQLQuery sqlQuery = null;


   String queryString=null;


   Date dbDate = null;


   try {


    queryString = "SELECT sysdate param FROM DUAL";


    sessionObj = getHibSession();   


    sqlQuery = sessionObj.createSQLQuery(queryString);


    sqlQuery.addScalar("param", org.hibernate.type.DateType.INSTANCE);


    dbDate = (Date)sqlQuery.uniqueResult(); 


    return dbDate;


   } catch (HibernateException ex) {


    throw new ORMException(ex, IORMErrorCode.LQM_ORM_E_GETCURRENT_DATE_TIME);


   } catch (RuntimeException ex) {


    throw new ORMException(ex, IORMErrorCode.LQM_ORM_E_GEN_EXCEPTION);


   } finally {


    if (sessionObj != null) {


     sessionObj.close();


    }


   }


  }






For JPA





  /**


  * public method to get the database Date and Time


  */


  public Timestamp getCurrentDBTime() throws ORMException {


   Timestamp dbTimeStamp = null;


   String queryString=null;


   Query query = null;


   try {


    queryString = "SELECT systimestamp param FROM DUAL";


    entityMgrObj = getHibJpaEntityManager();


    query = entityMgrObj.createNativeQuery(queryString);


    query.unwrap(SQLQuery.class).addScalar("param", org.hibernate.type.TimestampType.INSTANCE);


    dbTimeStamp = (Timestamp)query.getSingleResult();


    return dbTimeStamp;


   } catch (HibernateException ex) {


    throw new ORMException(ex, IORMErrorCode.LQM_ORM_E_GETCURRENT_DATE_TIME);


   } catch (RuntimeException ex) {


    throw new ORMException(ex, IORMErrorCode.LQM_ORM_E_GEN_EXCEPTION);


   } 


  


  }


  /**


  * public method to get the database Date


  */


  public Date getCurrentDBDate() throws ORMException {


   Date dbDate = null;


   String queryString=null;


   Query query = null;


   try {


    queryString = "SELECT sysdate param FROM DUAL";


    entityMgrObj = getHibJpaEntityManager();


    query = entityMgrObj.createNativeQuery(queryString);


    query.unwrap(SQLQuery.class).addScalar("param", org.hibernate.type.DateType.INSTANCE);


    dbDate = (Date)query.getSingleResult();


    return dbDate;


   } catch (HibernateException ex) {


    throw new ORMException(ex, IORMErrorCode.LQM_ORM_E_GETCURRENT_DATE_TIME);


   } catch (RuntimeException ex) {


    throw new ORMException(ex, IORMErrorCode.LQM_ORM_E_GEN_EXCEPTION);


   } 


  


  }


setting schema information


schema names in hibernate can be painful but not with the latest releases

Refer to the following URL and you will get useful information of setting the schema names for the application at different levels


  1. Global Level
  2. Entity Level
  3. Property Level and
  4. Association Level

For JPA rather than doing it in every ENTITY class
just create the orm.xml with the following contents

<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm    
    http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
    version="1.0">
    <persistence-unit-metadata>
        <persistence-unit-defaults>
            <schema>mySchema</schema>
        </persistence-unit-defaults>
    </persistence-unit-metadata>
</entity-mappings>

Make sure that you refer this from the persistence.xml as follows

<persistence-unit>
<mapping-file>META-INF/orm.xml</mapping-file>
</persistence-unit>


Speed up Spring loading

add the following to the web.xml

 metadata-complete="true"

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" metadata-complete="true">

and you will see the difference in Spring performance

Thursday, April 4, 2013

Useful Debugging parameters for Hibernate via Log4j

### Hibernate logging configuration ### 

### Log everything (a lot of information, but very useful for troubleshooting) ### 
#log4j.logger.org.hibernate=info 

### Log HQL and SQL ASTs during query parsing ### 
log4j.logger.org.hibernate.hql.ast.AST=DEBUG, SQL_APPENDER 
log4j.additivity.org.hibernate.hql.ast.AST=false 

### log just the SQL 
log4j.logger.org.hibernate.SQL=DEBUG, SQL_APPENDER 
log4j.additivity.org.hibernate.SQL=false 

### log JDBC bind parameters. Very userfull, when debug parameterized queries ### 
log4j.logger.org.hibernate.type=TRACE, SQL_APPENDER 
log4j.additivity.org.hibernate.type=false 

### log schema export/update ### 
#log4j.logger.org.hibernate.tool.hbm2ddl=info 

### log HQL parse trees 
#log4j.logger.org.hibernate.hql=debug 

### log cache activity ### 
#log4j.logger.org.hibernate.cache=info 

### log transaction activity 
#log4j.logger.org.hibernate.transaction=debug 

### Log all JDBC resource acquisition 
#log4j.logger.org.hibernate.jdbc=debug 

### enable the following line if you want to track down connection ### 
### leakages when using DriverManagerConnectionProvider ### 
#log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace 

log4j.appender.SQL_APPENDER=org.apache.log4j.RollingFileAppender 
log4j.appender.SQL_APPENDER.File=c\:/EC_sql.log
log4j.appender.SQL_APPENDER.MaxFileSize=1000KB 
log4j.appender.SQL_APPENDER.MaxBackupIndex=62 
log4j.appender.SQL_APPENDER.layout=org.apache.log4j.PatternLayout 
log4j.appender.SQL_APPENDER.layout.ConversionPattern=[%d] %5p [%t] (%F:%L) - %m%n