Wednesday, July 16, 2008

Doing StoredProcedures as NamedQueries in Hibernate and JPA Java Persistence API or EJB3.0

Here are some wise words of wisdom regarding stored procedures and Hibernate from Mark Spritzler from the JavaRanch:

In Hibernate there are some caveats to stored procedures.

1. Only one out parameter and it must be the first parameter
2. The out parameter must be a refcursor. No other types will work.

If you have to work with a stored procedure that returns a varchar, you can call session.getConnection() and work directly with the jdbc connection. Although in this case you can't use a named query.

How to Map an Integer Column Value of 0 (zero) to a null, in Hibernate with MySQL Oracle or DB2?

A question I often get is how to map a zero 0 value to a null value in the database.

According to Gavin King, the man behind Hibernate, the way to map 0 to null is to write a UserType that transforms 0 to null, and use that custom UserType as the identified type. Hibernate will not create a schema with default values, so in this case, you're on your own.

Another neat Hibernate trick is to use the nullif function in HQL. It's slick:

from Clazz c where nullif(c.cat, 0) is null

If both expressions are equal, null is returned, otherwise the first expression is returned, as you would expect. This simply helps out in the coding for the null or zero values.

Hibernate Forum Topic on Mapping a 0 to a null...




Solve ORA-01002: Fetch Out Of Sequence Error Message Oracle Dialect

This is an error that is generated specifically by Oracle. It can happen under a variety of circumstances, but lets look at what the Oracle database documentation says:

ORA-01002: fetch out of sequence

Cause: This error means that a fetch has been attempted from a cursor which is no longer valid. Note that a PL/SQL cursor loop implicitly does fetches, and thus may also cause this error. There are a number of possible causes for this error, including: 1) Fetching from a cursor after the last row has been retrieved and the ORA-1403 error returned. 2) If the cursor has been opened with the FOR UPDATE clause, fetching after a COMMIT has been issued will return the error. 3) Rebinding any placeholders in the SQL statement, then issuing a fetch before reexecuting the statement.

Action: 1) Do not issue a fetch statement after the last row has been retrieved - there are no more rows to fetch. 2) Do not issue a COMMIT inside a fetch loop for a cursor that has been opened FOR UPDATE. 3) Reexecute the statement after rebinding, then attempt to fetch again.


This can all be confusing. And it can be frustrating.

Sometimes, I've seen this simply due to data in the database problems. If it's troubling you, do a small query on your Oracle8i or 9 database and see if you are searching past the last record in the returned list. If you are, you might just be missing data or something in your database - phantom rows!

"After enabling Hibernate log and DB trace, we noticed 2 records missing in the DB, and it seems that this caused the problem. So, for now, I would say that the problem isn't related to hibernate." -Hibernate.org Forum Posting

I've also seen this caused by people looping through a ref cursor that is being used as an out parameter in the code. Instead, you can avoid the ORA-01002: Fetch Out Of Sequence Exception by opening two different and separate database cursors, and use one as the out parameter, and use the other cursor throughout the loop. -orafaq.com

Monday, July 14, 2008

Can Hibernate Map to a Database View Without A Primary Key Defined?

If you're not updating or creating new rows in the view, you can simply map an entity to the db2 view or oracle view. That's usually the easiest thing to do when mapping db views.

And while views don't have primary keys it is very easy to define a view that has a surrogate key. I'd recommend this to easy the pain of mapping it.

Hibernate and a Database View

Here's some info from Hibernate.org:

"Does Hibernate3 support database views?

Of course. From the point of view of Hibernate, a view is just like any other table (except that you might not be able to update or insert to a view). You can easily map a class to a view by specifying table="view_name".

In Hibernate3, you may even map an entity class to a SQL query result set using the subselect mapping element. This is useful if you are unable to define new views in your database. "

Using Hibernate and Database Views

My Hibernate Tutorials

My WebSphere Tutorials
My Sun Certification Mock Java Exams
My Portlet Development and Portal Administration Tutorials

Friday, July 11, 2008

Review: Hibernate Made Easy: Simplified Data Persistence with Hibernate3 and JPA Annotations (Java Persistence API)

Here's a great review I just received on amazon.

You know, I really love getting feedback for my books. I appreciate criticism, I incorporate updates and fixes quickly, and I always take feedback seriously. The nice thing about self-publishing is that I can get updates, changes and improvements into press within a few weeks.

Anyways, I just got this review from who I consider to be a new friend, on Amazon.com. Certainly made my day, which is quite an accomplishment on a day when the DOW dropped below 11000. :)

***
Hibernate3 & JPA Book Review: Hibernate Made Easy


5.0 out of 5 stars An excellent step by step guide to learning Hibernate, July 11, 2008
By J. Dewberry (Atlanta, GA United States) - See all my reviews
(REAL NAME)
I love this book. I wish all technical books were written this way.

A few months ago, I had a job interview and they asked me if I knew Hibernate. I told them that I didn't know it, but i could learn it. So they gave me a little sample database problem - they wanted me to build a one-to-many, bi-directional join - and asked me to spend the weekend reading tutorials on the web and see if I could get something up and running by Monday morning. So I hit it hard. I read everything I could find, just trying to get something that worked. But I wasnt able to get it working, so I didnt get the job. But I still wanted to learn Hibernate, so I bought the book "Java Persistence with Hibernate" that everybody raves about. That book was not good for me. It seemed ponderous. It has all sorts of references to JSR white papers and stuff like that. I dont care about all that. I just wanted to find something that would help me figure out how to get my project running. I read 500 pages of that book, and I still felt confused.

So then I bought Hibernate Made Easy, and I LOVE it! It starts out with some very simple examples, so you get something working and you start to gain confidence. The first chapter is about how to set up Hibernate, and the second chapter is about testing your setup. Then he starts off with some very simple code that saves a record to the database. And then gradually he increases the complexity of the problems and the solutions until by the end of the book you're ready to solve real world problems. It's great. It's very logical and straightforward, and surprisingly, it's also fun to read. I mean, this is a book about Java persistence, but it's fun to read. How did he do that?

This book is thorough, easy to read, light-hearted, and it tackles all the difficult topics in a logical, step-by-step way. I think it's an outstanding book. After reading it I feel prepared to tackle my next interview. I feel like I can confidently say "I know Hibernate."

Thursday, July 10, 2008

Hibernate Made Easy Book Feedback from Mark Spitzer of the JavaRanch

When my book on Hiberante was released on amazon a short while ago, I was lucky enough to have Mark Spritzler from the JavaRanch to ask for a copy of the book to go over. To be honest, I was a little surprised when Mark sent me some feedback. I mean, the guys a pretty respected expert on the topic of Hibernate and ORM mapping in general, so I really don't think there was a lick of information that he could learn from the book, but he did read it, and he sent me some great feedback.

He noticed a coulple of little things that have been in thorn in my side since re-reading the original printing. One thorn was the updateAll method that calls saveOrUpdate, but doesn't really need to since the User object is loaded and subsequently changes all within the same open session. If you do that, there's no need to update, because the Hibernate Session is still keeping track of any state changes, and will commit them when the transaction is finished.

He also noticed the change from User u to User user. I made that change after about writing a third of the book, but I was worried that if I went back to the first third and did a find/replace thing, then I'd end up missing a bunch of references and the code would break. :( I do have a line that mentions the change when it first happens, but perhaps it is not enough.

Another thing he notes is that I place the annotation on the getter, not the variable. That's they way my last few project managers have requested it, and that's the way I wrote the book. But it seems the trend is actually to put the annotation on the property, not the getter. :( I'll make a note of that in the next printing of the Hibernate book.

I also think Mark's mention of disagreeing with me on HQL vs. the Criteria API is hugely important. I make a few glib remarks about HQL, but someone like Mark, who has really done alot of work in this area, knows the value of HQL. His opinion on the project should probably hold much more water than mine.

Anyways, here's his feedback. i do hope he doesn't mind me sharing the email he sent me. It's really more embarassing to me than to him. :)


"Hey Cameron, I finished reading your Hibernate book over my vacation, and got a good list for you. I had found 2 spelling errors, but I wanted to read through the book and didn't note the pages. I am also sure I saw somewhere, where you created an instance variable called "user or "u", and then later in the same code switched it around. Something like

User user = new User();
u.setPassword();

But when I went page by page through the book three times, I couldn't find it again. It might be that I was looking at the code and maybe it was just in the text part. Anyway, it might be nice to consistently use "user" or just "u" in all code that makes Users.

So some notes about my notes. I take it many reasons for things in your book it so show the basics, and just enough to not scare people off. In some cases not show them all options, but just the basic stuff.

OK, so for my notes

Around page 64-65. Basically it is about the @Id annotation. In the case of all your code, you always put the @Id on the getter. But it is also able to go on the actual field/instance variable. And based on where you put your @Id, is where you put your other Annotations, unless you use an special attribute of the annotations. I think something like access="field" or something like that.


Page 67 - In all your code you use addAnnotatedClass(), and in the book never mention that in the config file you can add one setting that will make Hibernate search the classpath for any class that has an @Entity annotation and automatically add it.

Page 76 - default field naming for ids. Correct me if I am wrong, but I was always under the impression that Hibernate will concatenate the class name with the id attribute as the default pk id field. For instance, if I have the User class with the id attribute, it would look for a user_id field as the PK.

Page 109 - JDBC and HQL variable injection with "?" I thought the first "?" in jdbc and Hibernate was the "0" position and not "1", that it was zero based?

page 117 - updateAll() method is calling the update() method in the for loop. But since the data objects were loaded in the session, then changed, that Hibernate is already maintaining those objects, and therefore a call to update() is unneccesary, as they will have update statements run at commit()

page 129 - SessionFactory singleton. Most people implement singletons with static initializers instead of the single check in the "get" method. The code as is could create two instances of the SessionFactory.

Page 190-192 - Do you want to mention @NamedQueries for when you have more than one named query declared?

Mapping Inheritence chapter - a whole big topic here. So I am saving it for last.

Page 323 - Title shows @OneToMany. Did you mean @OneToOne? also look at the text in the first paragraph there for another @OneToMany


Ok the mapping inheritence chapter. As far as exactly what is written, it is fine. But there is another view point, or perspecitve in terms of the object model that the text would not work for, and is one that is used in the JBoss Hibernate class. If you have an object model that has one super class and two direct subclasses. So a parent with two children, instead of your grand-parent/parent/child example. In the case of two children then you would find that for a database the third normal form will be the better data model to map to. Now in one or two cases the polymorphic query would be slower, in other cases the queries will be faster in this model.

I definitely enjoyed the book, and will give it a good review. Although I disagree with you on Criteria versus HQL, but I am more of a whatever will work best for the particular query. So for simple queries, I love Criteria, but once I add more tables/objects/joins, and more complex queries, give me HQL anyday over Criteria. :)

I think your book does a great job in its intention to get someone who is brand new to Hibernate, up and running quickly and understanding the basics to effectively take on the Hibernate learning curve. (I better copy/paste that into my review)

Talk to you later

Mark Spritzler"

Errata for Hibernate Made Easy: Simplified Data Persistence with Hibernate and JPA Annotations

So, I've got some great feedback from readers over the past month or so with regards to my Hibernate3 book. Not too many errors, a few typos, and lots of great suggestions. Seriously, the Java community is so awesome with regards to helping each other out. It makes the whole process very rewarding!

I'm going to just post some of the feedback here, in no particular order for now, just so I can get it out there. So, here goes!



**********************
(From TUN)
Book: Hibernate Made Easy
Page: 137
In chapter 5, the code for the improved version of the User class using HibernateUtil doesn't compile. I had to add the following line of code to work.

import

com.examscam.HibernateUtil;

Tun.

(I believe this was fixed in the second printing)


***********************************

More from Tun

Hi,
There was something in the book that made me scratch my head. I had to spend about 40 minutes to figure this out. (I am talking about the very first printing edition of Hibernate Made Easy book.)
On page 41, you recommended to install JDK 1.5 or higher. On page 75, you gave an example on compiling the User.java class using "javac -classpath "c:\_hiblib\*" User.java". What I found was that the wildcarding "*" the classpath is JDK 1.6 feature. It's not supported on JDK 1.5. Since I had JDK 1.5, it wasn't working for me. After I upgraded to JDK 1.6, it all worked fine. Just wanted to let you know.
Tun.

I still haven't figured this out for Java5...Maybe someone can give me the right compiler syntax?

***********************************
Book: Hibernate Made Easy
Edition: Very First Printing
On page 223, the code for the abstract ExamScamDAO class has the "import com.examscam.ExamScamException;" statement. Up to page 223, I couldn't find any reference or definition of com.examscam.ExamScamException. Also, the code works fine without the import statement. So I assume it's a typo.

Yup...Again, I believe I fixed this for the second printing. :( See what happens when you play around with ideas when you're writing a book. This should just be a warning, not a compile error.

***********************************
From Tun - this guy rocks!

On page 257, the code for user.jsp, on line 3 it says
contentType="text/html;
It should be
contentType="text/html"
Semicolon should be replaced with double quotes.


***********************************
On the same page,on line 10, the code is instantiating the interface UserDAO.
Currently,
UserDAO userDAO = new UserDAO;
Should be
UserDAO userDAO = new HibernateUserDAO;

user.jsp is a pretty neat example. I learned a lot. Thanks.

This is actually a REAL code error. Tun wouldn't take the $100 reward. I am sending him some signed copies of the next update, not to mention a thank you in the print as well. Great guy!
***********************************

I never promised to help deploy a web app in my Hibernate Book. I leave that to the Sevlet and JSP books out there. But Tun did provide some guidance, saying:

"I really appreciate this user.jsp example. One other thing on the example. I had to copy standard.jar and jstl.jar files to my WEB-INF/lib directory. I am not sure what I did was the standard way. But I got it working."

So, you many need the standard.jar and jstl.jar files to deploy this application to Tomcat.

"Regarding the jstl issue I had, I am running Tomcat 5.5.26. When I tried to bring up user.jsp, Tomcat complained about not being able to resolve the uri=http://java.sun.com/jsp/jstl/core. I found my solution at http://forum.java.sun.com/thread.jspa?threadID=764807&messageID=4362177."

***********************************
Amit Anand saved my but on these ones...You need to make a few changes to actually get the Inheritance Mapping to run. It's not a compile error, but it is a showstopping runtime omission.

Working through the chapter on mapping inheritance

If you use
@Id
@GeneratedValue

With
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
On the Ancestor class

Throws the following error

Cannot use identity column key generation with mapping for: com.anand.hibernatesample.domain.Parent

I got around this as follows

public Class Ancestor
{
@Id
@org.hibernate.annotations.GenericGenerator(name = "hibernate-uuid",
strategy = "uuid")

@Column(name = "ancestor_id")
private Long id;

....

}

I am Using MySQL (maybe a different version from yours) Actually, the SchemaExport throws the error when you try to run it as is from the book and I have seen various people posting this error on hibernate forums - cannot use the native Id generator with the TABLE_PER_CLASS strategy. If you paste the actual error message I pasted below and do a google search - you will see what I mean (just do a google search on

Cannot use identity column key generation with mapping for
)


***********************************
Alex Fanti was kind enought to help me with some typos in the prose:

Pg 16-80 (even) header
Pg 17 Line 1
Pg 22 PP 2 Line 4
Pg 52 PP 3 Line 4
Pg 90 PP 3 Line 1

Pg 98 Line 1
Pg 158 PP 2 Line 6
Pg 193 Line 6
Pg 196 Line 2
Pg 215 Line 2

Pg 217 Line 3-4
Pg 293 PP 3 Line 4
Pg 314 PP 2 Line 3
Pg 323 Section Header
Pg 356 Section Header

***********************************
Jim Dewberry helped me here:

I found a small typo and thought I'd pass it along in case nobody else did already.
On the last line on page 54 you spelled "compiled" as "comiled".


***********************************
Robert de Bésche had this great find that nobody else noticed!

In the image on page 385 (showing the database structure for chapter 20 -Advanced Mappings) of your Hibernate book there is what seems to be a strangely named property in the ‘address’ table listing. The column of ‘address’ mapping addresses to clients is named “insider_id”. I would have expected this to be “client_id” as the @JoinColumn annotation specifies in the listing on page 379? Is it an error, or am I missing something?

No, you're not missing anything Robert....That was me who messed up. Thanks!
***********************************
Jim Dewberry added some more:

I found a few little typos you might want to fix. On page 90 right underneath the "Persistent Objects" header, the sentence says "Once a JavaBean has touched by the Hibernate Session," and I think you might want to add the word "been" so that it reads "Once a JavaBean has BEEN touched by the Hibernate Session".
Also, the first sentence on line 98 says "You will also notice that I have set they type attribute" and I think you probably meant to say "set THE type attribute" instead.

Also, page 106, line 2: "It is completely up to the you" should be "It is completely up to you"
Also, page 140, line 1 "one of the thing you might have noticed" should be "one of the thingS you might have noticed"
page 163, first line of 2nd paragraph: "Once you being a transaction" should be "Once you begin a transaction"
Also, I found a small coding error. There's a missing quote on page 257. On line 3 the contentType="text/html is missing the end quote.
Also, I didnt feel like typing out all the code for creating sample data for the Criteria examples, so I wrote a little class that generates sample data. Feel free to use it or to offer it to other readers if you could just give me credit. I attached a zip file with the java code.



***********************************
Andrej didn't like my naming of banks in my example. It's good, solid, feedbac:

On page 299 of your book towards the bottom of the " public classInterest", you have:
Long wayne=new Long(99); Long mario=new Long(88);
mario is not a bank name, and I think that is what you want to describe. Should have a bankname variable, e.g, firstNationalBank etc. Same correction needed on page 300.

Sometimes, when you're up late at night, you start doing things that keep your mind interested. 88 and 99 are obviously Mario Lemiuex and Wayne Gretzky's jersy numbers, but alsa, maybe it doesn't help the learning process. I'll mark that for an update.
***********************************
Ademiju also notice the mistake on the DAO, along with a few other errors in the prose. Thanks Ademiju!

1. The most important one is on page 256/257, the fact that you have instantiated an interface.

UserDAO userdao = new UserDAO();

2. Page 274. In the last past paragraph you mentioned that foo table is the primary mapping table and bar will be the secondary but it seem to have swapped around in your sample code below it.

3. Page 146, at the start of the first Paragraph, should this say “With our” instead of “With out”?

***********************************

Well, that's it for now!

***********************************





Jim's Sample Code Class for everyone:

package com.examscam;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Random;

import org.hibernate.Query;
import org.hibernate.Session;

import com.examscam.model.User;

public class SampleData {

private static final String alphanumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static final String[] domains = {"@Yahoo.com", "@Hotmail.com", "@Gmail.com"};
private static final String[] names = {
"Mary", "Joe", "Tom", "Sally", "Sallie", "Jim", "Tim", "Bill", "Brad", "Damian",
"Katrina", "Shelley", "Shannon", "Amie", "Cathy", "Kathy", "Elizabeth", "Blake",
"Peggy", "Sabrina", "Samantha", "Chris", "Matt", "Vince", "Bran", "Megan", "Sean",
"Jillian", "Chrissi", "Mike", "Matthew", "David", "Jeff", "Shelly", "Sherra",
"Robin", "Curtis", "Lev", "Andrew", "Richard", "Derrick"
};

public static void main(String[] args){

deleteAll();
for (int i = 0; i < 20; i++){
createSampleData();
}
}

public static void deleteAll(){
Session session = HibernateUtil.beginTransaction();

List allUsers;
Query queryResult = session.createQuery("from User");
allUsers = queryResult.list();

for (int i = 0; i < allUsers.size(); i++){
User user = (User)allUsers.get(i);
session.delete(user);
}

HibernateUtil.commitTransaction();
}


public static void createSampleData(){

User u = new User();

u.setEncryptedPassword(createPassword());
u.setRegistrationDate(createDate());
u.setLastAccessTime(createDateTime(u));
u.setLoginName(createName());
u.setPassword(createPassword());
u.setVerified(createRandomBoolean());
u.setEmailAddress(createEmailAddress(u));

Session session = HibernateUtil.beginTransaction();
session.saveOrUpdate(u);
HibernateUtil.commitTransaction();
}

private static java.util.Date createDateTime(User u){

java.util.Date accDate = generateDateTime();
java.util.Date regDate = convertCalendarToDate(u.getRegistrationDate());

while (regDate.after(accDate)){
accDate = generateDateTime();
}
return accDate;
}

private static java.util.Date convertCalendarToDate(Calendar cal){

java.util.Date date = null;
date = new Date(cal.get(Calendar.YEAR)-1900, cal.get(Calendar.MONTH), cal.get(Calendar.DATE));
return date;
}

private static java.util.Date generateDateTime(){

Random generator = new Random();
java.util.Date date = null;

int month = generator.nextInt(12) + 1;
int day = generator.nextInt(30) + 1;
int year = generator.nextInt(3);
int hour = generator.nextInt(24);
int minute = generator.nextInt(60);
int second = generator.nextInt(60);

int[] yearChoices = {2006,2007,2008};

int dateYear = yearChoices[year]-1900;

while ( (month == 2) && (day > 28) ){
day = generator.nextInt(30) + 1;
}

date = new Date(dateYear, month, day, hour, minute, second);

return date;
}

private static java.util.GregorianCalendar createDate(){
Random generator = new Random();
int days = generator.nextInt(1000);

GregorianCalendar cal = new GregorianCalendar();
cal.add(Calendar.DATE, -days);
cal.getTime();

return cal;
}

private static String createPassword(){
StringBuffer result = new StringBuffer();
Random generator = new Random();
int pos = 0;

for (int i=0; i< 6; i++){
pos = generator.nextInt((alphanumeric.length()-1));
result.append( alphanumeric.charAt(pos) );
}

return result.toString();
}

private static String createEmailAddress(User u){

Random generator = new Random();
int pos = generator.nextInt(3);

String user = u.getLoginName();
String domain = domains[pos];

return user+domain;

}

private static String createName(){

Random generator = new Random();
int sizeOfNames = names.length;
int pos = generator.nextInt(sizeOfNames);
return names[pos];
}

private static Boolean createRandomBoolean(){
Random generator = new Random();
if (generator.nextInt() % 2 == 0){
return Boolean.TRUE;
} else {
return Boolean.FALSE;
}
}
}