Friday, October 5, 2007

Understanding "inverse" mapping attribute

Generality

This page intends to give an internal view and understanding of inverse="true". Please, please, please read the Hibernate reference guide and especially:

  • Mapping a collection
  • Bidirectional Association
  • Parent Child Relationships

and the FAQs (the official ones and the one from the Wiki) before reading this.

Inverse defines which side is responsible of the association maintenance. The side having inverse="false" (default value) has this responsibility (and will create the appropriate SQL query - insert, update or delete). Changes made to the association on the side of the inverse="true" are not persisted in DB.

Inverse attribute is not related in any way to the navigation through relationship. It is related to the way hibernate generate SQL queries to update association data. Association data are:

  • a column in the one-to-many association
  • a row in the association table in a many-to-many association

Monodirectional association is managed by the only side available through navigation. When association is bidirectional, choosing the manager side allow better SQL optimization, this is the recommended behaviour.

one-to-many sample

Let's have a look at a simple one-to-many sample. Setting inverse="true" is recommanded and allow SQL optimization.

Note that <many-to-one> is always inverse="false" (the attribute does not exist).

<class name="net.sf.test.Parent" table="parent">
<id name="id" column="id" type="long" unsaved-value="null">
<generator class="sequence">
<param name="sequence">SEQ_DEFAULT</param>
</generator>
</id>
<set name="children" lazy="true" inverse="true">
<key column="parent_id"/>
<one-to-many class="net.sf.test.Child"/>
</set>
</class>

<class name="net.sf.test.Child" table="child">
<id name="id" column="id" type="long" unsaved-value="null">
<generator class="sequence">
<param name="sequence">SEQ_DEFAULT</param>
</generator>
</id>
<many-to-one name="parent" column="parent_id" not-null="true"/>
</class>

The inverse="true" is set to the one side.

Proper code

Parent p = new Parent();
Child c = new Child();
p.setChildren(new HashSet());
p.getChildren().add(c);
c.setParent(p);

session.save(p);
session.save(c);
session.flush();

Will do the following SQL queries

Hibernate: select SEQ_DEFAULT.nextval from dual
Hibernate: select SEQ_DEFAULT.nextval from dual
Hibernate: insert into parent (id) values (?)
Hibernate: insert into child (parent_id, id) values (?, ?)

Hibernate insert parent then insert child. Note that my DB has a not null FK constraint on Child(parent_id), inserts work fine because I set <many-to-one not-null="true"

Note that I explicitly save parent and child objets. A better way is to use the cascade="save-update" element. I didn't do it to keep this explanation easier to understand and avoid concepts mismatch.

inverse="true" sample

Insert

Parent p = new Parent();
Child c = new Child();
p.setChildren(new HashSet());
p.getChildren().add(c);
c.setParent(p);

session.save(p);
session.flush(); //flush to DB
System.out.println("Parent saved");

session.save(c);
System.out.println("Child saved");
session.flush(); //flush to DB

Will do the following SQL queries

Hibernate: select SEQ_DEFAULT.nextval from dual
Hibernate: insert into parent (id) values (?)
Parent saved
Hibernate: select SEQ_DEFAULT.nextval from dual
Hibernate: insert into child (parent_id, id) values (?, ?)
Child saved

As you can see the relationship (incarnated by the parent_id column) is set during the child save : this is of the child responsibility. When saving parent, nothing is done on the relationship.

Update

Let's have a look at a relationship update

Parent p = (Parent) session.load(Parent.class, parentId);
Parent p2 = (Parent) session.load(Parent.class, parentId2);

c = (Child) session.find(
"from Child as child where child.parent = ?",
p, Hibernate.entity(Parent.class)).get(0);

// change parent of child c from p to p2
p.getChildren().remove(c);
p2.getChildren().add(c);
c.setParent(p2);

Will do the following SQL queries

Hibernate: select parent0_.id as id from parent parent0_ where parent0_.id=? //get parent 1
Hibernate: select parent0_.id as id from parent parent0_ where parent0_.id=? //get parent 2
Hibernate: select child0_.id as id, child0_.parent_id as parent_id from child child0_ where (child0_.parent_id=? ) //get children of parent 1

Hibernate: select child0_.id as id__, child0_.id as id, child0_.parent_id as parent_id from child child0_ where child0_.parent_id=?
Hibernate: select child0_.id as id__, child0_.id as id, child0_.parent_id as parent_id from child child0_ where child0_.parent_id=?
//load childrens of Parent 1 and 2 (can't avoid this with set, see FAQ)

Hibernate: update child set parent_id=? where id=?

After a proper Java setting of the Parent child relationship (both side), Hibernate, set parent_id column to the proper value. As you can see, only 1 update is executed.

Now, we'll see inverse="true" in action ;-)

Parent p = (Parent) session.load(Parent.class, parentId);
Parent p2 = (Parent) session.load(Parent.class, parentId2);

c = (Child) session.find(
"from Child as child where child.parent = ?",
p, Hibernate.entity(Parent.class)).get(0);

c.setParent(p2);

Will do the following SQL queries

Hibernate: select parent0_.id as id from parent parent0_ where parent0_.id=? //get parent 1
Hibernate: select parent0_.id as id from parent parent0_ where parent0_.id=? //get parent 2
Hibernate: select child0_.id as id, child0_.parent_id as parent_id from child child0_ where (child0_.parent_id=? ) //get children

Hibernate: update child set parent_id=? where id=?

The relationship is updated because I change it on the child side. Note that the object tree is not consistent with the Database (children collections are not up to date). This is not recommanded.

On the contrary,

Parent p = (Parent) session.load(Parent.class, parentId);
Parent p2 = (Parent) session.load(Parent.class, parentId2);

c = (Child) session.find(
"from Child as child where child.parent = ?",
p, Hibernate.entity(Parent.class)).get(0);

p.getChildren().remove(c);
p2.getChildren().add(p);

Will do the following SQL queries

Hibernate: select parent0_.id as id from parent parent0_ where parent0_.id=? //get parent 1
Hibernate: select parent0_.id as id from parent parent0_ where parent0_.id=? //get parent 2
Hibernate: select child0_.id as id, child0_.parent_id as parent_id from child child0_ where (child0_.parent_id=? ) //get children

Hibernate: select child0_.id as id__, child0_.id as id, child0_.parent_id as parent_id from child child0_ where child0_.parent_id=?
Hibernate: select child0_.id as id__, child0_.id as id, child0_.parent_id as parent_id from child child0_ where child0_.parent_id=?
//load childrens of Parent 1 and 2 (can't avoid this see FAQ)

Relationship update is not executed because update is only done on the parent side.

inverse="false"

inverse="false" (the default value) is not optimized for bidirectional relationships.

<class name="net.sf.test.Parent" table="parent">
<id name="id" column="id" type="long" unsaved-value="null">
<generator class="sequence">
<param name="sequence">SEQ_DEFAULT</param>
</generator>
</id>
<set name="children" lazy="true" inverse="false">
<key column="parent_id"/>
<one-to-many class="net.sf.test.Child"/>
</set>
</class>

<class name="net.sf.test.Child" table="child">
<id name="id" column="id" type="long" unsaved-value="null">
<generator class="sequence">
<param name="sequence">SEQ_DEFAULT</param>
</generator>
</id>
<many-to-one name="parent" column="parent_id" not-null="true"/>
</class>

The inverse="false" is set to the one side.

insert

Parent p = new Parent();
Child c = new Child();
p.setChildren(new HashSet());
p.getChildren().add(c);
c.setParent(p);

session.save(p);
session.save(c);
session.flush();

Will do the following SQL queries

Hibernate: select SEQ_DEFAULT.nextval from dual
Hibernate: select SEQ_DEFAULT.nextval from dual
Hibernate: insert into parent (id) values (?)
Hibernate: insert into child (parent_id, id) values (?, ?)
Hibernate: update child set parent_id=? where id=?

Parent is responsible of the relationship. Hibernate insert parent, insert child then update the relationship (as a request to the parent). Two SQL orders are executed (one insert and one update) instead of one.

Note that I cannot do a flush between session.save(p) and session.save(c) because, parent, which is responsible of the relationship, needs a persistent child to play with.

update

Let's have a look at a relationship update

Parent p = (Parent) session.load(Parent.class, parentId);
Parent p2 = (Parent) session.load(Parent.class, parentId2);

c = (Child) session.find(
"from Child as child where child.parent = ?",
p, Hibernate.entity(Parent.class)).get(0);

p.getChildren().remove(c);
p2.getChildren().add(c);
c.setParent(p2);

Will do the following SQL queries

Hibernate: select parent0_.id as id from parent parent0_ where parent0_.id=?    //get parent 1
Hibernate: select parent0_.id as id from parent parent0_ where parent0_.id=? //get parent 2
Hibernate: select child0_.id as id, child0_.parent_id as parent_id from child child0_ where (child0_.parent_id=? )
//get first child for parent 1

Hibernate: select child0_.id as id__, child0_.id as id, child0_.parent_id as parent_id from child child0_ where child0_.parent_id=?
Hibernate: select child0_.id as id__, child0_.id as id, child0_.parent_id as parent_id from child child0_ where child0_.parent_id=?
//load childrens of Parent 1 and 2 (can't avoid this see FAQ)

Hibernate: update child set parent_id=? where id=? // child.setParent
Hibernate: update child set parent_id=null where parent_id=? //remove
Hibernate: update child set parent_id=? where id=? // add

As you can see, having set inverse="true" allow the relationship to be managed by the parent side AND the child side. Several updates to the association data are done. This is inefficient considering the inverse="true" equivalent.

Parent p = (Parent) session.load(Parent.class, parentId);
Parent p2 = (Parent) session.load(Parent.class, parentId2);

c = (Child) session.find(
"from Child as child where child.parent = ?",
p, Hibernate.entity(Parent.class)).get(0);

p2.getChildren().add(c);

Will do the following SQL queries

Hibernate: select parent0_.id as id from parent parent0_ where parent0_.id=?    //get parent 1
Hibernate: select parent0_.id as id from parent parent0_ where parent0_.id=? //get parent 2
Hibernate: select child0_.id as id, child0_.parent_id as parent_id from child child0_ where (child0_.parent_id=? )
//get first child for parent 1

Hibernate: select child0_.id as id__, child0_.id as id, child0_.parent_id as parent_id from child child0_ where child0_.parent_id=?
Hibernate: select child0_.id as id__, child0_.id as id, child0_.parent_id as parent_id from child child0_ where child0_.parent_id=?
//load childrens of Parent 1 and 2 (can't avoid this see FAQ)

Hibernate: update child set parent_id=? where id=? // add

The relationship is properly set but the object model is in inconsistent state. This is not recommanded.

Conclusion

Using and understanding inverse="true" is essential to optimize your code. Prefer using inverse="true" on bidirectional association. After this tutorial it will be soooo easy ;-)

Source: simoes.org

Tuesday, September 25, 2007

Java: Few important points when it comes to Strings

How many times have you coded a check for String being null or empty? Countless times, right? I have. We use some ready-to-use classes from open source frameworks or we write our own StringUtils class. More or less they all implement the same thing and it always looks similar to the following code snippet:

String s = ...
if (s == null || s.equals(""))...

or similar to the following, which trims leading and ending whitespaces

String s = ...
if (s == null || s.trim().equals(""))...

Of course you could also do this:

"".equals(s)

which is a case when you do not care if String s is null and you don't have to worry about NPE as if won't happen ("" is never null, whereas s could be). But that's another story.

I have had "extra" warnings turned on in my IDE for couple of days. But today my IDE suprised me when it highlighted

[1] s.equals("")

and suggested that I could optimize it by making it to

[2] s.length() == 0

And guess what?! The IDE was right! I looked at the suggested code briefly, gave it a bit of thought and agreed that it would probably be faster. Method [1] creates a new instance of the String (an empty String, yes I know that all instances of "" would be caught during compilation and optimized and that they all would refer to the same instance). Just to be on the safe side I looked at the source of the String class.

And here is what I found. The length() method returns and integer primitive, which is not calculated with each method call to length(). It is rather a member variable (or constant, as Strings are invariants) of String class that is calculated when new String instance is created. So this method would be super fast.

public int length() {
return count;
}

On the other side, there is the equals() method, which is fast as well, but not as fast as length method. It has to do a check for class, class casting and comparison of count members (that's what length method returns).

public boolean equals(Object anObject) {
if (! (anObject instanceof String))
return false;
String str2 = (String) anObject;
if (count != str2.count)
return false;
if (value == str2.value && offset == str2.offset)
return true;
int i = count;
int x = offset;
int y = str2.offset;
while (--i >= 0)
if (value[x++] != str2.value[y++])
return false;
return true;
}

And remember the few important points when it comes to Strings:

  • Do not compare Strings with == operator. Unless you want to compare the object references. Use equals() method.
  • Do not construct new instances like new String("abc"). Simple "abc" will do, unless you really mean that you need a new instance of String with same value.
  • Do not concatenate Strings in loops using + operator. It's faster to use StringBuffer (or StringBuilder, which is in Tiger and is not synchronized) append() and then toString() methods instead. The plus (+) operator constructs new String object each time.

Source: hanuska.blogspot.com

Tuesday, August 21, 2007

Java: PermGen OutOfMemory

This blog is in relation to Java PermGen OutOfMemory issue as described in Frank Kieviet's blog entries:

    Classloader leaks and How to fix Classloader leaks? 

To summarize, a new instance of custom Classloader is created by Application Server whenever a new application (.ear, .jar, .war) is deployed to the server, and this Classloader is used to load all the classes and resources contained in this application.  Benefit in this approach is that, this way applications are self-contained and isolated from each other, and there are no conflicts between different applications.  When an application is undeployed from server, its associated Classloader is also unloaded, and it is subject to garbage-collection by JVM.

As described in Frank's blog, there are situations in which Classloaders cannot be garbage-collected because of dangling references to them thru most unexpected places, and this will cause memory-leak in the PermGen space (a special section of heap).  To find the cause of this problem, I used JDK 6.0's jmap and jhat utility to generate memory dump and analyze memory dump, respectively.

Orphaned Classloader

jhat utility can be easily extended to include your own query on the heap snapshot, you need to download and modify the jhat source code though.  I added a new query to find all the Orphaned Classloaders in memory and display all the reference links to these Orphaned Classloaders.  By orphaned, I mean these classloader instances that have no strong-reference chains to them from the root set, except by these strong-references chains from rootset that goes through instance of Class loaded by the Classloader.  To illustrate this, see the diagram below (solid line = strong-reference, dash-line = weak-reference) :

The yellow Classloader instance is orphaned, because the only strong-reference chain to it from root set is the chain that goes through B.class, and B.class is loaded by this Classloader (all the red lines).  All other references that do not go through classes loaded by this Classloader are weak-references.  This scenario is a possible suspect of Classloader leak, because most likely Orphaned Classloaders are not intended result of programmer, there are exceptions though.  By using this query, we can easily find all the possible suspects, and then goes through each one to determine if they are real memory leak or not.

blogs.sun.com

Monday, August 6, 2007

Java research: Anonymous Inner Classes

There are a lot of articles through Internet which have mistakes regarding anonymous inner classes in Java. Anonymous inner class:

  • has no name;
  • can’t be declared as static;
  • can be instantiated only once.

Let me show you the truth.
Consider the following code:

public class Anonymous {
public static void main(String[] args) {
Runnable anonym = new Runnable() {
public void run() {
}
};
}
}

In order to get the name of inner class write down the following:

anonym.getClass().toString().

You’ll get something like that: Anonymous$1.
Anonymous class can be either static or non-static. It depends on the block in which the class have been declared. In the previous example the anonymous class was static. In this case we can create the second instance of this class in such a way:

Runnable anonym2 = (Runnable) anonym
.getClass().newInstance().

There is no need in type cast in JDK 1.5.
If the anonymous class was declared in non-static block, we have to provide a reference to the outer class to the proper constructor (in reflection veritas!). In the other case we’ll get the InstantiationException.
Here we have an example (determining of proper constructor and exception handling are not shown below):

public class Anonymous {
public void nonStaticMethod() {
Runnable anonym = new Runnable() {
public void run() {
}
};
Constructor[] constructors = anonym.getClass()
.getDeclaredConstructors();
Object[] params = new Object[1];
params[0] = this;

Runnable anonym2 = (Runnable) constructors[0]
.newInstance(params);
}

public static void main(String[] args) {
Anonymous example = new Anonymous();
example.nonStaticMethod();
}
}

In this example we have to use getDeclaredConstructors instead of getConstructors. Method getConstructors will return only public constructors, while needed constructor is protected one.


Have a nice day.


Technorati Tags: , ,