Many to Many mapping with Hibernate when the mapping table has extra fields

When you want some additional fields in the mapping table other than the mapping primary keys from the tables to be mapped, some extra work is involved.
Two additional domain classes have to be generated as compared to a normal many to many mapping scenario.

This is an example of the example here:
http://cuppajavamattiz.com/2014/05/17/many-to-many-hibernate-mapping-using-mapping-table/

Here goes.

The SQL DDL used:

drop database pubsTill12;
create database pubsTill12;
use pubsTill12;
drop table TB_AUTHORS;
CREATE TABLE TB_AUTHORS (AUTHOR_ID int(11) NOT NULL AUTO_INCREMENT ,AUTHOR_NAME varchar(30) 
default NULL,PRIMARY KEY (AUTHOR_ID)) ;
drop table TB_BOOKS;
CREATE TABLE TB_BOOKS (ISBN_CODE int(11) NOT NULL AUTO_INCREMENT, 
BOOK_TITLE varchar(30) default NULL,PRIMARY KEY (ISBN_CODE));
drop table TB_AUTHORS_BOOKS;
CREATE TABLE TB_AUTHORS_BOOKS (AUTHOR_ID int(11), ISBN_CODE int(11),
CREATED_BY varchar(30),
CREATED_AT timestamp
);

The hbm.xml hibernate mapping files that can automate the generation of domain classes.

Author.hbm.xml

<?xml version="1.0"?>
<!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.mattiz.persistence.beans.Author" lazy="false" table="TB_AUTHORS" >
        <id name="authorId" column="AUTHOR_ID" length="11" type="int" >
        	<generator class="increment"></generator>
        </id>
        <set name="authorBookAssocs" table="TB_AUTHORS_BOOKS" lazy="false" cascade="save-update" >
			<key column="AUTHOR_ID"/>
			<one-to-many class="com.mattiz.persistence.beans.AuthorBookAssoc"/>
		</set>
        <property name="name" type="java.lang.String" column="AUTHOR_NAME" length="30" />
    </class>
</hibernate-mapping>

AuthorBookAssoc.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Aug 4, 2011 3:10:41 PM by Hibernate Tools 3.2.4.GA -->
<hibernate-mapping >
    <class name="com.mattiz.persistence.beans.AuthorBookAssoc" table="TB_AUTHORS_BOOKS">
		<composite-id name="id" class="com.mattiz.persistence.beans.AuthorBookAssocId"  >
        	<key-many-to-one name="book" class="com.mattiz.persistence.beans.Book" column="ISBN_CODE" />
        	<key-many-to-one name="author" class="com.mattiz.persistence.beans.Author" column="AUTHOR_ID"  />
    	</composite-id>
        <property name="createdBy" type="java.lang.String">
            <column name="CREATED_BY" not-null="true" />
        </property>
        <property name="createdAt" type="timestamp">
            <column name="CREATED_AT" not-null="true" />
        </property>
	</class>
</hibernate-mapping>

Book.hbm.xml

<?xml version="1.0"?>
<!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.mattiz.persistence.beans.Book" table="TB_BOOKS" lazy="false">
		<id name="isbn" column="ISBN_CODE" type="int" >
			<generator class="increment"></generator>
		</id>
		 <set name="authorBookAssocs" table="TB_AUTHORS_BOOKS" lazy="true" cascade="save-update" >
			<key column="ISBN_CODE"/>
			<one-to-many class="com.mattiz.persistence.beans.AuthorBookAssoc"/>
		</set>
		<property name="title" type="java.lang.String" column="BOOK_TITLE" length="30" />
	</class>
</hibernate-mapping>

The generated domain classes:

Author.java

package com.mattiz.persistence.beans;
// Generated 17 Oct, 2015 10:59:38 PM by Hibernate Tools 3.2.2.GA
import java.util.HashSet;
import java.util.Set;
/**
 * Author generated by hbm2java
 */
public class Author  implements java.io.Serializable {
     private int authorId;
     private Set authorBookAssocs = new HashSet(0);
     private String name;

    public Author() {
    }
    public Author(Set authorBookAssocs, String name) {
       this.authorBookAssocs = authorBookAssocs;
       this.name = name;
    }   
    public int getAuthorId() {
        return this.authorId;
    }    
    public void setAuthorId(int authorId) {
        this.authorId = authorId;
    }
    public Set getAuthorBookAssocs() {
        return this.authorBookAssocs;
    }    
    public void setAuthorBookAssocs(Set authorBookAssocs) {
        this.authorBookAssocs = authorBookAssocs;
    }
    public String getName() {
        return this.name;
    }    
    public void setName(String name) {
        this.name = name;
    }
}

Book.java

package com.mattiz.persistence.beans;
// Generated 17 Oct, 2015 10:59:38 PM by Hibernate Tools 3.2.2.GA


import java.util.HashSet;
import java.util.Set;
/**
 * Book generated by hbm2java
 */
public class Book  implements java.io.Serializable {
     private int isbn;
     private Set authorBookAssocs = new HashSet(0);
     private String title;

    public Book() {
    }
    public Book(Set authorBookAssocs, String title) {
       this.authorBookAssocs = authorBookAssocs;
       this.title = title;
    }   
    public int getIsbn() {
        return this.isbn;
    }    
    public void setIsbn(int isbn) {
        this.isbn = isbn;
    }
    public Set getAuthorBookAssocs() {
        return this.authorBookAssocs;
    }    
    public void setAuthorBookAssocs(Set authorBookAssocs) {
        this.authorBookAssocs = authorBookAssocs;
    }
    public String getTitle() {
        return this.title;
    }    
    public void setTitle(String title) {
        this.title = title;
    }
}

AuthorBookAssoc.java

package com.mattiz.persistence.beans;
// Generated 17 Oct, 2015 10:59:38 PM by Hibernate Tools 3.2.2.GA
import java.util.Date;
/**
 * AuthorBookAssoc generated by hbm2java
 */
public class AuthorBookAssoc  implements java.io.Serializable {

     private AuthorBookAssocId id;
     private String createdBy;
     private Date createdAt;

    public AuthorBookAssoc() {
    }
    public AuthorBookAssoc(AuthorBookAssocId id, String createdBy, Date createdAt) {
       this.id = id;
       this.createdBy = createdBy;
       this.createdAt = createdAt;
    }   
    public AuthorBookAssocId getId() {
        return this.id;
    }    
    public void setId(AuthorBookAssocId id) {
        this.id = id;
    }
    public String getCreatedBy() {
        return this.createdBy;
    }    
    public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }
    public Date getCreatedAt() {
        return this.createdAt;
    }    
    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }
}

AuthorBookAssocId.java

package com.mattiz.persistence.beans;
// Generated 17 Oct, 2015 10:59:38 PM by Hibernate Tools 3.2.2.GA
/**
 * AuthorBookAssocId generated by hbm2java
 */
public class AuthorBookAssocId  implements java.io.Serializable {
     private Book book;
     private Author author;
    public AuthorBookAssocId() {
    }
    public AuthorBookAssocId(Book book, Author author) {
       this.book = book;
       this.author = author;
    }   
    public Book getBook() {
        return this.book;
    }    
    public void setBook(Book book) {
        this.book = book;
    }
    public Author getAuthor() {
        return this.author;
    }    
    public void setAuthor(Author author) {
        this.author = author;
    }
}

IMattizDelegate.java

package com.mattiz.service.spring;

import java.util.Set;

import com.mattiz.persistence.beans.Author;
import com.mattiz.persistence.beans.Book;
import com.mattiz.persistence.data.IMattizDao;

public interface IMattizDelegate {
	public void insertAuthor(Author author);
	public void insertBook(Book book);
	public Set<Book> getBooksForAuthor(int authorId);
	public IMattizDao getMattizDao();
	public void setMattizDao(IMattizDao mattizDao);
}

MattizDelegate.java

package com.mattiz.service.spring;

import java.util.Set;
import com.mattiz.persistence.beans.Author;
import com.mattiz.persistence.beans.Book;
import com.mattiz.persistence.data.IMattizDao;

public class MattizDelegate implements IMattizDelegate {

	private IMattizDao mattizDao;

	public void insertAuthor(Author author) {
		getMattizDao().insertAuthor(author);
	}
	public void insertBook(Book book) {
		getMattizDao().insertBook(book);
	}
	public Set<Book> getBooksForAuthor(int authorId) {
		Set<Book> books = getMattizDao().getBooksForAuthor(authorId);
		return books;
	}
	public IMattizDao getMattizDao() {
		return mattizDao;
	}
	public void setMattizDao(IMattizDao mattizDao) {
		this.mattizDao = mattizDao;
	}
}

IMattizDao.java

package com.mattiz.persistence.data;

import java.util.Set;

import org.springframework.dao.DataAccessException;
import com.mattiz.persistence.beans.Author;
import com.mattiz.persistence.beans.Book;

public interface IMattizDao {
	public Set<Book> getBooksForAuthor(int authorId) throws DataAccessException;
	public void insertAuthor(Author author) throws DataAccessException;
	public void insertBook(Book book) throws DataAccessException;
}

MattizDao.java

package com.mattiz.persistence.data;

import java.util.HashSet;
import java.util.Set;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateTemplate;
import com.mattiz.persistence.beans.Author;
import com.mattiz.persistence.beans.AuthorBookAssoc;
import com.mattiz.persistence.beans.Book;

public class MattizDao implements IMattizDao {

	private HibernateTemplate hibernateTemplate;
	public Set<Book> getBooksForAuthor(int authorId) throws DataAccessException {
		Author authorBean = null;
		Set<Book> books = new HashSet();
		authorBean = (Author) getHibernateTemplate().load(Author.class,
				authorId);
		Set<AuthorBookAssoc> authorBookAssocs = authorBean
				.getAuthorBookAssocs();
		for (AuthorBookAssoc authorBookAssoc : authorBookAssocs) {
			Book book = authorBookAssoc.getId().getBook();
			books.add(book);
		}
		return books;
	}
	public void insertAuthor(Author author) throws DataAccessException {
		HibernateTemplate template = getHibernateTemplate();
		template.saveOrUpdate(author);
		System.out.println("Inserted/Updated AuthorBean " + author.getName());
	}
	public void insertBook(Book book) throws DataAccessException {
		HibernateTemplate template = getHibernateTemplate();
		template.saveOrUpdate(book);
		System.out.println("Inserted/Updated AuthorBean " + book.getTitle());
	}
	public HibernateTemplate getHibernateTemplate() {
		return hibernateTemplate;
	}
	public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
		this.hibernateTemplate = hibernateTemplate;
	}
}

mattiz.xml – the spring configuration file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean
        id="mattiz.hibernate.dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close" >
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/pubsTill12" />
        <property name="user" value="root" />
        <property name="password" value="admin" />
    </bean>
    <bean id="mattiz.abstract.hibernate.sessionfactory" abstract="true" 
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" lazy-init="true" >
        <property name="dataSource" >
            <ref local="mattiz.hibernate.dataSource" />
        </property>
        <property name="hibernateProperties" >
            <props>
                <prop key="hibernate.dialect" >org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql" >false</prop>
            </props>
        </property>
    </bean>
    <bean id="mattiz.pageSize" class="java.lang.Integer" >
        <constructor-arg value="5000" >
        </constructor-arg>
    </bean>
    <bean id="mattiz.hibernate.sessionfactory" parent="mattiz.abstract.hibernate.sessionfactory" >
        <property name="mappingResources" >
            <list>
                <value>resources/Author.hbm.xml</value>
                <value>resources/Book.hbm.xml</value>
                <value>resources/AuthorBookAssoc.hbm.xml</value>
            </list>
        </property>
    </bean>
    <bean id="mattiz.transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" >
        <property name="sessionFactory" >
            <ref bean="mattiz.hibernate.sessionfactory" />
        </property>
    </bean>
    <bean id="mattiz.hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate" >
        <property name="sessionFactory" >
            <ref bean="mattiz.hibernate.sessionfactory" />
        </property>
    </bean>
    <bean id="mattiz.dao.mattizDAO" class="com.mattiz.persistence.data.MattizDao" singleton="true" >
        <property name="hibernateTemplate" >
            <ref bean="mattiz.hibernateTemplate" />
        </property>
    </bean>
    <bean id="mattiz.client" class="com.mattiz.web.managedbeans.MattizMain" singleton="true" >
        <property name="mattizDelegate" ref="mattiz.service.delegate" />
    </bean>
    <bean id="mattiz.service.delegate" class="com.mattiz.service.spring.MattizDelegate" singleton="true" >
        <property name="mattizDao" ref="mattiz.dao.mattizDAO" />
    </bean>
</beans>

MattizMain.java – the standalone java class that tests the use case.

package com.mattiz.web.managedbeans;

import java.util.HashSet;
import java.util.Set;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mattiz.persistence.beans.Author;
import com.mattiz.persistence.beans.AuthorBookAssoc;
import com.mattiz.persistence.beans.AuthorBookAssocId;
import com.mattiz.persistence.beans.Book;
import com.mattiz.service.spring.IMattizDelegate;

public class MattizMain {

	private ApplicationContext applicationContext;
	private IMattizDelegate mattizDelegate;

	public IMattizDelegate getMattizDelegate() {
		mattizDelegate = (IMattizDelegate) applicationContext
				.getBean("mattiz.service.delegate");
		return mattizDelegate;
	}
	public void setMattizDelegate(IMattizDelegate mattizDelegate) {
		this.mattizDelegate = mattizDelegate;
	}
	public ApplicationContext getApplicationContext() {
		return applicationContext;
	}
	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}
	public static void main(String[] args) {
		MattizMain mattizMain = new MattizMain();
		mattizMain.applicationContext = new ClassPathXmlApplicationContext(
				"resources/mattiz.xml");

		Book a = new Book();
		a.setTitle("The Cool Drink");
		mattizMain.getMattizDelegate().insertBook(a);
		Book b = new Book();
		b.setTitle("Lost Island");
		mattizMain.getMattizDelegate().insertBook(b);
		Book j = new Book();
		j.setTitle("Long live His Cotton Socks");
		mattizMain.getMattizDelegate().insertBook(j);
		Book k = new Book();
		k.setTitle("Praise Be to Harman!");
		mattizMain.getMattizDelegate().insertBook(k);

		Author a1 = new Author();

		Set<AuthorBookAssoc> aa = new HashSet<AuthorBookAssoc>();
		AuthorBookAssoc authorBookAssoc1 = new AuthorBookAssoc();
		authorBookAssoc1.setCreatedBy("Jill");
		authorBookAssoc1.setCreatedAt(new java.util.Date());
		AuthorBookAssocId authorBookAssocId = new AuthorBookAssocId();
		authorBookAssocId.setBook(a);
		authorBookAssocId.setAuthor(a1);
		authorBookAssoc1.setId(authorBookAssocId);
		aa.add(authorBookAssoc1);
		AuthorBookAssoc authorBookAssoc2 = new AuthorBookAssoc();
		authorBookAssoc2 = new AuthorBookAssoc();
		authorBookAssoc2.setCreatedBy("Jack");
		authorBookAssoc2.setCreatedAt(new java.util.Date());
		authorBookAssocId = new AuthorBookAssocId();
		authorBookAssocId.setBook(b);
		authorBookAssocId.setAuthor(a1);
		authorBookAssoc2.setId(authorBookAssocId);
		aa.add(authorBookAssoc2);

		a1.setAuthorBookAssocs(aa);
		a1.setName("Mac");

		mattizMain.getMattizDelegate().insertAuthor(a1);

		Author b1 = new Author();

		Set<AuthorBookAssoc> ab = new HashSet<AuthorBookAssoc>();
		AuthorBookAssoc authorBookAssoc3 = new AuthorBookAssoc();
		authorBookAssoc3 = new AuthorBookAssoc();
		authorBookAssoc3.setCreatedBy("Mattiz");
		authorBookAssoc3.setCreatedAt(new java.util.Date());
		authorBookAssocId = new AuthorBookAssocId();
		authorBookAssocId.setBook(j);
		authorBookAssocId.setAuthor(b1);
		authorBookAssoc3.setId(authorBookAssocId);
		ab.add(authorBookAssoc3);
		AuthorBookAssoc authorBookAssoc4 = new AuthorBookAssoc();
		authorBookAssoc4 = new AuthorBookAssoc();
		authorBookAssoc4.setCreatedBy("Jim");
		authorBookAssoc4.setCreatedAt(new java.util.Date());
		authorBookAssocId = new AuthorBookAssocId();
		authorBookAssocId.setBook(k);
		authorBookAssocId.setAuthor(b1);
		authorBookAssoc4.setId(authorBookAssocId);
		ab.add(authorBookAssoc4);
		AuthorBookAssoc authorBookAssoc5 = new AuthorBookAssoc();
		authorBookAssoc5 = new AuthorBookAssoc();
		authorBookAssoc5.setCreatedBy("James");
		authorBookAssoc5.setCreatedAt(new java.util.Date());
		authorBookAssocId = new AuthorBookAssocId();
		authorBookAssoc5.setId(authorBookAssocId);
		authorBookAssocId.setBook(a);
		authorBookAssocId.setAuthor(b1);
		ab.add(authorBookAssoc5);

		b1.setAuthorBookAssocs(ab);
		b1.setName("Kenzie");

		mattizMain.getMattizDelegate().insertAuthor(b1);

		System.out.println("----------------------Author with Id 1");
		Set<Book> books1 = mattizMain.getMattizDelegate().getBooksForAuthor(1);
		for (Book iter1 : books1) {
			System.out.println(iter1.getTitle());
		}
		System.out.println("----------------------Author with Id 2");
		Set<Book> books2 = mattizMain.getMattizDelegate().getBooksForAuthor(2);
		for (Book iter1 : books2) {
			System.out.println(iter1.getTitle());
		}
	}
}

The generated output:

Inserted/Updated AuthorBean The Cool Drink
Inserted/Updated AuthorBean Lost Island
Inserted/Updated AuthorBean Long live His Cotton Socks
Inserted/Updated AuthorBean Praise Be to Harman!
Inserted/Updated AuthorBean Mac
Inserted/Updated AuthorBean Kenzie
----------------------Author with Id 1
Lost Island
The Cool Drink
----------------------Author with Id 2
Long live His Cotton Socks
The Cool Drink
Praise Be to Harman!

The database data:
db
The project explorer view:
project
Jars on the classpath:
jar_classpath

About cuppajavamattiz
Matty Jacob - Avid technical blogger with interests in J2EE, Web Application Servers, Web frameworks, Open source libraries, Relational Databases, Web Services, Source control repositories, ETL, IDE Tools and related technologies.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: