9/18/2010

Java ResultSet Mapping Utility


Most of the time when we are not using any ORM tool like JPA or Hibernate or Toplink for database access; we will be writing lot of code for iterating through ResultSet and populating our DTO objects. Here I am going to introduce a utility for doing this.

Most of the our DAO classes will return either an Object or a Collection of Objects. So in my mapping utility I am going to give two methods - toObject and toList.

This utility doesn't do any object mapping to database mapping. We can use this only for creating application specific DTO from our ResultSet.

Now look how a typical DAO class look like:
public class EmployeeDetailsDAO {
 public EmployeeDetailsVO getEmployeeDetails(Long employeeId) {
  EmployeeDetailsVO emp = null;
  PreparedStatement stat = null;
  Connection con = null;
  ResultSet rs = null;
  try {
   con = datasource.getConnection();
   stat = con.prepareStatement("Query to Fetch Employee Details");
   stat.setLong(1, Long.valueOf(employeeId));
   rs = stat.executeQuery();

   if(rs.next()) {
    emp = new EmployeeDetailsVO();
    // Code to populate EmployeeDetailsVO from ResultSet
    ......
    .....
   }
  } catch (SQLException e) {
   throw new DAOException(e);
  } finally {
   closeAll(rs, stat, con);
  }
  
  return emp;
 }
}

Here suppose if you are fetching like 25 columns, then you will have to write atleast 25 lines of code like
YourDTO.setField(ResultSet.getField())
which is quiet annoying.

The Basic idea of my utility is very simple:
  • First get all the fields in your DTO class using java Refletction.
  • Iterate through each field.
  • Get the name and Type of the Field.
  • Using the name and type of the field call ResultSet.get<DTO FieldType>.
  • Set the value to the corresponsing DTO class object

But there are some problems with this approach.
  • It is not always a good idea to name your DTO instance variables same as database column names.
  • Not always database column types match your DTO types.
  • Not required that all your DTO instance variable have to be mapped to ResultSet

First problem can be solved by using alias names in the query. But I feel like it is not a good idea. If I am the only one who is going to this utility I can live with alias names. But solution to all these problems are there in all the ORM tools. Use Custom Annotation!

For name and type problem I am going to create a custom annotation Column.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
 String name() default "";
 Class type() default Object.class;
}

and for not mapping instance variable with ResultSet I decided to go with the same Transient annotation available as part of JPA.

Take a loot at a typical DTO object.
public class EmployeeDetailsVO implements Serializable {

 private static final long serialVersionUID = 4865243695898605677L;

 @Column(name="emp_id)
 private Long empno;

 @column(name="dept_id",type=String.class)
 private String deptno;
 
 private String fname,lname,address1,city,
  state,zip,busphone,busext,title,internetadd,deptName;
  
 @Transient
 private String formattedEmpId;

 .......................
 Geters and Setter will go here
 .....................
}
In the above example you can see empno and deptno field is annotated with Column. empno is annotated because of the name change and deptno is annotated to handle name as well as type difference. You can see formattedEmpId filed is annotated with Transient, so Mapper will ignore this field when doing the mapping.

Now the real ResultSetMapper class:
public class ResultsetMapper<T> {
 
 @SuppressWarnings("unchecked")
 private static ResultsetMapper thisInstance;
 
 @SuppressWarnings("unchecked")
 public static <T> ResultsetMapper<T> getInstance() {
  if(thisInstance == null) {
   synchronized (ResultsetMapper.class) {
    if(thisInstance == null)
     thisInstance = new ResultsetMapper<T>();
   }
  }
  return thisInstance;
 }
 
 private ResultsetMapper() { }

 public T toObject(ResultSet rs, Class<T> toClazz) {
  T obj = null;
  Field[] fields = toClazz.getDeclaredFields();
  try {
   if(rs.next() && fields != null && fields.length > 0) {
    obj = toClazz.newInstance();
    populateFields(rs, obj, fields);
   }
  } catch (Exception e) {
   e.printStackTrace();
   throw new CustomException(e);
  }
  
  return obj;
 }
 
 public List<T> toList(ResultSet rs, Class<T> toClazz) {
  List<T> listObj = Lists.newLinkedList();
  T obj = null; 
  Field[] fields = toClazz.getDeclaredFields();
  if(fields == null || fields.length == 0) return listObj;
  try {
   while(rs.next()) {
    obj = toClazz.newInstance();
    populateFields(rs, obj, fields);
    listObj.add(obj);
   }
  } catch (Exception e) {
   e.printStackTrace();
   throw new CustomException(e);
  }
  
  return listObj;
 }
 

 private void populateFields(ResultSet rs, T obj, Field[] fields)
   throws IllegalAccessException, SQLException {
  String fieldName = null;
  for(Field f : fields) {
   if("serialVersionUID".equals(f.getName())) continue;
   f.setAccessible(true);
   fieldName = f.getName();
   Class fieldType = f.getType();
   Annotation[] annotations = f.getDeclaredAnnotations();
   if(annotations != null && annotations.length > 0) {
    if(annotations[0].annotationType() == Column.class) {
     Column colAnn = (Column) annotations[0];
     fieldName = colAnn.name();
     if(colAnn.type() != Object.class)
      fieldType = colAnn.type();
    } else if(annotations[0].annotationType() == Transient.class) continue;
   }
   if(fieldType == Long.class) 
    f.set(obj, rs.getLong(fieldName));
   
   if(fieldType == String.class) 
    f.set(obj, rs.getString(fieldName));
   
   if(fieldType == Integer.class) 
    f.set(obj, rs.getInt(fieldName));
   
   if(fieldType == Double.class) 
    f.set(obj, rs.getDouble(fieldName));
   
   if(fieldType == Date.class) {
    java.sql.Date sqlDate = rs.getDate(fieldName);
    f.set(obj, new Date(sqlDate.getTime()));
   }
   if(fieldType == BigDecimal.class) 
    f.set(obj, rs.getBigDecimal(fieldName));
   
   if(fieldType == Boolean.class) 
    f.set(obj, rs.getBoolean(fieldName));
   
   if(fieldType == Float.class) 
    f.set(obj, rs.getFloat(fieldName));
   
   if(fieldType == Short.class) 
    f.set(obj, rs.getShort(fieldName));
   
   if(fieldType == Timestamp.class) 
    f.set(obj, rs.getTimestamp(fieldName));
  }
 }
}

There are certain limitations for this utility.
  • You cannot use primitive types for your DTO fields. Always use wrapper classes. Java5 AutoBoxing feature will minimize this effort.
  • Wil support only certain types - Integer, Float, Long, String, BigDecimal, Boolean, Shot, TimeStamp and Date.

Now will look at our old EmployeeDetailsDAO.
public class EmployeeDetailsDAO {
 public EmployeeDetailsVO getEmployeeDetails(Long employeeId) {
  EmployeeDetailsVO emp = null;
  PreparedStatement stat = null;
  Connection con = null;
  ResultSet rs = null;
  try {
   con = datasource.getConnection();
   stat = con.prepareStatement("Query to fetch employee details");
   stat.setLong(1, Long.valueOf(employeeId));
   rs = stat.executeQuery();
   ResultsetMapper mapper = ResultsetMapper.getInstance();
   emp = mapper.toObject(rs, EmployeeDetailsVO.class);
  } catch (SQLException e) {
   throw new DAOAppException(e);
  } finally {
   closeAll(rs, stat, con);
  }
  
  return emp;
 }
}
Now look at another DAO which returns a List.
public class BranchDAO {

 public List<BranchVO> getAllBranches() throws DaoException {
  List<BranchVO> branches = null;
  PreparedStatement stat = null;
  Connection con = null;
  ResultSet rs = null;
  try {
   con = datasource.getConnection();
   stat = con.createStatement();
   rs = stat.executeQuery("get all Branches.query");
   
   ResultsetMapper<BranchVO> mapper = ResultsetMapper.getInstance();
   branches = mapper.toList(rs, BranchVO.class);
  } catch (SQLException e) {
   throw new DaoException(e);
  } finally {
   this.closeAll();
  }
  return branches;
 }
}

9/17/2010

Using AspectJ with RAD


Here I am going to explain how to use AspectJ with RAD 7.5 and Websphere Application Server 7.0.

Installing Aspect to RAD
The AspectJ Development Tools project provides support for AspectJ development on the Eclipse platform. To install Aspect here I am using Update Manager of Eclipse platform. The Update Manager can be accessed by clicking Help -> Software Updates -> Available Software -> Add Site.

Location for AspectJ update for RAD 7.5 is http://archive.eclipse.org/tools/ajdt/34/update/.

After install and RAD restart, you should be able to see Aspect Development Tools.

Here I am going to explain how to develop an aspect to process an annotation during a class constructor call. Using this aspect I can overcome the problem of calling injectMembers in my previous blog.

We will start by defining our Constructor Aspect.
package com.mine.myframework.aspects.ConstructorAspect;

import com.google.inject.Injector;

public aspect ConstructorAspect {
 
 before() : execution(new(..)) && !this(ConstructorAspect) { 
  Class clazz = thisJoinPoint.getThis().getClass();
  Injectable injectable = clazz.getAnnotation(Injectable.class);
  if(injectable != null) {
   String clazzName = thisJoinPoint.getThis().getClass().getName();
   StringBuilder builder = new StringBuilder(clazzName);
   builder.append(" constructor invoked ");
   Object[] params = thisJoinPoint.getArgs();
   if(params != null && params.length > 0) {
    builder.append("with arguments ")
        .append(Arrays.toString(params));
   }
   builder.append("............");

   Injector injector = Guice.createInjector(new MyModule());
   injector.injectMembers(this);
  }
 }
}

This aspect will get called before executing any code inside your constructor. But I don't want to call Ijector in every class. So what I am doing is I created an Annotation Injectable to annotate your class. If Injectable annotation is present in your class only Injection logic will execute.

Here is code for Injectable annotation.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Injectable {

}

If you want to read more about AspectJ refer to Programming Guide.

Since I am packaging this aspect inside a utility jar file and I am gonna use this aspect in classes inside other jar file. For that I need to use AspectJ Load Time Weaving feature to weave constructor aspect to classes outside of my utility jar. To do this I need to package AspectJ configuration file aop.xml with the my utility jar file.

<?xml version="1.0" encoding="UTF-8"?>
<aspectj>
 <aspects>
      <aspect name="com.mine.myframework.aspects.ConstructorAspect"/>
      <include within="com.mine..*"/>
     </aspects>
    <weaver>
     <include within="com.mine..*"/>
    </weaver>
</aspectj>

We need to place aop.xml file inside the META-INF folder of the utility jar file.

As you know to enable load time weaving we need to configure aspectweaving jar file using -javaagent JVM argument.

Now I am going to explain how to configure our Websphere Application Server to use AspectJ Load Time Weaving.

Configure -javaagent JVM argument.
In Websphere Admin Console -> Servers -> Server Types -> Websphere Application Server -> <your server> -> Java and Process Management -> Process Definition -> Java Virtual Machine -> Add Generic JVM argument.

-javaagent:pathto/apectjweaving.jar


WAIT!!! Since Websphere security is enabled by default, and the aspectJ weaver is located outside the Websphere libraries - java security policy won't let it load - and you can't start your server!!! Copy the jars into the Websphere lib directory.


Now your Websphere Application Server is configured for ApectJ Load Time Weaving.

Any my problem with calling injectMembers is solved. Now if I want to inject an EJB to my POJO just annotate my POJO with Injectable annotation.


@Injectable
public class MyPOJO {

 // Injecting Remote EJB 3.0
 @EJB(name="ejb/MyRemoteBean")
 private MyRemote remote;
 
 // Injecting Local EJB 3.0
 @EJB(name="ejb/MyLocalBean", type="local")
 private MyLocal local;
 
 // Injecting Remote EJB 2.1
 @EJB(name="ejb/MyRemoteBean21", home=MyRemoteHome.class)
 private MyRemote21 remote1;
 
 // Injecting Local EJB 2.1
 @EJB(name="ejb/MyLocalBean21", type="local", home=MyLocalHome.class)
 private MyLocal21 local1;
  
 public void test() {
  remote.test();
  local.test();
  remote1.test();
  local1.test();
 }
}

Now we can hide all the injection logic from users. Enjoy!

9/09/2010

Injecting EJB to POJO Using Google Guice






Google Guice is a lightweight Dependency Injection Framework that can be used by Applications where Relation-ship/Dependency between Business Objects have to be maintained manually in the Application code. Guice takes benefit of Java 5.0 annotations.

Java EE5 supports EJB injection to some extend. We can inject EJB references only to certain components like EJB, Servlets etc. Java EE5 EJB injection doesn't work with normal POJO classes. In this article I am explaining how to inject EJB references to any class with the help of Google Guice.

In Java EE5 EJB can be injected using @EJB annotation. First we are also going to create a custom annotation for EJB injection.

Here is the of custom @EJB annotation.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@BindingAnnotation
public @interface EJB {
 String name();
 String type() default "remote";
 
 @SuppressWarnings("unchecked")
 Class home() default EJBHome.class;
}
In @EJB annotation name will refer the JNDI name of the EJB you want to inject. type refers the type of the EJB whether it is local or remote (default value). home refers the Home interface class (only for EJB 1.x & 2.x).

Next we are going to extend explore Custom Injection feature in Google Guice.In addition to the standard @Inject driven injections, Guice includes hooks for custom injections. Each custom injection requires a type listener, an injection listener, and registration of each.

TypeListeners get notified of the types that Guice injects. MembersInjectors and InjectionListeners can be used to receive a callback after Guice has injected an instance. The instance is first injected by Guice, then by the custom members injectors, and finally the injection listeners are notified

We will start our custom injection by registering a type Listener EJBTypeListener in Google Guice Module.

public class EJBTypeListener implements TypeListener {

 @Override
 public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
  for (Field field : typeLiteral.getRawType().getDeclaredFields()) {
   if (field.isAnnotationPresent(EJB.class)) {
    EJB dsan = field.getAnnotation(EJB.class);
    typeEncounter.register(new EJBMemberInjector<I>(field,dsan.jndiName()));
   }
  }
 }
}
EJBTypeListener will scan through a types fields looking for a field annotated with @EJB annotation. For each field that's found we register a EJBMemberInjector.

Finally we are implementing EJBMemberInjector to set the EJB reference.

public class EJBMemberInjector<T> implements MembersInjector<T> {
 
 private final Field field;
 private String jndiName;
 private final String type;
 
 @SuppressWarnings("unchecked")
 private final Class home;
 
 EJBMemberInjector(Field f) {
  this.field = f;
  InjectEJB annotation = f.getAnnotation(EJB.class);
  this.type = annotation.type();
  this.jndiName = annotation.name();
  this.home = annotation.home();
  this.field.setAccessible(true);
 }
 
 @SuppressWarnings("unchecked")
 private Object findRemoteEJB() {
  Context initialContext = null;
  Object remoteRef = null;

  if(remoteRef == null) {
   try {
    initialContext = new InitialContext();
    
    Object obj = initialContext.lookup(this.jndiName);
    
    Class clazz =  this.field.getType();
    remoteRef = PortableRemoteObject.narrow(obj, clazz);
   } catch (Exception e) {
    e.printStackTrace();
    throw new CustomException(e);
   } finally {
    try {
     if(initialContext != null) initialContext.close();
    } catch(Exception e) {}
   }
  }
  
  return remoteRef;
 }
 
 private Object findLocalEJB() {
  Context initialContext = null;
  Object localRef = null;

  if(localRef == null) {
   try {
    initialContext = new InitialContext();
    
    localRef = initialContext.lookup(this.jndiName);
   } catch (Exception e) {
    e.printStackTrace();
    throw new CustomException(e);
   } finally {
    try {
     if(initialContext != null) initialContext.close();
    } catch(Exception e) {}
   }
  }
  
  return localRef;
 }
 
 @SuppressWarnings("unchecked")
 private Object findRemoteEJB20() {
  Context initialContext = null;
  Object remoteHomeRef = null, remoteObj = null;
  
  Class remoteHomeClazz = this.home;
  try {
   initialContext = new InitialContext();
   
   Object remoteHome = initialContext.lookup(this.jndiName);
   remoteHomeRef = PortableRemoteObject.narrow(remoteHome, remoteHomeClazz);
   
   Method createMethod = remoteHomeClazz.getMethod("create");
   remoteObj = createMethod.invoke(remoteHomeRef);
   
  } catch (Exception e) {
   e.printStackTrace();
   throw new CustomAppException(e);
  } finally {
   try {
    if(initialContext != null) initialContext.close();
   } catch(Exception e) {}
  }
  return remoteObj;
 }
 
 @SuppressWarnings("unchecked")
 private Object findLocalEJB20() {
  Context initialContext = null;
  Object localRef = null, localObj = null;
  
  Class localHomeClazz = this.home;
  try {
   initialContext = new InitialContext();

   Object obj = initialContext.lookup(this.jndiName);
   localRef = PortableRemoteObject.narrow(obj, localHomeClazz);
   
   Method createMethod = localHomeClazz.getMethod("create");
   localObj = createMethod.invoke(localRef);
  } catch (Exception e) {
   e.printStackTrace();
   throw new CustomException(e);
  } finally {
   try {
    if(initialContext != null) initialContext.close();
   } catch(Exception e) {}
  }
  
  return localObj;
 }
 
 @Override
 public void injectMembers(T t) {
  try {
   if(EJBHome.class != this.home && EJBLocalHome.class != this.home) {
    if("remote".compareToIgnoreCase(this.type) == 0)
     this.field.set(t, this.findRemoteEJB20());
    else
     this.field.set(t, findLocalEJB20());
   } else {
    if("remote".compareToIgnoreCase(this.type) == 0)
     this.field.set(t, this.findRemoteEJB());
    else
     this.field.set(t, findLocalEJB());
   }
  } catch (Exception e) {
   e.printStackTrace();
   throw new CustomException(e);
  } 
 }
}


For EJB2.1 you can even cache Home interface. You can use a WeakHashMap inside EJBMemberInjector and store all home interfaces and do a map lookup before doing EJB JNDI lookup.

In Google Guice module register EJBTypeListener
public class MyModule implements Module {
 
 @Override
 public void configure(Binder binder) {
  binder.bindListener(Matchers.any(), new EJBTypeListener());
 }
}

Inside you POJO:

public class MyPOJO {

 // Injecting Remote EJB 3.0
 @EJB(name="ejb/MyRemoteBean")
 private MyRemote remote;
 
 // Injecting Local EJB 3.0
 @EJB(name="ejb/MyLocalBean", type="local")
 private MyLocal local;
 
 // Injecting Remote EJB 2.1
 @EJB(name="ejb/MyRemoteBean21", home=MyRemoteHome.class)
 private MyRemote21 remote1;
 
 // Injecting Local EJB 2.1
 @EJB(name="ejb/MyLocalBean21", type="local", home=MyLocalHome.class)
 private MyLocal21 local1;
 
 
 public MyPOJO() {
  Injector injector = Guice.createInjector(new MyModule());
  injector.injectMembers(this);
 }
 
 public void test() {
  remote.test();
  local.test();
  remote1.test();
  local1.test();
 }
}

When I released this API, most of the time developers keep forgetting to call injector.injectMembers(this);. So I decided to remove this call. Finally I got that working with the help of AspectJ. I will explain this in my next blog.