Tuesday, October 17, 2006

Java 5, EJB 3 Persistence and Hibernate annotations

Java 5, EJB 3 Persistence and Hibernate annotations

I finally decided to try out some code with Java 5 annonations. For this I chose to try hibernate annotations implementation, which implements EJB 3 persistence with some enhanced annotations of its own.

Pre -requisites
1) Java 5 SDK
2) Latest Hibernate jar with dependent libraries from the lib folder of the download.
3) Latest Hibernate annotation jar with dependent libraries from the lib folder of the download.
4) Ant

Domain Objects

The domain objects are Person and Child. The relationship is one-to-many from person to child and many-to-one from Child to person. The java POJO's are fairly straight forward for this, so I will share the xml here.

Person.hbm.xml


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="test.suchak.hibernatetest.domainobjects.Person" table="Person">
<id
name="ID"
column="ID"
>
<generator class="sequence">
<param name="sequence">PERSONIDSEQUENCE</param>
</generator>
</id>
<property name="firstName">
<column name="FirstName"/>
</property>
<property name="lastName">
<column name="LastName"/>
</property>

<list name="children" cascade="all" inverse="true">
<key column="parent_id"/>
<index column="child_id"/>
<one-to-many class="test.suchak.hibernatetest.domainobjects.Child"/>
</list>

</class>

</hibernate-mapping>


Child.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="test.suchak.hibernatetest.domainobjects.Child" table="Child">
<id
name="child_id"
column="child_id"
>
<generator class="sequence">
<param name="sequence">CHILDIDSEQUENCE</param>
</generator>
</id>
<property name="name">
<column name="name"/>
</property>
<property name="age">
<column name="age"/>
</property>
<many-to-one name="person"
column="parent_id"
class="test.suchak.hibernatetest.domainobjects.Person"
cascade="all"
/>
</class>

</hibernate-mapping>



XML Session factory configuration
The session factry configuration here is prettty simple. Just read the hbm files and pass them to the hibernate configugation.

public class MyHibernateSessionFactory {
......
private void buildSessionFactory(String[] fileNames) {

Configuration config = new Configuration();

for(int i=0;i<filenames.length;i++){
config.addFile(hbmDir + File.separator + fileNames[i]);
}
sessionFactory = config.buildSessionFactory();
} ....
}


Now the sessionFactory.openSession() method will retun the sessions we need. Using the session we can now do CRUD on the child and person POJO's, in the traditional hibernate world.

Annotations

Now instead of the xml files we will use annotations for the same two POJO's person and child.

Person.java


package test.suchak.hibernatetest.domainobjects;

import java.util.List;
//import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.OneToMany;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.MapKey;
import javax.persistence.CascadeType;

/**
* @author Suchak.Jani
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*
*/

@Entity
@Table(name="Person")
public class Person {
private Integer ID;
private String firstName;
private String lastName;
private List children;

public Person(){

}
/**
* @param firstName2
* @param lastName2
*
*/
public Person(String firstName, String lastName) {
this.firstName=firstName;
this.lastName=lastName;
}

/**
* @return Returns the firstName.
*/
@Column(name = "FirstName")
public String getFirstName() {
return firstName;
}
/**
* @return Returns the iD.
*/
@Id @GeneratedValue(generator="sequence")
@GenericGenerator(name="sequence", strategy = "sequence",
parameters = {
@Parameter(name="sequence", value="PERSONIDSEQUENCE")
}
)
public Integer getID() {
return ID;
}
/**
* @return Returns the lastName.
*/
@Column(name = "LastName")
public String getLastName() {
return lastName;
}
/**
* @param firstName The firstName to set.
*/
public void setFirstName(String firstName) {
this.firstName = firstName;
}
/**
* @param id The iD to set.
*/
public void setID(Integer id) {
ID = id;
}
/**
* @param lastName The lastName to set.
*/
public void setLastName(String lastName) {
this.lastName = lastName;
}

/**
* @return Returns the children.
*/
@OneToMany(mappedBy="person", cascade=CascadeType.ALL)
//@OneToMany(mappedBy="person")
@MapKey(columns=@Column(name="child_id"))
public List getChildren() {
return children;
}
/**
* @param children The children to set.
*/
public void setChildren(List children) {
this.children = children;
}
}


Child.java


package test.suchak.hibernatetest.domainobjects;

//import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.ManyToOne;
import javax.persistence.JoinColumn;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;

/**
* @author Suchak.Jani
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
@Entity
@Table(name="Child")
public class Child {

private Integer child_id; // ID
private String name;
private Integer age;
private Person person;


// need a default cosntructor for hibernate to be able to read for this object
public Child() {

}

/**
* @param parent_id2
* @param name2
* @param age2
*/
public Child(Person person,String name, Integer age) {

this.setPerson(person);
this.setName(name);
this.setAge(age);
}
/**
* @return Returns the age.
*/
@Column(name = "age")
public Integer getAge() {
return age;
}
/**
* @param age The age to set.
*/
public void setAge(Integer age) {
this.age = age;
}
/**
* @return Returns the child_id.
*/
@Id @GeneratedValue(generator="sequence")
@GenericGenerator(name="sequence", strategy = "sequence",
parameters = {
@Parameter(name="sequence", value="CHILDIDSEQUENCE")
}
)
public Integer getChild_id() {
return child_id;
}
/**
* @param child_id The child_id to set.
*/
public void setChild_id(Integer child_id) {
this.child_id = child_id;
}
/**
* @return Returns the name.
*/
@Column(name = "name")
public String getName() {
return name;
}
/**
* @param name The name to set.
*/
public void setName(String name) {
this.name = name;
}

/**
* @return Returns the person.
*/
@ManyToOne
@JoinColumn(name="parent_id",nullable=false)
public Person getPerson() {
return person;
}
/**
* @param person The person to set.
*/
public void setPerson(Person person) {
this.person = person;
}
}


Annotation Session factory configuration

The session factry configuration here is different than the xml session factory as shown below.


public class AnnotationHibernateSessionFactory {
....
/**
* @param files
*/
private void buildSessionFactory() {
try {
sessionFactory = new AnnotationConfiguration()
.addAnnotatedClass(Person.class)
.addAnnotatedClass(Child.class)
.buildSessionFactory();
} catch (Throwable ex) {
// Log exception!
throw new ExceptionInInitializerError(ex);
}
}


Key Points

1) XML files not needed.
2) An import per annotation used in the POJO.
3) (inverse="true") is now equivalent to mappedBy.
4) Most annotations are in javax.persistence package.
5) The new AnnotationConfiguration class is to be used instead of the Configuartion class.
6) The xml tags class and class name are not necessary.

Code

The working code is available for download below.

hibernatejdk5test
hibernatejdk5test....
Hosted by eSnips

Java and unsigned numbers

Java and unsigned numbers

Recently there was a requirement for some binnary data read/write work. I started out with some java code. I used the nio ByteBuffer to read and write data.

Something was not right. Whenever i tried to read some sample data the values were right at times and wrong(negative) at other times.

Then a senior consultant colleague, reminded me that all java numbers are 2's complement. In short the MSB(most significant bit) is used to indicate positive or negative number. For further details on 2's complement, please read http://en.wikipedia.org/wiki/Two's_complement .

That sent me on a trail to firgure out how could i read/write an unsigned number using java.

The basic understanding is that you use a bigger number to store the smaller signed number. That way the bigger signed number becomes the unsigned smaller number.

For example a byte is 8 bits. Its range in java is +127 to -128. (Please read up on the 2's complement bit to understand how we get the negative number)

0111 1111 = 127
1000 0000 = - 128 (2's complement)

So in order to convert this byte to unsinged byte we will incease the byte to a short.

A short in java is 16 bits

0000 0000 0000 0000 0111 1111 = 127
0000 0000 0000 0000 1000 0000 = 128

Here is some code to test this

public class Test {
public static void main(String args[]) {
byte b = -128;
short s = (short)(0x00FF & b);
System.out.println("Byte b = " + b);
System.out.println("Short s = " + s);
}
}

The Result is
D:\>java Test
Byte b = -128
Short s = 128

Please note that i have not directly casted to a short, instead i have done a bit and operation with all 1's (F in hex is 1111 in binary). That way the number is preserved at the byte level. If i had just cast it to a short then java would have treated it as a neagtive number i.e. it still would be -128.

Here is a link that explains clearly how to use binary opreations to achieve this for all number types - http://www.darksleep.com/player/JavaAndUnsignedTypes.html and also Java's integer sizes - http://www.scism.sbu.ac.uk/jfl/Appa/appa1.html

Why does java have no unsigned numbers ?

Sean R. Owens at http://www.darksleep.com/player/JavaAndUnsignedTypes.html, digs up old emails and interviews about Java and Oak where James Gosling explains this :-

http://www.gotw.ca/publications/c_family_interview.htm

Gosling: For me as a language designer, which I don't really count myself as these days, what "simple" really ended up meaning was could I expect J. Random Developer to hold the spec in his head. That definition says that, for instance, Java isn't -- and in fact a lot of these languages end up with a lot of corner cases, things that nobody really understands. Quiz any C developer about unsigned, and pretty soon you discover that almost no C developers actually understand what goes on with unsigned, what unsigned arithmetic is. Things like that made C complex. The language part of Java is, I think, pretty simple. The libraries you have to look up.

Finally Java has no unsigned numbers and when dealing with binary data which might contain unsigned numbers, one has to keep this fact in mind.