Monday, November 22, 2010

Avoiding duplicated data using Hibernate

When using Hibernate, it happens that we have to map into the database some DTO beans linked one another by some relations. Let's see an example to explain better the situation:

public class Entity {
private int e_id;
private String type;
private String name;
private String address;
private boolean deleted;

// getters and setters
}

public class Researcher{
private int res_id;
private String fname;
private String lname;
private String email;
private int age;
private Entity entity;
private boolean deleted;

//getters and setters
}

In this case, we have a many-to-one relation between Researcher and Entity.
When we save a new Researcher, we want that Hibernate recognizes whether the related Entity is already stored into the database and avoid its duplication. So, we ask Hibernate to create a relation between the Researcher and the Entity already stored instead saving a new copy of it.
Hibernate always check whether the objects that are related to the one he is saving are already stored and uses the equals method to compare the incoming data to the ones into the database. So, why it doesn't work?
The problem is that the equals method invoked is the one for the class Object that compares the pointers to the objects (that are obviously different).
In order to make Hibernate able to recognize whether two beans are equals, we need to define the appropriate equals and hashCode methods for our beans.
Let's see these methods for Entity:
@Override
public int hashCode() {
final int prime = 23;
int result = 1;
result = prime * result + ((address == null) ? 0 : address.hashCode());
result = prime * result + (deleted ? 1231 : 1237);
result = prime * result + e_id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((type == null) ? 0 : type.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;
Entity other = (Entity) obj;
if (address == null) {
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (deleted != other.deleted)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
}

As can be seen, in the equals method we don't check whether the e_id is the same since when we save a Researcher we don't know the id of the Entity (we don't even know whether it already exists).

And now someone could ask how much time he has to spend in doing such boring methods. The answer is just few seconds.
If you are working with Eclipse, you can easily generate these pieces of code just in few clicks:
  • open the bean in which you want to add these methods and position the cursor in the place where you want to add the code;
  • into the menu select Source->Generate hashCode() and equals()...;
  • selects the fields that you want to be checked to find the equality (all fields are selected as default);
  • check whether the methods generated are ok.

XPath another xml parser

In this period I am working with xml...lot of technologies try to solve the problem of retrieve information from an xml file like Dom, Sax and Jaxb. They all work very fine, however the simple and more easy tools that I found (and use ;) ) for me is xPath.
XPath is a query language designed for querying XML documents that uses path expressions to navigate XML documents. In order to define paths, we have to use a sintax which is similar to the one used for describing paths to files in operating systems.

Let us now start by describing a very simple example of how xPath work.
First of all let me show here a simple example of the xml file. This XML describes the employees that work inside a company.

<Personnel>
    <Employee type="contract">
       <Name>Robin</Name>
       <Salary>3011</Salary>
       <Id>3675</Id>
       <Age>25</Age>
    </Employee>
</Personnel>

Now, if we want to get the information about the age of the Employee named Robin we have to search inside the xml searching for the employee named Robin and, one found it, we can get the age by using the path
"/Personnel/Employee/Age"

A simple and useful code that help us to obtain every information about the employee is shown here:


public class SearchEmplyeesUsingXPath {
    private static String name ="Seagull";
   
     public static void main(String[] args) {

            DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
            domFactory.setNamespaceAware(true);
            DocumentBuilder builder = null;
            try {
                builder = domFactory.newDocumentBuilder();
            } catch (ParserConfigurationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Document doc = null;
            try {
                doc = builder.parse("employees.xml");
            } catch (SAXException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            XPathFactory factory = XPathFactory.newInstance();
            XPath xpath = factory.newXPath();
            XPathExpression expr = null;
            XPathExpression expr2 = null;
            XPathExpression expr3 = null;
            XPathExpression expr4 = null;
            try {
/**
*Here we get the text that we found at this path.  Note that text() retrieve the
* context associated to such path
**/
                expr = xpath.compile("/Personnel/Employee/Name/text()");
                expr2 = xpath.compile("/Personnel/Employee/Salary/text()");
                expr3 = xpath.compile("/Personnel/Employee/Id/text()");
                expr4 = xpath.compile("/Personnel/Employee/Age/text()");
               
            } catch (XPathExpressionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            Object result;
            Object salary;
            Object id;
            Object age;
            try {
 /**
* Get the whole node in which we have found that path
**/
                result = expr.evaluate(doc, XPathConstants.NODESET);
                salary = expr2.evaluate(doc, XPathConstants.NODESET);
                id = expr3.evaluate(doc, XPathConstants.NODESET);
                age = expr4.evaluate(doc, XPathConstants.NODESET);
               
               
                NodeList nodes = (NodeList) result;
                NodeList nSal = (NodeList) salary;
                NodeList ids  = (NodeList) id;
                NodeList ages  = (NodeList) age;
                result = getInfo(nodes,nSal,ids,ages, name);
                System.out.println(result);
              
              
            } catch (XPathExpressionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
          }

/**
* Print method
 **/
    private static Object getInfo(NodeList nodes, NodeList nSal, NodeList ids,
            NodeList ages, String name) {
        String result ="";
        for (int i = 0; i < nodes.getLength(); i++) {
            if(nodes.item(i).getNodeValue().equals(name)){   
   
                result+=nodes.item(i).getNodeValue()+"\n";
                result+=nSal.item(i).getNodeValue()+"\n";
                result+=ids.item(i).getNodeValue()+"\n";
                result+=ages.item(i).getNodeValue()+" \n\n\n";
            }
           
           
        }
        return result;
    }
}


For further information about XPath take a look here
http://www.w3.org/TR/xpath20/

Monday, November 15, 2010

JAXB an overview

JAXB provides a fast and convenient way to bind between XML schemas and Java classes
JAXB provides two main features: the ability to marshal Java objects into XML
the ability to unmarshal XML back into Java objects
Project site: https://jaxb.dev.java.net/

Let us start by defining a simple example. Assume we want to create a simple application that is able to convert a java object into an XML representation.

First of all let me show a simple example of xml code with its related xsd file.
We want to create a schema that represent an xml format like this one:


<entity_list>
  <entity type="university" id="1294681486">
    <name>University of Trento</name>
    <address>Via Sommarive 14, Trento</address>
  </entity>
</entity_list>



Which correspond to the following XML schema


<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="entity_list" type="entityListType" />
   <xsd:complexType name="entityListType">
   <xsd:sequence>
    <xsd:element ref="entity" minOccurs="0"maxOccurs="unbounded"/>
  </xsd:sequence>
  </xsd:complexType>


  <xsd:element name="entity" type="entityType" />
  <xsd:complexType name="entityType">
  <xsd:sequence>
  <xsd:element name="name" type="xsd:string" />
  <xsd:element name="address" type="xsd:string" />
  </xsd:sequence>
<xsd:attribute name="id" type="xsd:integer" use="required"/>
<xsd:attribute name="type" type="xsd:string" use="required"/>
  </xsd:complexType>
</xsd:schema>


In order to convert this object into a java representation, let us define such object (in this case let us define an entityType and entityListType). Note that this operation can be done directly by our environment (Eclipse or NetBeans as you want) or by command line using the command:
>~$ xjc *.xsd
(Libraries at https://jaxb.dev.java.net/)

Once done this, we simply have to create the methods for marshalling and unmarshalling. An example of such a code is shown here.


public class JAXBConvesion {
private static JAXBConvesion jconv;
Marshaller marshaller;
Unmarshaller unMarshaller;
transfer.xml.ObjectFactory factory;
SchemaFactory schemaFactory;
WriteXml writer;

           /**
        * Initialize objects that we are going to use. note that "transfer.xml" in the JAXBContext is the   
        * package in which we have our EntityType and EntityListType java objects.
        * This is a singleton class, we did this only for concurrency 
        **/
private JAXBConvesion() {
   try {
     JAXBContext jaxbContext = JAXBContext.newInstance("transfer.xml");
             marshaller = jaxbContext.createMarshaller();
     marshaller.setProperty("jaxb.formatted.output", new Boolean(true));
     factory = new transfer.xml.ObjectFactory();

     unMarshaller = jaxbContext.createUnmarshaller();
     schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
CustomValidationEventHandler validationEventHandler = new CustomValidationEventHandler();
            unMarshaller.setEventHandler(validationEventHandler);
    writer = WriteXml.getInstance();
} catch (Exception e) {
e.printStackTrace();
}

}
        /**
        * This method convert the java object into XML format representation
        * Constant.entityXml represent the path to the xml file that contains the xml.
        **/
public void marshal(Object res) {
try {
String result = null;
StringWriter sw = new StringWriter();
marshaller.marshal(res, sw);
result = sw.getBuffer().toString();
writer.writeXml(result, Constant.entityXml);
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
        /**
        * This method convert the XML into java object representation
        * entity.xsd is the xml schema  for the entity 
        **/
public EntityListType unmarshalEntityListType() {

try {
Schema schema = schemaFactory.newSchema(new File("entity.xsd"));
unMarshaller.setSchema(schema);

JAXBElement<EntityListType> listElement =(JAXBElement<EntityListType>)unMarshaller.unmarshal(new File(Constant.entityXml));
EntityListType list = listElement.getValue();

return list;
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}



public boolean handleEvent(ValidationEvent event) {
if (event.getSeverity() == ValidationEvent.WARNING) {
return true;
}
if ((event.getSeverity() == ValidationEvent.ERROR)
|| (event.getSeverity() == ValidationEvent.FATAL_ERROR)) {

System.out.println("Validation Error:" + event.getMessage());

ValidationEventLocator locator = event.getLocator();
System.out.println("at line number:" + locator.getLineNumber());
System.out.println("Unmarshalling Terminated");
return false;
}
return true;
}


public static JAXBConvesion getInstance() {
if (jconv == null) {
jconv = new JAXBConvesion();
}
return jconv;
}

Let Hibernate create DB tables

Another problem that I found during my projects is the database creation.
A good design practice is to let hibernate create its own database by itself. I searched for a long time around the net and I found a simple and really useful guide here.
You can find it there however I will put a copy of that code here!



package com.cascadetg.ch11;

/** Various Hibernate-related imports */
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import net.sf.hibernate.tool.hbm2ddl.SchemaExport;
import java.util.Properties;

public class SchemaGeneratorExample
{
// System constants for the current platform directory
token
static String fileSep =
System.getProperty("file.separator");

/** We use this session factory to create our sessions */
public static SessionFactory sessionFactory;

static String[] db_dialects =
{"Mysql",
"com.mysql.jdbc.Driver", //
"DB2", //
"net.sf.hibernate.dialect.DB2Dialect", //
"DB2400", //
"net.sf.hibernate.dialect.DB2400Dialect", //
"Firebird", //
"net.sf.hibernate.dialect.FirebirdDialect", //
"FrontBase", //
"net.sf.hibernate.dialect.FrontBaseDialect", //
"Generic", //
"net.sf.hibernate.dialect.GenericDialect", //
"HypersonicSQL", //
"net.sf.hibernate.dialect.HSQLDialect", //
"Informix", //
"net.sf.hibernate.dialect.InformixDialect", //
"Informix9", //
"net.sf.hibernate.dialect.Informix9Dialect", //
"Ingres", //
"net.sf.hibernate.dialect.IngresDialect", //
"Interbase", //
"net.sf.hibernate.dialect.InterbaseDialect", //
"Mckoi SQL", //
"net.sf.hibernate.dialect.MckoiDialect", //
"Microsoft SQL Server", //
"net.sf.hibernate.dialect.SQLServerDialect", //
"MySQL", //
"net.sf.hibernate.dialect.MySQLDialect", //
"Oracle 9", //
"net.sf.hibernate.dialect.Oracle9Dialect", //
"Oracle", //
"net.sf.hibernate.dialect.OracleDialect", //
"Pointbase", //
"net.sf.hibernate.dialect.PointbaseDialect", //
"PostgreSQL", //
"net.sf.hibernate.dialect.PostgreSQLDialect", //
"Progress", //
"net.sf.hibernate.dialect.ProgressDialect", //
"SAP DB", //
"net.sf.hibernate.dialect.SAPDBDialect", //
"Sybase Anywhere", //
"net.sf.hibernate.dialect.SybaseAnywhereDialect",
"Sybase 11.9.2", //
"net.sf.hibernate.dialect.Sybase11_9_2Dialect", //
"Sybase", //
"net.sf.hibernate.dialect.SybaseDialect",};

public static void main(String[] args)
{
initialization();
}

/**
* Loads the Hibernate configuration information, sets up the
* database and the Hibernate session factory.
*/
public static void initialization()
{
System.out.println("initialization");
try
{
Configuration myConfiguration = new
Configuration();
/**
* Insert here your beans classes for which you want to generate the SQL script
* one for each bean for each table
**/
myConfiguration.addClass(com.cascadetg.ch03.Owner.class);
myConfiguration.addClass(com.cascadetg.ch03.Artifact.class);

Properties myProperties = new Properties();

for (int i = 0; i < db_dialects.length; i = i + 2)
{
String dialect_name = db_dialects[i];
String dialect_class = db_dialects[i + 1];

String dialect_file =
dialect_name.toLowerCase();
dialect_file = dialect_file.replace(' ', '_');
dialect_file += (".sql");

String path = "com" + fileSep + "cascadetg"
+ fileSep + "ch11" + fileSep;

System.out.println("Generating " +
dialect_name);

// Note that this is the only Hibernate property
// set. In particular, there is no JDBC
// connectivity data, nor are we specifying a
// driver!
myProperties.put("hibernate.dialect",
dialect_class);
try
{
// Load the *.hbm.xml files as set in the
// config, and set the dialect.
SchemaExport mySchemaExport = new
SchemaExport(
myConfiguration, myProperties);

mySchemaExport.setDelimiter(";");

// Despite the name, the generated create
// scripts WILL include drop statements at
// the top of the script!
mySchemaExport.setOutputFile(path + "create_"
+ dialect_file);
mySchemaExport.create(false, false) ;

// Generates DROP statements only
mySchemaExport.setOutputFile(path + "drop_"
+ dialect_file);
mySchemaExport.drop(false, false);

System.out.println(dialect_name + " OK.");

} catch (Exception e)
{
System.out.println(e.getMessage());
}
}
}
} catch (Exception e)
{
e.printStackTrace();
}
}
}
Note that in order to make this simple class run, we need to attach to our Java build path a lot of libraries (specially the one needed for the creation of the scripts). Of course if you don't have the libraries in your console you will see some error lines(something about missing libraries).
I found this code really useful. You have only to modify the path in which you want to save the two output files (create and drop) and that's all!

Many-to-many hibernate relations

We all have worked with Hibernate,
one of the biggest problem that we have found is the many-to-many relation. Here we can find a simple example that really :D work.

Assume that we want to create a relation between the table ITEM (that represents a book) and the table RESEARCHER that, as we can imagine, represents the author of a book.

Let us start our step by step guide.

1) create  the item java beans like this


public class Item implements Serializable{
private int item_id;
private String type;
private String title;
private String description;
private String pub_date;

private Set<Researcher> writers;
private boolean deleted;

public Item(){
}

public int getItem_id() {
return item_id;
}


public void setItem_id(int item_id) {
this.item_id = item_id;
}


public String getType() {
return type;
}


public void setType(String type) {
this.type = type;
}


public String getTitle() {
return title;
}


public void setTitle(String title) {
this.title = title;
}


public String getDescription() {
return description;
}


public void setDescription(String description) {
this.description = description;
}


public String getPub_date() {
return pub_date;
}


public void setPub_date(String pub_date) {
this.pub_date = pub_date;
}


public Set<Researcher> getWriters() {
return writers;
}


public void setWriters(Set<Researcher> writers) {
this.writers = writers;
}


public boolean isDeleted() {
return deleted;
}


public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}


and now the researcher java bean



public class Researcher {
private int res_id;
private String fname;
private String lname;
private String email;
private int age;
private Entity entity;
private boolean deleted;


public Researcher() {
}


public int getRes_id() {
return res_id;
}


public void setRes_id(int res_id) {
this.res_id = res_id;
}


public String getFname() {
return fname;
}


public void setFname(String fname) {
this.fname = fname;
}


public String getLname() {
return lname;
}


public void setLname(String lname) {
this.lname = lname;
}


public String getEmail() {
return email;
}


public void setEmail(String email) {
this.email = email;
}


public int getAge() {
return age;
}


public void setAge(int age) {
this.age = age;
}


public Entity getEntity() {
return entity;
}


public void setEntity(Entity entity) {
this.entity = entity;
}


public boolean isDeleted() {
return deleted;
}


public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}


2) Let now Hibernate create its mapping file (researcher.hbm.xml and item.hbm.xml)
To do this step we need to install the Hibernate tool package for eclipse. We used Helios distribution of eclipse. In this case it is sufficient to go into Help -> Eclipse Marketplace and search Hibernate tool and install it.
Once we have this package installed, we just to right click on the package where we have our beans and select New -> Other... In the popup window select Hibernate -> Hibernate XML Mapping File (hbm.xml) . Now we can follow the wizard and it will create the mapping files for all beans in the selected package.
The mapping generator is not able to create many-to-many relations, so we have adjusted the one-to-many relation generated.
Here there is the mapping file for the item:


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Nov 12, 2010 11:48:52 AM by Hibernate Tools 3.4.0.Beta1 -->
<hibernate-mapping>
    <class name="DBmodel.Item" table="ITEM">
        <id name="item_id" type="int">
            <column name="ITEM_ID" />
            <generator class="increment" />
        </id>
        <property name="type" type="java.lang.String">
            <column name="TYPE" />
        </property>
        <property name="title" type="java.lang.String">
            <column name="TITLE" />
        </property>
        <property name="description" type="java.lang.String">
            <column name="DESCRIPTION" />
        </property>
        <property name="pub_date" type="java.lang.String">
            <column name="PUB_DATE" />
        </property>
        <set name="writers" table="ITEM_RESEARCHER" cascade="all" lazy="false">
            <key column="ITEM_ID"/>
            <many-to-many column="RES_ID" class="DBmodel.Researcher" />
        </set>
        <property name="deleted" type="boolean">
            <column name="DELETED" />
        </property>
    </class>
</hibernate-mapping>

The mapping file for the researcher does not contain anything for the many-to-many relation, it doesn't need to know about it. So it contains only the natural mappings for its fields, as we can see:


<hibernate-mapping>
    <class name="DBmodel.Researcher" table="RESEARCHER">
        <id name="res_id" type="int">
            <column name="RES_ID" />
            <generator class="increment" />
        </id>
        <property name="fname" type="java.lang.String">
            <column name="FNAME" />
        </property>
        <property name="lname" type="java.lang.String">
            <column name="LNAME" />
        </property>
        <property name="email" type="java.lang.String">
            <column name="EMAIL" />
        </property>
        <property name="age" type="int">
            <column name="AGE" />
        </property>
        <many-to-one name="entity" class="DBmodel.Entity" cascade="all" lazy="false">
            <column name="ENTITY" />
        </many-to-one>
        <property name="deleted" type="boolean">
            <column name="DELETED" />
        </property>
    </class>
</hibernate-mapping>





3) Let's explain how the many-to-many relation works.
First of all we need a set tag in which we put:

  • name: the name of the field in the bean that stores the Set (HashSet).
  • table: the name of a new table that contains the foreign keys of the two tables. It's a "middle table" that creates the relations between the two tables.
  • cascade="all" and lazy="false": these are mandatory. They allows Hibernate to propagate updates into db tables.
  • key: it contains the name of the column in this table (ITEM) that contains the foreign key for the relation.
  • many-to-many: it contains the name of the column in the target table (RESEARCHER) and the class mapped into the target table