donderdag 5 november 2009

Java + MySQL + Hibernate

Last time I explained how to set up your system so you could feed queries to a MySQL database from your Java application. Now, to make it prettier and easier, I added Hibernate.

Hibernate will let you get and put Java objects from and into the database.

Ok, download Hibernate Core here and maybe read this tutorial for extra information.

I will be using the database table I made in last example, a table named 'phonebook', with contents:

mysql> SELECT * FROM phonebook;
+----+-------+----------+
| id | name | number |
+----+-------+----------+
| 1 | Barry | 555-1234 |
| 2 | Bob | 555-5678 |
+----+-------+----------+
2 rows in set (0.00 sec)


Now, create a new Java project and add the jars of Hibernate Core and the MySQL Connector/J connector. Our project consists of 5 files, which are 1. a SessionFactoryUtil, to connect with our database, 2. a class which describes a entry of the database table, a phonebook entry, 3. a hibernate config file which lets our program connect to the database, 4. a hibernate mapping file, which maps a database row to the object made in 2. and 5. a test class to show (how) it works.

SessionFactoryUtil
This file creates a connection to the database.

package com.example.phonebook;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class SessionFactoryUtil {

private static final SessionFactory sessionFactory = buildSessionFactory();

private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure("hibernate_phonebook.cfg.xml").buildSessionFactory();
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed. " + ex);
throw new ExceptionInInitializerError(ex);
}
}



public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}


The PhoneBookEntry object

package com.example.phonebook;

public class PhoneBookEntry {
private Integer id;
private String name;
private String number;

/*public PhoneBookEntry(String name, String number) {
this.name = name;
this.number = number;
}*/

public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}

public String toString() {
return "Name: " + getName() + " Number: " + getNumber();
}
}


The hibernate config file
I called my file hibernate_phonebook.xml.cfg. I needed to name this file in the SessionFactoryUtil, because by default hibernate looks for the file hibernate.xml.cfg.
In this file, there's all the data to connect to the database.

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:mysql://192.168.1.74/test2</property>
<property name="connection.username">root</property>
<property name="connection.password">password</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>

<!-- thread is the short name for
org.hibernate.context.ThreadLocalSessionContext
and let Hibernate bind the session automatically to the thread
-->
<property name="current_session_context_class">thread</property>

<!-- this will show us all sql statements -->
<property name="hibernate.show_sql">true</property>

<!-- mapping files -->
<mapping resource="phonebook.hbm.xml" />

</session-factory>
</hibernate-configuration>


The hibernate mapping file
I called this file phonebook.hbm.xml. I maps the database rows to the object PhoneBookEntry.

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

<hibernate-mapping>
<class name="com.example.phonebook.PhoneBookEntry" table="phonebook">
<id name="id" column="id" type="java.lang.Integer">
<generator class="increment"/>
</id>
<property name="name" column="name" type="java.lang.String" />
<property name="number" column="number" type="java.lang.String" />
</class>
</hibernate-mapping>


The test
The last file is a test. What it does: create a connection, read all entries from the table in the database, add an entry and display all entries again.

package com.example.phonebook;

import java.util.Iterator;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;

public class PhoneBookTest {
public static void main(String[] args) {
listEntries();
PhoneBookEntry entry = new PhoneBookEntry();
entry.setName("Jay");
entry.setNumber("555-8912");
addEntry(entry);
listEntries();
}


private static void listEntries() {
Transaction tx = null;
Session session = SessionFactoryUtil.getSessionFactory().getCurrentSession();
try {
tx = session.beginTransaction();
List entries = session.createQuery("select e from PhoneBookEntry as e").list();
for (Iterator iter = entries.iterator(); iter.hasNext();) {
PhoneBookEntry element = (PhoneBookEntry) iter.next();
System.out.println(element.toString());
}
tx.commit();
} catch (RuntimeException e) {
if (tx != null && tx.isActive()) {
try {
// Second try catch as the rollback could fail as well
tx.rollback();
} catch (HibernateException e1) {
//logger.debug("Error rolling back transaction");
}
// throw again the first exception
throw e;
} else throw e;


}
}

private static void addEntry(PhoneBookEntry entry) {
Session session = SessionFactoryUtil.getSessionFactory().getCurrentSession();
Transaction tx = session.beginTransaction();
session.save(entry);
session.getTransaction().commit();
}
}


The result
After running, the result is

Hibernate: select phonebooke0_.id as id0_, phonebooke0_.name as name0_, phonebooke0_.number as number0_ from phonebook phonebooke0_
Name: Barry Number: 555-1234
Name: Bob Number: 555-5678
Hibernate: select max(id) from phonebook
Hibernate: insert into phonebook (name, number, id) values (?, ?, ?)
Hibernate: select phonebooke0_.id as id0_, phonebooke0_.name as name0_, phonebooke0_.number as number0_ from phonebook phonebooke0_
Name: Barry Number: 555-1234
Name: Bob Number: 555-5678
Name: Jay Number: 555-8912

First, we see the SELECT query and the result, 2 entries. Then we see the INSERT query, and then another SELECT, now with 3 entries. Just as planned.

Of course, this is a very simple example. You could/should include functions to UPDATE and REMOVE entries in the database, and try to catch more Exceptions. This is just the very basics, to see if it works, and if you understand the basics.

woensdag 4 november 2009

Java and MySQL in Eclipse

Here's an example how to work with Java and MySQL in Eclipse, in my case on a Ubuntu machine. My original idea was to create an example with Java, Hibernate, Google Web Toolkit and MySQL in Eclipse, but because a lot of errors I decided to split up the project. Maybe later I'll post the intended example.

So, Eclipse, Java and MySQL, it don't sound that difficult, and actually it isn't.

Installing Eclipse
I started downloading Eclipse from http://www.eclipse.org/downloads/ I received the file eclipse-jee-galileo-SR1-linux-gtk.tar.gz
I extracted the file in ~/ so I got a directory ~/eclipse there.

MySQL
I already had MySQL installed. Information on installing MySQL is found over here http://dev.mysql.com/downloads/
I downloaded the MySQL Connector/J connector. It is the official JDBC driver for MySQL. (http://dev.mysql.com/downloads/connector/j/5.1.html)

Sun JDK
Make sure you are using the Sun java 6 JDK. As a Ubuntu user I installed the package sun-java6-jdk from the repository.

Now we have installed everything we need.

Create database
Login to MySQL as root:
$ mysql -u root -p
Create a new database:
mysql> CREATE DATABASE test2;
Open the database:
mysql> connect test2;
Create a new table:
mysql> CREATE TABLE phonebook (id INT(8) UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(32) NOT NULL, number VARCHAR(32) NOT NULL, PRIMARY KEY(id));
The table is a very simple phonebook, containing name and number and a unique id.
Now insert 2 rows for testing:
mysql> INSERT INTO phonebook (name, number) VALUES ('Barry', '555-1234');
mysql> INSERT INTO phonebook (name, number) VALUES ('Bob', '555-5678');

If you type the query SELECT * FROM phonebook; you should see something like this:
mysql> SELECT * FROM phonebook;
+----+-------+----------+
| id | name | number |
+----+-------+----------+
| 1 | Barry | 555-1234 |
| 2 | Bob | 555-5678 |
+----+-------+----------+
2 rows in set (0.00 sec)


Starting Eclipse
First of all, set up Eclipse so it uses the Sun JDK, because the default GNU JDK will produce errors. A nice howto is over here: https://help.ubuntu.com/community/EclipseIDE#Eclipse%20and%20Sun%20Java
It took me a long long time to realise the default VM was the cause of all kind of connection errors. So learn from it.

Now it's time to create a new project. File > New > Project opens a wizard. Just choose Java project and Next. I named the project mysqltest.
Include the downloaded JDBC driver for MySQL. Right-click on the project and select properties. Choose Java Build Path > Libraries > Add External JARs. Include the file mysql-connector-java-.bin.jar from the location you extracted the JDBC driver earlier on.
In the directory src, create a new class Test1. Test1.java can be the following.

package com.example.mysqltst;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


public class Test1 {
public static void main(String [] args) {
try {
// load the driver
Class.forName("com.mysql.jdbc.Driver").newInstance();

// create a connection to the db
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/test2", "root", "password");

// get some results
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM phonebook");
while (rs.next()) {
System.out.println(rs.getString(1) + " " + rs.getString(2) + " " + rs.getString(3));
}

} catch (SQLException ex) {
// handle any errors
System.out.println("SQLException: " + ex.getMessage());
System.out.println("SQLState: " + ex.getSQLState());
System.out.println("VendorError: " + ex.getErrorCode());
} catch (Exception e) {
System.out.println("Other exception: " + e.getMessage());
}
}
}


Running the code will result on the stdout:

1 Barry 555-1234
2 Bob 555-5678


A useful link to get more in depth information (old, but still good): Writing JDBC Applications with MySQL

dinsdag 3 november 2009

Online mancala game

I wanted to train my java, gwt, junit and mockito skills. The result is an online mancala (or kalaha) game. It's a game for two players. You can try and play the mancala game over here: mancala game. Enjoy.