Monday, May 5, 2014

Setting default value for fields in Grails Domain Class


class MyDomainClass {
    Date myDate = new Date();
    Boolean myBoolean = false;
    String myString = "";

    static mapping = {
        myDate defaultValue: "now()"
        myBoolean defaultValue: "false"
        myString defaultValue: ""
    }
}

You can use sql now() method rather than Java/Groovy new Date()

Convert Map, HashMap or List/ArrayList to XML and reverse

HashMapToStringXml.java

package pritom;

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by pritom on 5/05/14.
 */
public class HashMapToStringXml {
    public static void main(String[] args) {
        Map<Object, Object> hashMap = new HashMap<Object, Object>();
        hashMap.put("firstName", "Pritom");
        hashMap.put("lastName", "Kumar");

        Map<Object, Object> secondMap = new HashMap<Object, Object>();
        secondMap.put("timeIn", "8:00");
        secondMap.put("timeOut", "5:00");
        hashMap.put("timing", secondMap);

        List<Object> list = new ArrayList<Object>();
        list.add(20);
        list.add(30);
        list.add(40);
        list.add(secondMap);
        hashMap.put("contents", list);

        /* Map to XML and reverse */
        String mapToString = objectToString(hashMap);
        Map parsedMap = (Map) stringToObject(mapToString);
        System.out.println("Map to XML: \n" + mapToString + "\nXML to map:\n" + parsedMap);

        /* List to XML and reverse */
        String listToString = objectToString(list);
        List parsedList = (List) stringToObject(listToString);
        System.out.println("List to XML: \n" + listToString + "\nXML to list:\n" + parsedList);
    }

    public static String objectToString(Object hashMap) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        XMLEncoder xmlEncoder = new XMLEncoder(bos);
        xmlEncoder.writeObject(hashMap);
        xmlEncoder.close();
        return bos.toString();
    }

    public static Object stringToObject(String string) {
        XMLDecoder xmlDecoder = new XMLDecoder(new ByteArrayInputStream(string.getBytes()));
        return xmlDecoder.readObject();
    }
}

Output

Map to XML: 
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_45" class="java.beans.XMLDecoder">
 <object class="java.util.HashMap">
  <void method="put">
   <string>lastName</string>
   <string>Kumar</string>
  </void>
  <void method="put">
   <string>contents</string>
   <object class="java.util.ArrayList">
    <void method="add">
     <int>20</int>
    </void>
    <void method="add">
     <int>30</int>
    </void>
    <void method="add">
     <int>40</int>
    </void>
    <void method="add">
     <object class="java.util.HashMap" id="HashMap0">
      <void method="put">
       <string>timeOut</string>
       <string>5:00</string>
      </void>
      <void method="put">
       <string>timeIn</string>
       <string>8:00</string>
      </void>
     </object>
    </void>
   </object>
  </void>
  <void method="put">
   <string>timing</string>
   <object idref="HashMap0"/>
  </void>
  <void method="put">
   <string>firstName</string>
   <string>Pritom</string>
  </void>
 </object>
</java>

XML to map:
{
    lastName=Kumar, 
    contents=[
        20, 
        30, 
        40, 
        {
            timeOut=5:00, 
            timeIn=8:00
        }
    ], 
    firstName=Pritom, 
    timing={
        timeOut=5:00, 
        timeIn=8:00
    }
}



List to XML: 
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_45" class="java.beans.XMLDecoder">
 <object class="java.util.ArrayList">
  <void method="add">
   <int>20</int>
  </void>
  <void method="add">
   <int>30</int>
  </void>
  <void method="add">
   <int>40</int>
  </void>
  <void method="add">
   <object class="java.util.HashMap">
    <void method="put">
     <string>timeOut</string>
     <string>5:00</string>
    </void>
    <void method="put">
     <string>timeIn</string>
     <string>8:00</string>
    </void>
   </object>
  </void>
 </object>
</java>

XML to list:
[20, 30, 40, {timeOut=5:00, timeIn=8:00}]

Logging Hibernate SQL in Grails With Parameters

Add the following line to DataSource.groovy marked yellow


dataSource {
    pooled = true
    url = jdbc:mysql://localhost/database_name?useUnicode=yes&characterEncoding=UTF-8
    driverClassName = com.mysql.jdbc.Driver
    username = database_user_name
    password = database_password
    dialect = org.hibernate.dialect.MySQL5InnoDBDialect
    dbCreate = "update"
    logSql = true
    properties {
        maxActive = 1000
        maxIdle = 100
        minIdle = 50
        initialSize = 1
        minEvictableIdleTimeMillis = 60000
        timeBetweenEvictionRunsMillis = 60000
        numTestsPerEvictionRun = 3
        maxWait = 10000
        testOnBorrow = true
        testWhileIdle = true
        testOnReturn = true
        validationQuery = "SELECT 1"
    }
}

Add the following two code block to last of the file


log4j = {
    debug 'org.hibernate.SQL'
    trace 'org.hibernate.type.descriptor.sql.BasicBinder'
}

hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory'
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
    format_sql = false
    use_sql_comments = true
}

Example output of trace/debug


Hibernate: /* insert com.pkm.LogTest */ insert into log_test (created, name, display_name, a_long_value) values (?, ?, ?, ?)
2014-05-05 09:42:44,803 [http-bio-8011-exec-10] TRACE sql.BasicBinder  - binding parameter [1] as [TIMESTAMP] - Mon May 05 09:42:44 ALMT 2014
2014-05-05 09:42:44,803 [http-bio-8011-exec-10] TRACE sql.BasicBinder  - binding parameter [2] as [VARCHAR] - Pritom Kumar Mondal
2014-05-05 09:42:44,803 [http-bio-8011-exec-10] TRACE sql.BasicBinder  - binding parameter [3] as [VARCHAR] - pritom
2014-05-05 09:42:44,803 [http-bio-8011-exec-10] TRACE sql.BasicBinder  - binding parameter [4] as [BIGINT] - 3

Saturday, May 3, 2014

Annotating Custom Types in Hibernate

Two new classes are needed. The first is the class you want to use for the column.  In my case, I created a class Phone to store data in database.  Here's my class:

Phone.java


package com.pkm.utils.types;

public class Phone implements java.io.Serializable {
    private String areaCode;
    public String getAreaCode() {
        return areaCode;
    }
    public void setAreaCode(String areaCode) {
        this.areaCode = areaCode;
    }
    public void clearAreaCode() {
        this.areaCode = null;
    }
    
    private String phoneNum;
    public String getPhoneNum() {
        return phoneNum;
    }
    public void setPhoneNum(String phoneNum) {
        this.phoneNum = phoneNum;
    }
    
    public Phone() {
        
    }
    
    public Phone(String phoneNum, String areaCode) {
        this.phoneNum = phoneNum;
        this.areaCode = areaCode;
    }
    
    public Phone(String phoneNum) {
        this.phoneNum = phoneNum;
        this.areaCode = null;
    }
    
    @Override
    public String toString(){
        return areaCode + "-" + phoneNum;
   }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((areaCode == null) ? 0 : areaCode.hashCode());
        result = prime * result + ((phoneNum == null) ? 0 : phoneNum.hashCode());
        return result;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Phone other = (Phone) obj;
        if (areaCode == null) {
            if (other.areaCode != null)
                return false;
        } else if (!areaCode.equals(other.areaCode))
            return false;
        if (phoneNum == null) {
            if (other.phoneNum != null)
                return false;
        } else if (!phoneNum.equals(other.phoneNum))
            return false;
        return true;
    }
}

The factory class is produced by implementing (in the simplest case) org.hibernate.usertype.UserType, you can also see for composite user type. Documentation in this interface is pretty thin, but there are good examples available in the Hibernate distribution. Here's my implementation.

PhoneSimpleType.java


package com.pkm.utils.types;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.StringTokenizer;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;

public class PhoneSimpleType implements UserType {
    @Override
    public int[] sqlTypes() {
        return new int[] {Types.VARCHAR};
    }

    @Override
    public Class returnedClass() {
        return Phone.class;
    }

    @Override
    public boolean equals(Object o, Object o1) throws HibernateException {
        if(o == null && o1 == null) {
            return true;
        }
        if(o == null || o1 == null) {
            return false;
        }
        return o.equals(o1);
    }

    @Override
    public int hashCode(Object o) throws HibernateException {
        if(o != null) {
            return o.hashCode();
        }
        return 0;
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] strings, SessionImplementor si, Object o) throws HibernateException, SQLException {
        String stringValue = rs.getString(strings[0]);
        if(stringValue == null) {
            return new Phone();
        }
        StringTokenizer tokenizer = new StringTokenizer(stringValue, "-");
        Phone phone = new Phone();
        phone.setAreaCode(tokenizer.nextToken());
        phone.setPhoneNum(tokenizer.nextToken());
        return phone;
    }

    @Override
    public void nullSafeSet(PreparedStatement ps, Object o, int i, SessionImplementor si) throws HibernateException, SQLException {
        if(o == null) {
            ps.setNull(i, Types.VARCHAR);
            return;
        }
        ps.setString(i, ((Phone) o).toString());
    }

    @Override
    public Object deepCopy(Object o) throws HibernateException {
        if(o == null || !(o instanceof Phone)) {
            return null;
        }
        Phone e = (Phone) o;
        Phone n = new Phone(e.getPhoneNum(), e.getAreaCode());
        return n;
    }

    @Override
    public boolean isMutable() {
        return true;
    }

    @Override
    public Serializable disassemble(Object o) throws HibernateException {
        Object object = deepCopy(o);
        if(object == null || !(object instanceof Phone)) {
            return null;
        }
        return (Phone) object;
    }

    @Override
    public Object assemble(Serializable srlzbl, Object o) throws HibernateException {
        return deepCopy(srlzbl);
    }

    @Override
    public Object replace(Object o, Object o1, Object o2) throws HibernateException {
        return deepCopy(o);
    }    
}

The core of this class is the two methods which get and set values associated with my new type: nullSafeSet and nullSafeGet.  One key thing to note is that nullSafeGet is supplied with a list of all the column names mapped to the custom datatype in the current query.  In my case, there's only one, but in complex cases, you can map multiple columns to one object (there are examples in the Hibernate documentation).
The final piece of the puzzle is the annotation which tells Hibernate to use the new "Type" class to generate objects of your custom type by adding a new @Type annotation to the column:

Use new type as follows:


@Type(type = "com.pkm.utils.types.PhoneSimpleType")
@Column(name = "phone_details", nullable = true)
private Phone phone = new Phone();
public void setPhone(Phone phone) {
    this.phone = phone;
}
public Phone getPhone() {
    return phone;
}

Use as follows


User user = (User) session.get(User.class, 1);
user.getPhone().setAreaCode("+880");
user.getPhone().setPhoneNum("01727499452");

Output in my database as like (After save/update reading values from database):


Phone area: +880
Phone number: 01727499452

Hibernate Interceptor Example

In order to use an Interceptor with our Session, we need to create a class that extends EmptyInterceptor.. Create a new class with name DataInterceptor.java. And now paste the following code:
package com.pkm.commands;

import java.io.Serializable;
import java.util.Iterator;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;

public class DataInterceptor  extends EmptyInterceptor {
    private int updates = 0;
    private int creates = 0;
    private int loads = 0;
    private int deletes = 0;
    
    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, org.hibernate.type.Type[] types) {
        System.out.println("onSave");
        creates++;
        return true;
    }
    
    // called on load events
    @Override
    public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, org.hibernate.type.Type[] types) {
        System.out.println("onLoad");
        loads++;
        return true;
    }

    @Override
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, org.hibernate.type.Type[] types) {
        System.out.println("onFlushDirty");
        updates++;
        return false;
    }

    @Override
    public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, org.hibernate.type.Type[] types) {
        System.out.println("onDelete");
        deletes++;
    }

    //called before commit into database
    @Override
    public void preFlush(Iterator iterator) {
        System.out.println("preFlush");
    } 

    //called after committed into database
    @Override
    public void postFlush(Iterator iterator) {
        System.out.println("postFlush");
    }
    
    @Override
    public void afterTransactionCompletion(Transaction tx) {
        if ( tx.wasCommitted() ) {
            System.out.println("Creations: " + creates + ", Updates: " + 
                    updates + ", Loads: " + loads + ", Deletes: " + deletes);
        }
    }
}

These are the most basic methods that an Interceptor implements:
  • onSave : Called when you save an object. The object is not persisted yet.
  • onFlushDirty : Called when you update an object. The object is not persisted yet.
  • onLoad: Called when you read an object from database.
  • onDelete : Called when you delete an object. The object is not deleted from the database yet.
  • preFlush : Called before committing to the database.
  • postFlush :  Called after committing to the database.

We should also update HibernateUtil class:


package com.pkm.utils;

import com.pkm.commands.DataInterceptor;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateUtil {
    private static final SessionFactory sessionFactory = buildSessionFactory();
    private static ServiceRegistry serviceRegistry;
    
    private static SessionFactory buildSessionFactory() {
        try {
            Configuration configuration = new Configuration().setInterceptor(new DataInterceptor());
            configuration.configure();
            serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry(); 
            return configuration.buildSessionFactory(serviceRegistry);
        } catch (Exception ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
 
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static void shutdown() {
        // Close caches and connection pools
        getSessionFactory().close();
    }
}

Now create main java class file to test the Interceptor.


package com.pkm.commands;

import com.pkm.domains.User;
import com.pkm.utils.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;

public class TestInterceptor {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        Session session = null;
     Transaction transaction = null;
        try {
            session = HibernateUtil.getSessionFactory().openSession();
            transaction = session.beginTransaction();

            User user = new User(System.currentTimeMillis() + "@gmail.com", "Pritom", "Kumar Mondal");
            session.save(user);
            Long firstId = user.getId();

            user = new User(System.currentTimeMillis() + "@gmail.com", "Pritom", "Kumar Mondal");
            session.save(user);
            Long secondId = user.getId();
            
            user = (User) session.get(User.class, firstId);
            user.setFirstName("First name changed");
            session.save(user);
            
            user = (User) session.get(User.class, secondId);
            session.delete(user);
            
            user = (User) session.get(User.class, Long.parseLong("10"));
            user = (User) session.get(User.class, Long.parseLong("11"));
            user = (User) session.get(User.class, Long.parseLong("12"));
            
            transaction.commit();
        } catch (Exception ex) {
            if(transaction != null) {
                transaction.rollback();
            }
            System.err.println(ex.getMessage());
        } finally {
            if(session != null) {
                session.close();
            }
        }
    }
}

Output of Example:


onSave
Hibernate: insert into User (email, first_name, last_name) values (?, ?, ?)
onSave
Hibernate: insert into User (email, first_name, last_name) values (?, ?, ?)
onDelete
Hibernate: select user0_.id as id0_0_, user0_.email as email0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_ from User user0_ where user0_.id=?
onLoad
Hibernate: select user0_.id as id0_0_, user0_.email as email0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_ from User user0_ where user0_.id=?
onLoad
Hibernate: select user0_.id as id0_0_, user0_.email as email0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_ from User user0_ where user0_.id=?
onLoad
preFlush
onFlushDirty
Hibernate: update User set email=?, first_name=?, last_name=? where id=?
Hibernate: delete from User where id=?
postFlush
Creations: 2, Updates: 1, Loads: 3, Deletes: 1

Download complete example