The crux of the relational database management system is in the idea of establishing relationship among entities. We establish this relationship in a database with the help of DDL SQL join, foreign keys, etc. With Hibernate and EJB persistence specification we can easily model association between entities. In this article we shall use this ability to model association between entities from Java code, without bothering about the SQL.
Relationship
Database relationship has a very concrete mathematical foundation in classical database literature. To make it simple, we keep aside its mathematical counterpart and try to understand the same analogically.
Entities are the key players of any system – for example in a school, teachers, students, and classrooms are entities. In much the same way, in an organization employees, managers, projects, and departments are entities. These entities obviously have some relationship among them such as:
Employees [ works for ] a department Employees [ works on ] a project
Now, if Employee is an entity then ‘John Smith’, for example, is an instance of that entity. So Employee is nothing but an abstraction of a real entity with some unique attributes. Each abstraction creates a relation/table in the database. And each table has a collection of such instances called records.
Now a collection of several such instances make a relation or table.
But while creating relationships among separate entities, we need to specify the number of times that an entity can participate in a relationship instance. This is called degree of relationship or cardinality ratios. There are three types of relationship degrees:
- One-to-One (1:1) – one employee works for one department.
- One-to-Many (1:N) or Many-to-One (N:1) depending upon from which perspective we are looking at the relationship. One employee works in many projects.
- Many-to-Many (N:M) – Many books are written by many authors.
Mapping Model Class into Database Tables
In hibernate we can map a model object into a relation/table with the help of @Entity annotation. The member variables of the model object corresponds to the table attributes. The table attributes in this case are Java primitive types, which maps into corresponding database specific primitive types. @Id defines a primary key into the database and @GeneratedValue specifies that this primary key would be auto generated by the database.
@Entity public class Employee{ @Id @GeneratedValue(strategy=GenerationType.AUTO) private long empId; private String empName; private String address; private String sex; private float salary; … }
EmpId (PK) |
EmpName |
… |
Sex |
salary |
123456789 |
John Smith |
… |
Male |
45000 |
… |
… |
… |
… |
… |
Mapping Embedded Objects
In a situation where the member variable of a model object is another object itself, then this encapsulated object is merged into the embedded model class. The @Embedded and @Embeddable annotation are used to manage this type of relationship.
@Entity public class Employee{ @Id @GeneratedValue(strategy=GenerationType.AUTO) private long empId; private String empName; @Embedded private Address address; private String sex; private float salary; … } @Embeddable public class Employee{ private String street; private String province; private String country; private String zip; … }
EmpId (PK) |
EmpName |
… |
street |
… |
zip |
… |
salary |
123456789 |
John Smith |
… |
… |
… |
… |
… |
45000 |
… |
… |
… |
… |
… |
… |
… |
… |
One-to-One Association
To establish a one-to-one association between two separate object models we use @OneToOne annotation as follows. Here in this example we assume that each employee works in one department, so the cardinality ratio of the relationship is 1:1.
@Entity public class Employee{ @Id @GeneratedValue(strategy=GenerationType.AUTO) private long empId; … @OneToOne private Department department; } @Entity public class Department{ @Id @GeneratedValue private long empId; … }
One-to-Many or Many-to-One Association
One-to-Many or Many-to-One association are basically same, seen from alternate perspectives of owning and subordinate entities. The annotation used for this type of relationship is @OneToMany. In the example below we assume that each employee can work in more than one project, so the degree of relationship is 1:N.
@Entity public class Employee{ @Id @GeneratedValue(strategy=GenerationType.AUTO) private long empId; … @OneToMany private Collection<Project> projects=new ArrayList<>(); } @Entity public class Project{ @Id @GeneratedValue(strategy=GenerationType.AUTO) private long projectId; … }
In this case what Hibernate does is that it creates a separate table with a primary key denoted by @Id in model class and uses it to map the attributes in the database table.
Employee
EmpId (PK) |
EmpName |
… |
salary |
123456789 |
John Smith |
… |
45000 |
… |
… |
… |
… |
Project
projectId (PK) |
… |
startDate |
123456789 |
… |
2/2/2012 |
… |
… |
… |
New Table: Employee_Project
EmpId |
projectId |
… |
… |
Further if we want, we can also make a reverse relationship from Project to Employee by making the following changes in the Project class.
@Entity public class Project{ @Id @GeneratedValue(strategy=GenerationType.AUTO) private long projectId; … @ManyToOne private Employee employee; }
Many-to-Many Association
When more than one entity instance is associated with multiple instances of a separate entity, The relation degree becomes N:M. This type of relationship is established in Hibernate with the help of @ManyToMany annotation. For example, the relationship between Book and Author; more than one author may write one or more books; alternatively, many books are written by one or more author.
@Entity public class Book{ @Id @GeneratedValue(strategy=GenerationType.AUTO) private long isbn; … @ManyToMany(mappedBy=”books”) private Collection<Author> authors=new ArrayList<>(); } @Entity public class Author{ @Id @GeneratedValue(strategy=GenerationType.AUTO) private long authorId; … @ManyToMany private Collection<Book> books=new ArrayList<>(); }
A Sample Application
The Java code below demonstrates all three types of relationships.
Book.java
//...import statements @Entity public class Book implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String isbn; private String title; @OneToOne private Publisher publisher; @OneToMany private Collection<Category> categories=new ArrayList<>(); @ManyToMany private Collection<Author> authors=new ArrayList<>(); //...getters and setters }
Publisher.java
//...import statements @Entity public class Publisher implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String publisherName; @Embedded private Address publisheAddress; //... getters and setters }
Address.java
//...import statements @Embeddable public class Address implements Serializable { private String street; private String province; private String zip; private String email; private String phone; //... getters and setters }
Authors.java
//...import statements @Entity public class Author implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String authorName; private String bio; @ManyToMany(mappedBy = "authors") private Collection<Book> books = new ArrayList<>(); //... getters and setters }
Category.java
//...import statements @Entity public class Category implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String categoryName; private String description; //... getters and setters }
HibernateUtil.java
//...import statements public class HibernateUtil { private static final SessionFactory sessionFactory; private static final ServiceRegistry serviceRegistry; static { try { Configuration config = getConfiguration(); serviceRegistry = new ServiceRegistryBuilder().applySettings( config.getProperties()).buildServiceRegistry(); config.setSessionFactoryObserver(new SessionFactoryObserver() { private static final long serialVersionUID = 1L; @Override public void sessionFactoryCreated(SessionFactory factory) { } @Override public void sessionFactoryClosed(SessionFactory factory) { ServiceRegistryBuilder.destroy(serviceRegistry); } }); sessionFactory = config.buildSessionFactory(serviceRegistry); } catch (Throwable ex) { System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public static Session openSession() { return sessionFactory.openSession(); } private static Configuration getConfiguration() { Configuration cfg = new Configuration(); cfg.addAnnotatedClass(Book.class ); cfg.addAnnotatedClass(Author.class ); cfg.addAnnotatedClass(Category.class ); cfg.addAnnotatedClass(Publisher.class ); cfg.setProperty("hibernate.connection.driver_class","com.mysql.jdbc.Driver"); cfg.setProperty("hibernate.connection.url","jdbc:mysql://localhost:3306/mydatabase?zeroDateTimeBehavior=convertToNull"); cfg.setProperty("hibernate.connection.username", "user1"); cfg.setProperty("hibernate.connection.password", "secret"); cfg.setProperty("hibernate.show_sql", "true"); cfg.setProperty("hibernate.dialect","org.hibernate.dialect.MySQLDialect"); cfg.setProperty("hibernate.hbm2ddl.auto", "create-drop"); cfg.setProperty("hibernate.cache.provider_class","org.hibernate.cache.NoCacheProvider"); cfg.setProperty("hibernate.current_session_context_class", "thread"); return cfg; } }
MyLibrary.java
//...import statements public class MyLibrary { public static void main(String[] args) { Book book=new Book(); book.setIsbn("81-7808-137-7"); book.setTitle("Fundamentals of Database System"); Publisher pub=new Publisher(); pub.setPublisherName("Pearson"); Address add=new Address(); add.setStreet("123 ABC Street"); add.setProvince("XYZ"); add.setPhone("9282736446"); add.setEmail("info@pearson.com"); add.setZip("1010101"); pub.setPublisheAddress(add); book.setPublisher(pub); Author a1=new Author(); a1.setAuthorName("Elmasri"); a1.setBio("bio not available"); Author a2=new Author(); a2.setAuthorName("Navathe"); a2.setBio("bio not available"); book.getAuthors().add(a1); book.getAuthors().add(a2); Category c1=new Category(); c1.setCategoryName("Database"); c1.setDescription("not Available"); Category c2=new Category(); c2.setCategoryName("Computer"); c2.setDescription("not Available"); book.getCategories().add(c1); book.getCategories().add(c2); Session session=HibernateUtil.openSession(); session.beginTransaction(); session.save(pub); session.save(a1); session.save(a2); session.save(c1); session.save(c2); session.save(book); session.getTransaction().commit(); session.close(); } }
Conclusion
Establishing relationship in a database often requires one to write complex DDL SQL statements. Obviously, we do not even get a hint of such complexity while working with Hibernate. Hibernate also allows one to create relationships using the inheritance mechanism. In a way it provides a solution where database programmers need to learn only one language – Java. This is the power of this excellent ORM tool.