13 - Collection Mapping in Hibernate

13.1 Overview

In chapter 12, we discussed the components as a value type which means the components (compositions in terms of java) has been stored as a value type in the entity table (parent table).

It is very important to understand that the components have no existence of their own and that is the reason they do not have any primary key. Their life span is dependent on the life of the containing object and cannot be referred with another entity.

If the component is single instance, we can add it as a value type in the same table, but what if the component is a collection? In this case we would need a separate table known as collection table. Again, as these are component, there will be no primary key (identifier associated with the rows).

Consider a scenario -  “Folder has a collection of files”. The file is an object (instance) and since it’s a collection so there will be a collection table say files. the file will have no life outside of the Folder.

13.2 Supported Interfaces  and Hibernate Mapping

While working with collections , always need to define reference as interface and object as implementation like below pseudo statement

private interface collection  = new implementation ();

java.util.Set- does not allows duplicate and order is not maintained.

  • is mapped to <set> element of hibernate
  • implementation should be HashSet

java.util.SortedSet-  does in memory sorting

  • is mapped to <set> element of hibernate and use sort attribute to configure comparator
  • implementation should be TreeSet

java.util.Listallows duplicates and maintains insertion order  

  • is mapped to <list> element of hibernate and use list-index attribute to preserve the index (position)
  • implementation should be ArrayList

java.util.Collectionallows duplicates and does not maintains insertion order.   

  • is mapped to <bag> or <idbag> element of hibernate
  • implementation should be ArrayList

java.util.Map is a key value pair.  

  • is mapped to <map>
  • implementation should be HashMap

java.util.SortedMap is a key value pair and performs in memory sorting.  

  • is mapped to <map> and use sort attribute to configure comparator
  • implementation should be TreeMap

13.3 Value Type Objects

As we already discussed , Component classes will not have its own identifier and should implement equals and hashcode() method in order to make the uniqueness of the objects as some of the java collections like set does not allow duplicates.

Hibernate provides a tag “<composite-element>” to configure the collections of components (objects ) which can be used in a collections tag like <set>, <map> etc.

Other approach is to use association tags like one to many, many to one etc., which we will discuss in the next chapter along with multiplicity.

Let's design our File object with three field name, size and  extension. As this is a value type there is no primary key so there is no need to define an identifier in File object.

package com.tutorial.hibernate;

public class File {
    private String name;
    private int size;
    private String extension;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getSize() {
        return size;
    }
    public void setSize(int size) {
        this.size = size;
    }
    public String getExtension() {
        return extension;
    }
    public void setExtension(String extension) {
        this.extension = extension;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((extension == null) ? 0 : extension.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + size;
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        File other = (File) obj;
        if (extension == null) {
            if (other.extension != null)
                return false;
        } else if (!extension.equals(other.extension))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (size != other.size)
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "File [name=" + name + ", size=" + size + ", extension="
                + extension + "]";
    }
}

Create hibernate.cfg.xml

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

<hibernate-configuration>

     <session-factory>

        <property name="hibernate.connection.url">
           jdbc:mysql://localhost:3306/tutorial
        </property>
        <property name="hibernate.connection.username">
            root
        </property>
        <property name="hibernate.connection.password">
            password
        </property>
        <property name="dialect">
            org.hibernate.dialect.MySQLDialect
        </property>
  
        <property name="hibernate.format_sql">true</property>        
        <property name="show_sql">true</property>

        <property name="hibernate.connection.driver_class">
              com.mysql.jdbc.Driver
        </property>

        <mapping resource="component-mapping.hbm.xml" />
     </session-factory>

</hibernate-configuration>  

13.3.1 Set Implementation

Folder class is will have a set of files so we need to design the Folder class as below. Since it is a containing class , it will have a identifier (folderId)

package com.tutorial.hibernate;

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

public class Folder {

    private int folderId;

    public int getFolderId() {
        return folderId;
    }
    public void setFolderId(int folderId) {
        this.folderId = folderId;
    }
    private String folderName;

    private Set<File> files = new HashSet<File>();

    public String getFolderName() {
        return folderName;
    }
    public void setFolderName(String folderName) {
        this.folderName = folderName;
    }
    public Set<File> getFiles() {
        return files;
    }
    public void setFiles(Set<File> files) {
        this.files = files;
    }
}

We will use <set> tag to define the Folder and File classes in component-mapping.hbm.xml  .

  1. <key column> tag will create a column with  given name in the table configured in <set> and it will be a foreign key column for the primary key of Folder table.  No need to create property in Object corresponding to this column as Hibernate does manages it internally.
  2. <composite-element> tag defines the class name and its properties.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 

<hibernate-mapping>
   <class name="com.tutorial.hibernate.Folder" table="Folder">
      <id name="folderId" type="int" column="folder_id">
         <generator class="native"/>
      </id>

      <property name="folderName" column="name" type="string"/>

      <set name="files" table="Files">
         <key column="folderIdForeignKey"></key>
        <composite-element class="com.tutorial.hibernate.File">
            <property name="name" type="string" column="file_name"/>
            <property name="size" type= "int" column="file_size"/>
            <property name="extension" type="string" column="file_ext"/>
        </composite-element>    
      </set>

    </class>
</hibernate-mapping>

Lets create Folder and File tables 

CREATE TABLE 'folder' (
  'folder_id' int(11) NOT NULL AUTO_INCREMENT,
  'name' varchar(255) DEFAULT NULL,
  PRIMARY KEY ('folder_id')
);

CREATE TABLE 'files' (
  'folderIdForeignKey' int(11) NOT NULL,
  'file_name' varchar(255) DEFAULT NULL,
  'file_size' int(11) DEFAULT NULL,
  'file_ext' varchar(255) DEFAULT NULL,
  KEY 'FK_n1d8h7ymasle0im3hk2dtxswt' ('folderIdForeignKey'),
  CONSTRAINT 'FK_n1d8h7ymasle0im3hk2dtxswt' FOREIGN KEY ('folderIdForeignKey') REFERENCES 'folder' ('folder_id')
);

Test Program

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

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


import com.tutorial.hibernate.File;
import com.tutorial.hibernate.Folder;

public class Test {

     public static void main(String args[])
      {

        Configuration cfg = new Configuration().configure();        
        SessionFactory factory = cfg.buildSessionFactory();
        Session session=  factory.openSession();        
        Transaction tx = session.beginTransaction();

        Folder folder = new Folder();        
        folder.setFolderName("folder1");        
        Set<File> files = new HashSet<File>();

        File f1= new File();
        f1.setExtension(".txt");
        f1.setName("sample");
        f1.setSize(102);

        File f2= new File();
        f2.setExtension(".xls");
        f2.setName("data");
        f2.setSize(1024);

        files.add(f1);
        files.add(f2);

        folder.setFiles(files);
        session.save(folder);

        tx.commit();
        session.close();

       factory.close();
    }
}

Run the Test Program. Following will be  the database tables state

Folder Table 

Files table 

Like us on Facebook