Friday, April 28, 2006

Hibernate annotations: The composite primary key with foreign keys references

written by Marcel Panse

A very common database design pattern is when you have a many-to-many table which creates a 3th table to create the many to many. You don't have to create a POJO for the 3th table but simply map the many-to-many between the two tables. But what if you want an extra property in your 3th table. You have to create a POJO with a primary key over multiple columns. But the columns exists of foreign keys to the other table's primary key. Notice you have to use the latest hibernate-annotations beta10.

Here's the picture to clarify it a bit:




And here is the code:


@Entity
@Table(name = "Product")
public class Product extends AbstractRecordImpl
{
@NotNull private String name;
private String description;

//----bidirectional association
@OneToMany(mappedBy="product")
private List productItems = new ArrayList();
//----

//Some getters & setters
...
}

@Entity
@Table(name = "Item")
public class Item extends AbstractRecordImpl
{
@NotNull private String name;

//----bidirectional association
@OneToMany(mappedBy="item")
private List productItems = new ArrayList();
//----

//Some getters & setters
...
}

Easy so far, just the normal POJO's.. Now for the magic:
I've added 2 extra getters and setters for Product and Item to make it easier to get or set a product or item on your primary key. I added to extra columns to make hibernate think there is a Product mapped to this table (and an Item) but hibernate never updates or inserts this property (becuase it doensn't exists in our db.) But when hibernate does the getProduct() method it will retrieve the Product from our composite foreign key and all will work fine! ^_^


@Entity
@Table(name = "ProductItem")
public class ProductItem
{
@Id
private ProductItemPK primaryKey = new ProductItemPK();
private String description;


//bidirectional association! Needed to trick hibernate ;P
@SuppressWarnings("unused")
@Column(name="item_id", nullable=false, updatable=false, insertable=false)
private Long item;

@SuppressWarnings("unused")
@Column(name="product_id", nullable=false, updatable=false, insertable=false)
private Long product;
//----


public String getDescription() {
return description;
}

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

public void setProduct(Product product) {
primaryKey.setProduct(product);
}

public Product getProduct(){
return primaryKey.getProduct();
}

public void setItem(Item item) {
primaryKey.setItem(item);
}

public Item getItem() {
return primaryKey.getItem();
}
}

@Embeddable
private class ProductItemPK implements Serializable
{
@ManyToOne
private Item item;

@ManyToOne
private Product product;

//Some getters and setters
...
}

Thursday, April 27, 2006

How to keep your hibernate POJO's clean from annotations

written by Marcel Panse

I've been reading a lot on blogs on the internet about annotations blurring your code in your java classes. Everybody states that you should be carefull about overly misusing annotations because it will easily blur your code. But if used correctly annotations will clean up your code and make stuff much easier to read. Using annotations instead of the hibernate mapping files (hbm.xml) gives a lot of benefits.
- You will have your POJO and mapping in one file which makes it more readable.
- Don't dont have to map basic properties one on one with your POJO's.
- Can't make typo's like when creating your hbm.xml and mispelled a package.
- Compile time validation.

Now how do i keep my POJO clean?

You should design your database 'hibernate style' to make the best use of annotations for your domain objects. With hibernate style i mean the following:
- Name tables exact the same name you use for your Class names. (like: Product)
- Name columns exact the same name you use for your properties. (like: productName)
- Name foreign keys like this 'propertyname_id'. (like: productGroup_id)

I follow with a simple exaple of a many-to-one case:





And here is the code:

Product.java

@Entity
class Product {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;

@NotNull private String name;
private String description;

@ManyToOne
@NotNull private ProductGroup productGroup;

//----getters and setters----//

public Integer getId() {
return this.id;
}

public void setId(Integer id){
this.id = id;
}

public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

public String getDescription() {
return this.description;
}

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

public String getProductGroup() {
return this.productGroup;
}

public void setProductGroup(ProductGroup productGroup) {
this.productGroup = productGroup;
}
}


ProductGroup.java

@Entity
class ProductGroup {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;

@NotNull private String name;

@OneToMany(mappedBy="productGroup")
private List products;

//----getters and setters----//

public Integer getId() {
return this.id;
}

public void setId(Integer id){
this.id = id;
}

public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

public List getProducts() {
return this.products;
}

public void setProducts(List products) {
this.products = products;
}
}


Well, certainly easier then writing a hbm.xml mapping ain't it and much better to read...

This is a good example how to overdue your annotations: this blog. The thing is you don't need most of the properties. It is useless to declare a @PrimaryKey if you already have a @Id. You don't need an @Attribute if you have a @Basic, Hell you don't even need both of them because hibernate gets them by default. (btw if you need properties NOT mapped to your db then use the @Transient annotation).

In my example we even don't have to specify any package and class names because i've used a generic list (hibernate uses the generic for the class). We also don't have to specify the productGroup_id column because hibernate gets the _id by default.

Keep it clean, keep it simple

Wednesday, April 19, 2006

Spring Framework, Hibernate and EJB3 Annotations

written by Marcel Panse

One of the best new feature of Hibernate3 must be the annotation support. No more writing hbm.xml files or no more using XDoclet to make things easier. The new annotations feature in JDK5 makes it possible to define your tables and columns in your java domain objects similar to XDoclet. The big difference is that XDoclet generates hbm.xml files from your XDoclet mappings in your comments and hibernate uses the generated hbm.xml files. This way you won't have any compile time code checking or code assist. The annotation feature makes code checking and completion possible.

There isn't much to find on the internet about configuring The Spring Framework with hibernate3 using annotations. So i'm writing an example of how to do it. In the example im using JDK5.0, Spring2.0 M3 and Hibernate 3.1.

The first thing to do is to make sure you have the appropiate necessary jar files in your project:
To use Spring with Hibernate:
  • spring-2.0.jar (+some dependencies like asm.jar)
  • spring-hibernate3.jar
To Use hibernate3
  • hibernate-3.1.jar
  • cglib-2.1.3.jar (runtime dependency)
  • ehcache-1.1.jar (runtime dependency)
To use annotations
  • hibernate-annotations.jar
  • hibernate-entitymanager.jar
  • ejb3-persistence.jar

Now we'll grab a simple POJO bean of a domain class and add annotations to the POJO:
import javax.persistence.*;
@Entity
@Table(name = "Product")
public class ProductImpl implements Product
{
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;

private String description;
private Double price;
private String name;
private String creationDate;
private String comments;

//followed by the getters and setters.
}
I choose to use the 'field' style annotation instead of the property type one. Hibernate automaticly detects which style you use by just looking where you placed the @Id tag. (either at a field or at a getter-method). Pretty easy huh? Hibernate automaticly maps the properties with your database. You simple add an annotation with the settings your like (i.e. if you like the use another column name then the property is named you simple use @Column before the property) if you dont want to use the default settings of hiberate on a property that gets mapped.
What if you want to create a property in your POJO which isn't mapped to the database. Then u simple use the @Transient tag which creates a transient object not connected to the session/dadtabase. You can find the list of possible annotations in the hibernate docs: hibernate annotations docs

Next is to alter the sessionFactory in your context xml. The only difference is that i removed the hbm.xml mappings list and added a configurationClass and location for the annotations.
<!-- Session Factory Bean -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="annotatedClasses">
<list>
<value>data.domain.impl.ProductGroupImpl</value>
<value>data.domain.impl.ProductImpl</value>
</list>
</property>

<property name="hibernateProperties">
<ref bean="hibernateProperties" />
</property>
</bean>
I used a external hibernateProperties bean defined to seperate my hibernateProperties of my tests with those of my live settings.
The AnnotationSessionFactoryBean class is new for annotations and replaces the LocalSessionFactoryBean.

Here is an example how to create a bi-directional many-to-one mapping:
- Product is in 1 ProductGroup.
- A ProductGroup has multiple Products in it

ProductImpl.java:
@ManyToOne(targetEntity = ProductGroupImpl.class)
@JoinColumn(name="productGroupId")
private ProductGroup productGroup;
ProductGroupImpl.java
@OneToMany(targetEntity = ProductImpl.class, mappedBy="productGroup")
private Set<Product> products = new HashSet<Product>();
As you can see i specified the 'JoinColumn' because hibernates make uses the name of your property plus '_id' for the database foreign key. And my foreign key in my database is specified as 'productGroupId'. Furthermore i specified the targetEntity because i used interfaces in my domain classes instead of implementations.

You dont need 'targetEntity' and 'JoinColumn' when you don't use interfaces in your domain and the hibernate table-column naming for foreign keys.

One last issue; you probably have a lot of reoccuring columns in your database. Like your primary key ID and probably a hibernate Version and/or Timestamp column. You don't want to have to create these properties in all of your domain class. The solution is simply creating a baseRecord class containing these properties. You simple mark this class with '@MappedSuperclass' instead of a '@Entity'. This way hibernate won't check for any tables for this superclass. All domain classes that extend this superclass will automaticly also extend the annotations in it!

Annotations rule!!

Tuesday, April 18, 2006

Still loving the AbstractTransactionalDataSource- SpringContextTests

written by Daniel Rijkhof

If you are using junit testing and a spring context, you surely have the performance problem of loading the context every time you run a single test. Reusing the context would enormously improve the performance. The next thing you'll find irritating is modifying the persistent data. Having a transaction for every test and rolling back after every run would solve this.

So after thinking about these solutions, you obviously google if someone else already made this possible.
You'll find the AbstractTransactionalDataSourceSpringContextTests class from the org.springframework.test package. It does precisely what i was looking for.

We are using it since the moment we found it, and we still love it. You'll find the class in the spring-mock.jar.

Read more about it here

A Simple Ajax enabled Spring & DWR example

written by Marcel Panse

DWR allows Javascript in a browser to interact with Java on a server and helps you manipulate web pages with the results. DWR makes it easy for you to Ajax enable your website.

DWR stands for Direct Web Remoting.

Lets explain it a little more with a simple example:

Step 1: First you have to create the dwr servlet in your web.xml:

This servlet captures all incoming request at http://yourserver.com/dwr/ and forwards it to the dwr engine
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>logLevel</param-name>
<param-value>WARN</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
Step 2: We have to create a dwr.xml file describing what Java classes we want to expose and convert to javascript

In the following example we convert a simple hibernate POJO. The converted POJO can be used in javascript code. We also grab a manager bean from our Spring context. We can call methods from these spring wired manager beans in our javascript.

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
"http://www.getahead.ltd.uk/dwr/dwr10.dtd">

<dwr>
<allow>
<convert converter="hibernate" match="com.nefli.webdigital.domain.User">
<param name="exclude" value="groups"/>
</convert>

<convert converter="bean" match="com.nefli.webdigital.manager.*"/>

<create creator="spring" javascript="userManager">
<param name="beanName" value="userManager"/>
</create>
</allow>
</dwr>

Step 3: Create a JSP file with our dwr/ajax

Include the javascripts in the <head> section of your code. Notice that our manager class can be included as a javascript file. This javascript file will be generated by dwr when our application loads and the dwr.xml gets loaded. The engine and utils classes are needed to do anything and are also retrieved from our dwr servlet.

<script type='text/javascript' src='/dwr/interface/userManager.js'></script>
<script type='text/javascript' src='/dwr/engine.js'></script>
<script type='text/javascript' src='/dwr/util.js'></script>

Create a javascript function to handle the callback, also in our <head> section.

This function will create a table filled with information from our (hibernate) user object, which will be returned from the userManager. userTable is the id of the table to be filled. And the cellFuncts represends the columns in the table.

<script type="text/javascript">
var cellFuncs = [ function(data) { return data.username; }, function(data) { return data.firstName + ' ' + data.lastName; } ];
var userCallback = function(users) {
DWRUtil.addRows( "userTable", users, cellFuncs);
}
</script>

At last we will create the buttons and the table (in the <body> section) to complete our first ajax-enabled dwr example:

<input type="button" onclick="userManager.getAllUsers(userCallback);" value="Get all users"/>
<br />
<br />
<table>
<thead>
<tr><th width="200">Username</th><th width="300">Naam</th></tr>
</thead>
<tbody id="userTable"> </tbody>
</table>

More usefull information can be found at: