Diving into Hibernate

I’ve written this Hibernate tutorial to learn how various mappings work in Hibernate and maybe help out other new-comers in the process. I am going to cover 2 mapping cardinalities (1-many, many-many) for entity types and  a 1-1 mapping for a value type (actually value types are always 1-1) .

Software needed

  • MySQL
  • MySQL Workbench
  • MySQL connector
  • Hibernate 3.6 or higher (earlier versions should be ok)
  • Eclipse (any recent verion)
  • log4j

Directory and project setup

Create a new project in eclipse (whatever name you like) and add the following jar files to the project

Create a package for the model (alphastar.model). The POJOs will reside here.
Create a package for the code (alphastar.main).  The program logic goes here

In this small application, I will model a Stock-market portfolio.

The following schema will apply:

This diagram specifies that a Person may own 1 or more stock codes (ie. unique stocks) and that each unique stock code may be owned by one or more Persons (lets consider this possibility even though it may not be 100% real world). Essentially we are modelling a many-to-many scenario here.

Additionally, each stock code may have a number of stock “events” associated to it. By this I mean a buy or sell action performed on a stock. For instance, a buy for stock code ABC, buy/sell on stock code XYZ. This is modelling a one-to-many scenario. One stock may have a number of purchasing events associated to it.

Address is being modelled as a value type. A value type is always associated to it’s owning class and does not have an indentity outside of it’s owning class. In hibermate, value types are coalesced into the table of the owning class (they do not reside in a seperate table ).

Hibernate has the concept of a persistence context. The persistence context knows the state of all objects within the transaction, and can automatically generate the correct update and insert statements based on the modifications made to objects. Objects that aren’t known to the persistence manager are known as transient (ie. when you create a new object). Also objects that are modified after a persistence context is closed are known as detached. When objects are in those 2 states, Hibernate does not know about them and there will be no database updates.

Back to the application.

First of all we want to create the Hibernate configuration file

[code lang=”xml”]

com.mysql.jdbc.Driver jdbc:mysql://localhost/stocks root (pass) org.hibernate.dialect.HSQLDialect 1 org.hibernate.dialect.MySQLDialect thread org.hibernate.cache.NoCacheProvider true update true true

[/code]

This is a pretty standard config file. The POJO classes that we have annotated are included in the mapping tag.

Next we want to create a helper class that calls the Hibernate sessionFactory. This will instansiate a Hibernate session for us. Note that only one session from the factory is ever instansiated.
Create a file called HibernateUtil.java and place it in the alphatar.main package/directory.

[code lang=”java”]

package alphastar.main;

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

public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
return new Configuration().configure().buildSessionFactory();
}
catch (Throwable e) {
System.out.println(“Error:” + e);
throw new ExceptionInInitializerError(e);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}

[/code]

POJO’s

There are a number of POJOs (Plain old Java Objects) that store data from the database.

Person

[code lang=”Java”]
package alphastar.model;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.*;

@Entity
@Table (name=”Person”)
public class Person {

int personId;
private String name;
Address address;

private Set stocksHeld = new HashSet();

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = “idperson”)
public int getPersonId() {
return personId;
}
public void setPersonId(int personId) {
this.personId = personId;
}

@Column(name = “name”)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

@Embedded
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}

@ManyToMany
@JoinTable(
name=”PersonStockJoinTable”,
joinColumns = {@JoinColumn(name= “idperson”)},
inverseJoinColumns = {@JoinColumn(name = “idstock”)}
)
@org.hibernate.annotations.IndexColumn(name = “DISPLAY_POSITION”)
public Set getStocksHeld() {
return stocksHeld;
}
public void setStocksHeld(Set stocksHeld) {
this.stocksHeld = stocksHeld;
}

// ASSOCIATION SETTERS
public void addStock(Stock aStock) {
stocksHeld.add(aStock);
}
}

[/code]

As this is a many to many association, a 3rd intermediary table is needed to map foreign keys from either table. I have named this “PersonStockJoinTable”.

Address is an embedded value-type. It is like a 1-1 association, except that it is completely dependant on the owning object and has no identity outside of the owning object (ie shared references aren’t possible). Also, as the data for Address is so closely associated with the Person object, the data for both are stored together in one table.

Address
[code lang=”Java”]
package alphastar.model;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Table;

@Embeddable
@Table (name=”address”)
public class Address {

private String addressLine1;
private String addressLine2;
int postCode;
String city;

@Column(name = “addressline1”, nullable=false)
public String getAddressLine1() {
return addressLine1;
}
public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}

@Column(name = “addressline2”, nullable=true)
public String getAddressLine2() {
return addressLine2;
}
public void setAddressLine2(String addressLine2) {
this.addressLine2 = addressLine2;
}

@Column(name = “zipcode”, nullable=false)
public int getPostCode() {
return postCode;
}
public void setPostCode(int postCode) {
this.postCode = postCode;
}

@Column(name = “city”, nullable=false)
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}

}

[/code]

The Address value type, set as an embeddable object within Person

Stock
[code lang=”Java”]
package alphastar.model;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.*;

@Entity
@Table (name=”stock”)
//@org.hibernate.annotations.AccessType(“field”)
public class Stock {

public int idstock;

private Set stockEvents = new HashSet(0);

public String code;

public String name;

public void addStockEvent(StockEvent se) {
// save associations to both ends
se.setStock(this);
stockEvents.add(se);
}

private Set
persons = new HashSet();

// ASSOCIATION SETTERS

public void addPerson(Person aPerson) {
persons.add(aPerson);
}

// GETTERS

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = “idstock”)
public int getIdstock() {
return idstock;
}

// when a parent is saved, so is its children
@OneToMany( cascade={CascadeType.PERSIST, CascadeType.MERGE}, mappedBy=”stock”)
@org.hibernate.annotations.Cascade({
org.hibernate.annotations.CascadeType.SAVE_UPDATE
})
public Set getStockEvents() {
return stockEvents;
}

@Column(name = “code”)
public String getCode() {
return code;
}

@Column(name = “name”)
public String getName() {
return name;
}

@ManyToMany(mappedBy=”stocksHeld”)
/*
@JoinTable(
name=”PersonStockJoinTable”,
joinColumns = {@JoinColumn(name= “idstock”)},
inverseJoinColumns = {@JoinColumn(name = “idperson”)}
)
@org.hibernate.annotations.IndexColumn(name = “DISPLAY_POSITION”)
*/
public Set
getPersons() {
return persons;
}

// SETTERS

public void setIdstock(int idstock) {
this.idstock = idstock;
}

public void setStockEvents(Set stockEvents) {
this.stockEvents = stockEvents;
}

public void setCode(String code) {
this.code = code;
}

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

public void setPersons(Set
persons) {
this.persons = persons;
}
}

[/code]

One thing to note, is that we use a mappedBy attribute to specify that this is the inverse part of the may-to-many relationship. This is due to an idiosnycracy with the way bi-directional associations work in Hibernate. If there are associations to both classes, Hibernate will try to add the same key twice in the join table. To counteract this, need to set one side as the inverse of the many-to-many by using mappedBy and specifying the variable. Basically it says these 2 things go together.

If there was no bi-directional association in the code (i.e. Person referenced Stock only), the commented out code would work fine.

StockEvent
[code lang=”Java”]
package alphastar.model;

import java.math.BigDecimal;
import javax.persistence.*;

@Entity
@Table(name=”stockevent”)
public class StockEvent {

public int idStockEvent;
public Stock stock;
public BigDecimal buyPrice;
public java.util.Date buyDate;
public BigDecimal soldPrice;
public java.util.Date soldDate;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = “idstockevent”)
public int getIdStockEvent() {
return idStockEvent;
}

public void setIdStockEvent(int idStockEvent) {
this.idStockEvent = idStockEvent;
}

@Column(name = “buyprice”)
public BigDecimal getBuyPrice() {
return buyPrice;
}

public void setBuyPrice(BigDecimal buyPrice) {
this.buyPrice = buyPrice;
}

@Column(name = “buydate”)
public java.util.Date getBuyDate() {
return buyDate;
}

public void setBuyDate(java.util.Date buyDate) {
this.buyDate = buyDate;
}

@Column(name = “soldprice”)
public BigDecimal getSoldPrice() {
return soldPrice;
}

public void setSoldPrice(BigDecimal soldPrice) {
this.soldPrice = soldPrice;
}

@Column(name = “solddate”)
public java.util.Date getSoldDate() {
return soldDate;
}

public void setSoldDate(java.util.Date soldDate) {
this.soldDate = soldDate;
}

@ManyToOne (targetEntity = alphastar.model.Stock.class)
@JoinColumn (name=”idstock”, nullable=false)
public Stock getStock() {
return stock;
}
public void setStock(Stock stk) {
stock = stk;
}
}

[/code]

Database

MySQL is the database vendor I am using for this project. Open MySQL Workbench and create a database called “stocks” and set it as the default database. Important note: We don’t need to create the database schema as Hibernate will create it automatically when we run the project! It will also choose the appropriate SQL database types based on the data types in the Java classes, which is very nice!

These tables should have been created after you ran the project

Project Layout


Project download

The zip file is here

Leave a Reply

Your email address will not be published. Required fields are marked *