Sunday, January 12, 2014

How to set column order in JPA under Eclipse Link


You know that when you automatically generating table structure using JPA entities, the field order you defined in the entity class not going to be sync with correspondent generated table column order. If you are not dealing directly with databases (I mean using native sql) the order of columns won't be a big matter for you. But most of the time we have to go through the database records to fix various problems with the data. So in a time like that, column order will going to matter a lot for a clear and readable result set with native sql.

So how you going to define the column order easily ? When we search on this topic under Eclipse Link, we found following URL.

http://program.g.hatena.ne.jp/halflite/20130801/jpacolumnorder

Above link content is in Japanese. So it is little hard understand even after converting to the English :). So I thought what if I could do it in a better way (at least better for me). I thought it is better if we could define column order meta data within the same entity class (using annotation), so that it is readable for anyone.

With Eclipse Link you can customize the generating table column order by assigning a weight for each field. Please refer following link.

http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Customizers#DescriptorCustomizer

Above does not having how to use weight property in entity class fields, but it contains the concept of extending defined meta data using the @Customizer annotation.

So now lets see how to do this.

Creating the Java Annotation to hold column order meta data
 import java.lang.annotation.ElementType;  
 import java.lang.annotation.Retention;  
 import java.lang.annotation.RetentionPolicy;  
 import java.lang.annotation.Target; 
 
 @Target(ElementType.FIELD)  
 @Retention(RetentionPolicy.RUNTIME)  
 public @interface ColumnPosition {  
  int position();  
 }  
Above is the  custom Java annotation to define the column order meta data in the JPA entity class. Using the position field you can specify the column position.

Eclipse Link Column Order Customize Annotation
 import java.lang.reflect.Field;  
 import java.util.HashMap;  
 import java.util.List;  
 import java.util.Map;  
 import org.eclipse.persistence.config.DescriptorCustomizer;  
 import org.eclipse.persistence.descriptors.ClassDescriptor;  
 import org.eclipse.persistence.mappings.DatabaseMapping;  

 public class EntityColumnPositionCustomizer implements DescriptorCustomizer {  
      @Override  
      public void customize(ClassDescriptor descriptor) throws Exception {  
           descriptor.setShouldOrderMappings(true);  
           List<DatabaseMapping> mappings = descriptor.getMappings();  
           addWeight(this.getClass(descriptor.getJavaClassName()), mappings);  
      }  

      private void addWeight(Class<?> cls, List<DatabaseMapping> mappings) {  
           Map<String, Integer> fieldOrderMap = getColumnPositions(cls, null);  
           for (DatabaseMapping mapping : mappings) {  
                String key = mapping.getAttributeName();  
                Object obj = fieldOrderMap.get(key);  
                int weight = 1;  
                if (obj != null) {  
                    weight = Integer.parseInt(obj.toString());  
                }  
                mapping.setWeight(weight);  
          }  
      }  

      private Class<?> getClass(String javaFileName) throws ClassNotFoundException {  
           Class<?> cls = null;  
           if (javaFileName != null && !javaFileName.equals("")) {  
                cls = Class.forName(javaFileName);  
           }  
           return cls;  
      }  

      private Map<String, Integer> getColumnPositions(Class<?> classFile, Map<String, Integer> columnOrder) {  
           if (columnOrder == null) {  
                columnOrder = new HashMap<>();  
           }  
           Field[] fields = classFile.getDeclaredFields();  
           for (Field field : fields) {  
                if (field.isAnnotationPresent(ColumnPosition.class)) {  
                     ColumnPosition cp = field.getAnnotation(ColumnPosition.class);  
                     columnOrder.put(field.getName(), cp.position());  
                }  
           }  
           if (classFile.getSuperclass() != null && classFile.getSuperclass() != Object.class) {  
                this.getColumnPositions(classFile.getSuperclass(), columnOrder);  
           }  
           return columnOrder;  
      }  
 }  
The DescriptorCustomizer interface containes the method customize() that you should override. If you notice, this class customize() method will get call for every JPA entity class you have in your system. Following is a brief introduction of what each method does in this class.

customize() method

First we informing Eclipse Link that it should use provided weight information by us, using following call.

descriptor.setShouldOrderMappings(true);


Then it getting the mapping information of current processing JPA entity class and calling the addWeight() method.  Inside addWeight() method, we calling the getClass() method that is using to get the Class object using the Java class name.

addWeight() method

This is the method that assign the defined weight in JPA entity class to each field.  First it will read all  annotations in the current JPA entity class by using the getColumnPositions() method.

getColumnPositions() method is a simple method that going through each field in the passed class and find the ColumnPostion annotation and reading the value of it. Note that this is recursive method. The reason to do this is that  the current processing class maybe having base classes, that needs to be scan for ColumnPosition annotation as well. This will return a map of field names and  its defined position value.

Ok. That's about getColumnPositions() method. Now back to the addWeight() method.  So now everything is clear inside this method. It just go through the passed mapping details that received by customize() method and getting each field position through the map it received by calling the getColumnPositions() method.

How to use EntityColumnPositionCustomizer class 

Now let's see how to use above implementation in a real JPA entity class. What I going to show you is a simple class that keeps very basic information about a student. Even though this implementation only having one entity class, typically there going to be many entity classes and most of the time every class will have some common fields. So it is better to create a super class and inherit those common properties. 

Here I'm assuming that you have at least a basic knowledge of JPA as I'm not going to explain about the JPA here.

Following is our base entity class 
 import java.util.Calendar;  
 import javax.persistence.Column;  
 import javax.persistence.GeneratedValue;  
 import javax.persistence.GenerationType;  
 import javax.persistence.Id;  
 import javax.persistence.MappedSuperclass;  
 import javax.persistence.Temporal;  
 import javax.persistence.TemporalType;  
 import lk.j2ee.test.common.ColumnPosition;  
 import lk.j2ee.test.common.EntityColumnPositionCustomizer;  
 import org.eclipse.persistence.annotations.Customizer;  

 @MappedSuperclass  
 @Customizer(EntityColumnPositionCustomizer.class)  
 public class BaseEntity {  
      @Id  
      @GeneratedValue(strategy=GenerationType.AUTO)  
      @Column(name = "id")  
      @ColumnPosition(position = 0)  
      private int id;  

      @Column(name = "created_at", nullable = false)  
      @Temporal(TemporalType.TIMESTAMP)  
      @ColumnPosition(position = 30)  
      private Calendar createdDate;  

      @Column(name = "updated_at", nullable = false)  
      @Temporal(TemporalType.TIMESTAMP)  
      @ColumnPosition(position = 31)  
      private Calendar updatedDate; 
 
      public int getId() {  
           return id;  
      }  
      public void setId(int id) {  
           this.id = id;  
      }  
      public Calendar getCreatedDate() {  
           return createdDate;  
      }  
      public void setCreatedDate(Calendar createdDate) {  
           this.createdDate = createdDate;  
      }  
      public Calendar getUpdatedDate() {  
           return updatedDate;  
      }  
      public void setUpdatedDate(Calendar updatedDate) {  
           this.updatedDate = updatedDate;  
      }       
 }  

In the above class please note that how we have used the implemented column customizer using the @Customizer(EntityColumnPositionCustomizer.class). Also not that the using of @ColumnPosition(position = 0). Some of you may notice that I have given higher nos for createdDate and updatedDate field. That is because I always wanted them to appear at the end of all the columns in the table. Likewise I wanted id column as the first column in the table.

So now lets see our basic Student class which is a child class of above base class.
 import javax.persistence.Column;  
 import javax.persistence.Entity;  
 import lk.j2ee.test.common.ColumnPosition;  

 @Entity(name = "student")  
 public class Student extends BaseEntity {  
      @Column(name = "name", nullable = false, length = 150)  
      @ColumnPosition(position = 1)  
      private String name;  

      @Column(name = "age", nullable = false)  
      @ColumnPosition(position = 2)  
      private int age;  

      public String getName() {  
           return name;  
      }  
      public void setName(String name) {  
           this.name = name;  
      }  
      public int getAge() {  
           return age;  
      }  
      public void setAge(int age) {  
           this.age = age;  
      }  
 }  
Above is the simple Student class. It also contains the @ColumnPosition annotation. Since we already defined the Eclipse Link Customizer at the base class, we do not need to redefine it in here.

So that is is it. If you see any problems or any improvements that should be apply, feel free to post. 

Saturday, January 19, 2008

How to do a simple ajax call (java servlet)

Ajax is a very famous technology for devolop higly interactive web sites. Here I would likes to describe how to do a ajax call to a servelt.

First of all you going to need the following java script for call to the servelt.

function callServer(callback,urlparas) {

httpRequest = false;

if (window.XMLHttpRequest) {

httpRequest = new XMLHttpRequest();

} else if (window.ActiveXObject) {

try {

httpRequest = new ActiveXObject("Msxml2.XMLHTTP");

} catch(e) {

try{
httpRequest = new ActiveXObject("Microsoft.XMLHTTP")
} catch (e){
alert("fetchData, e: " + e);
}
}

} else {
return false;
}

httpRequest.onreadystatechange = callback;
httpRequest.open("POST", "RequestHandler", true);
httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

httpRequest.send(urlparas);

}

Now lets see wht each above function really does.

if (window.XMLHttpRequest) {

httpRequest = new XMLHttpRequest();

} else if (window.ActiveXObject) {

try {

httpRequest = new ActiveXObject("Msxml2.XMLHTTP");

} catch(e) {
try{
httpRequest = new ActiveXObject("Microsoft.XMLHTTP")
} catch (e){
alert("fetchData, e: " + e);
}
}

} else {
return false;
}


above mentioned script part is for creating correct httpRequest object for the ajax call. As you can see if browser supports "window.XMLHttpRequest" it creates a "XMLHttpRequest" type of a httpRequest and so on.. If browser does not support httpRequest simply this function will return false. That means you cannot make ajax calls.

httpRequest.onreadystatechange = callback;

For every ajax call, you have to specify a function which should get executed after web page receive a response from the server. As a example callback function may fill a table with the received data from the server. Here we passing the callback function a parameter.

httpRequest.open("POST", "RequestHandler", true);

Line above describes, which servlet we going to call and the method of calling. Here in this example servlet name is "RequestHandler" and the calling method is "POST". In java web application you should specify all servelts in a file called web.xml. Here you have to provide the exact servelt name you provide in the web.xml file.

httpRequest.send(urlparas);

In most times you wants to make a Ajax call with appended paras to URL. As a example URL "RequestHandler?action=login&loginid=mars&pwd=46". So for make a Ajax call like I mentioned, you have to pass urlparas as "action=login&loginid=mars&pwd=46" to this function.

Hmmmm ! That is all about the function we used to make a Ajax call..

-------------------------------------------------------------------------------------

Here as a example I would like to specify a simple example about how to use above function in a web appliacation. For tht I'm using following servelt. Here I have assumed you have a some knoledge of web devolopment in java.

public class RequestHandler extends HttpServlet {}

Lets think we passing the parameter called "action=sayhello" to the servelt and our callback javascript function is as follows.

function printHello() {}


First lets see how to call above mentoed callServer() function from an web page. Please include the callServer() function in same the same page or write that function in a separate js file and import it in to your web page. Here in the following code, I assumed you have done either one of above ways to specify the callServer() function.

callServer(printHello,"action=sayhello"); // calling the server

// callback function
function printHello() {


if (httpRequest.readyState == 4) {

if (httpRequest.status == 200) {
alert(httpRequest.responseText);
}
}
}




In above printHello() function, we have checked readyState and status of the httpRequest.responseText object to make sure, we recived a succesfull response from the server. So it is required to check those things. Then we just alerting the response from the server to the user. In this case, it should alert as "Hello from the server"



Now lets concider about the servlet. The servelt should read the passed parameter by ajax call and respond to it. The code for doing it as follows.

public class RequestHandler extends HttpServlet {

response.setContentType("text/xml;charset=UTF-8");

String strAction = request.getParameter("action");

final ServletOutputStream writer = response.getOutputStream();

if (strAction.equals("sayhello") {
writer.print("Hello from the server") ;
}


}

Now lets see what above code doing. First we setting the response conetent type. Which here it can be either plain text or xml formatted and the charset is utf-8.
And in next line, we readin the para,eters in the url and then servelt acting acording to the parameter value in the if condition.