Wednesday, September 28, 2011

Information about Mock and Stub

One of the common questions in interview,have you written test cases for your Java Code.If your code uses some other class,how you resolved this dependency.
Have you used Stubs or Mocks and whats the reason behind it?
Is there any difference between them?

Lets discuss this scenario and figure out when to use what.
Use Case
We have to create authentication module in our system.It checks for username and password and returns true or false depending upon whether username and password supplied is correct
or not.In our use case we have a LoginService which uses IUserDAO.There is a concrete class named UserDAO which implements IUserDAO and uses database table for doing authentication check.It will return success/failure as authentication result

So now if we have to write test case for LoginService authenticate () method.We need to provide some implementation for IUserDAO for test case to complete.We fake the IUserDAO  implementation by creating a mock or stub of it.

What is Stub?

Stub is a fake implementation of the dependent class.If we follow interface driven approach we can create a stub by implementing the interface and provide hard coded return values.
The basic idea is to implement the dependencies as concrete classes, which reveal only a small part of the overall behavior of the dependent class, which is needed by the class under test.

+ves
-There is no dependency on external jars.
-We are writing extra class having redundant implementation and hard coded return values for testing our api.
-Easy to simulate and understand

-ves
-If interface changes we have to modify the stubs.
-There could be multiple stub implementations for testing different  api's because if we use one stub class for all test case,we may break other test cases.
-If interface has many methods our stub will have lots of empty methods.

Above use case with Stub Implementation

 
package com.kunaal.stubMock.test;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

import com.kunaal.stubMock.dao.IUserDAO;
import com.kunaal.stubMock.service.LoginService;

/**
 * @author Kunaal A Trehan
 *
 */
public class LoginServiceTest {
	
	private LoginService loginService=new LoginService();
	
	/**
	 * Here we setting stub implementation for userDAO which we will use in this service
	 * So interface driven approach helps us to fake the dependency by injecting  
	 * stub[anonymous implementation of IUserDAO] of other class and let us test 
	 * our method  
	 */
	@Before
	public void stubSetup(){
		loginService.setUserDAO(new IUserDAO(){

			public boolean authenticate(String uName, String passWd) {
				if(uName!=null && uName.equals("Kunaal") && 
						passWd !=null && passWd.equals("Password"))
					return true;
				else
					return false;
			}
		});
	}
	
	
	@Test
	public void authenticate(){
		assertTrue(loginService.authenticate("Kunaal", "Password"));
		assertFalse(loginService.authenticate("Kunaal", "Kunaal"));
	}

}


What is Mock?


Mock also fake's the implementation .But in case of mock we record and verify the interaction between the two classes.Basic idea is to plug the expectation at the runtime and verify the same.

+ves

-No need to create an extra class for faking the implementation
-Relives us from having blank implementations for methods in case number of methods are huge.
-We can define the expectation at the run time
-We can verify whether the method in the dependent class has actually been invoked or not

-ves
-Need third party api for providing mocking behavior
-Its not straight forward to understand

Above use case with mock implementation

package com.kunaal.stubMock.test;

import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertTrue;

import org.junit.Before;
import org.junit.Test;

import com.kunaal.stubMock.dao.UserDAO;
import com.kunaal.stubMock.service.LoginService;

/**
 * @author Kunaal A Trehan
 *
 */
public class MockLoginServiceTest {
	
	private LoginService loginService;
	private UserDAO mockUserDAO;
	
	@Before
	public void setup(){
		loginService=new LoginService();
		mockUserDAO=createMock(UserDAO.class);
		loginService.setUserDAO(mockUserDAO);
	}
	
	@Test
	public void authenticate(){
		//push the expectations and other information
		// Set expectations on mocks.
		expect(mockUserDAO.authenticate("Kunaal","Password")).
			andReturn(true);
		// Set mocks into testing mode.
		replay(mockUserDAO);
		
		assertTrue(loginService.authenticate("Kunaal", "Password"));
		//assertFalse(loginService.authenticate("Kunaal", "Kunaal"));
		verify(mockUserDAO);
	}


}

Whats the difference between these two?
So final word whats the difference between these two.As per my understanding mock is generated at the run time and it helps to test the state as well as interaction behavior between two classes.This is not possible in stub implementation as there is no way to verify whether the particular method in the dependent class has been invoked by the test class or not.


However I can be wrong.Do let me know if my understanding is not right.

Wednesday, August 31, 2011

N+1 and fetch strategies in hibernate

What is N+1 problem in Hibernate?
Lets take a use case of Library.One Library can have any number of books.Imagine a screen where we are listing library information and book details.

By default hibernate fetches the associated objects lazily.That means associated collection is not fetched.Instead hibernate creates a proxy for the same.There will be proxy for every object of the collection.

So when hibernate fetches the library information.Hibernate will fire following queries.These are:-
i) One query to fetch the parent object which is Library in this case and initializing the associated collection with proxy for each item in the collection which is books in this case.
ii)N queries for hydrating/fetching the data for the proxy of associated item in the collection.
So for fetching the data we are firing N+1 queries.

Are there any solutions to this problem.Can we avoid it?
We can avoid it by tweaking batch size and fetch mode which is explained below.

Batch Size 
When we define batch size for the many valued collection.Hibernate fetches child records for the size mentioned by batch size property.

i.e If we have a data set of 5 libraries.Each having N number of books. and we provide batch size as 3.Then when do listing of all libraries.It will eagerly fetch book data for 3 libraries .so instead of 5+1 queries.It will fire 1+1(Fetch book data for 3 libraries)+1(fetch book data for remaining 2 libraries)

Sub Select strategy
When we use sub select strategy for fetch mode.Then hibernate fetches child records for all entities of the parent node.It actually fires query with IN clause while selecting child records.


i.e Taking the above example.Lets suppose we are selecting data on the basis of some condition which returns 4 libraries.If we have incorporated sub select strategy.Then hibernate will fetch book data for all the 4 libraries.

In order to understand better lets take a use case

UseCase:- Every project has set of QA people,Dev people, and works on a particular technology stack.So a project has one to many relationship with QA ,Dev team and technology stack.

SQL Script:-
create table ORM_SW_Project(
	ProjectId bigint Identity(1,1) primary key,
	ProjectName varchar(50) not null,
	ProjectManager varchar(100) not null
)

create table ORM_SW_QA(
    QAId bigint Identity(1,1) primary key,
    QAName varchar(100) not null,
    QAAge int not null,
    ProjectId bigint not null
)

create table ORM_SW_Dev(
    DevId bigint identity(1,1) primary key,
    DevName varchar(100) not null,
    DevAge int not null,
    ProjectId bigint not null
)

create table ORM_SW_Tech(
    TechId bigint identity(1,1) primary key,
    TechName varchar(100) not null,
    TechVersion varchar(50) not null,
    ProjectId bigint not null
)

alter table ORM_SW_QA 
add  FOREIGN KEY (ProjectId) REFERENCES ORM_SW_Project(ProjectId);

alter table ORM_SW_Dev 
add  FOREIGN KEY (ProjectId) REFERENCES ORM_SW_Project(ProjectId);

alter table ORM_SW_Tech 
add  FOREIGN KEY (ProjectId) REFERENCES ORM_SW_Project(ProjectId);

insert into ORM_SW_Project(ProjectName,ProjectManager)values('New Website','Mr.X');
insert into ORM_SW_Project(ProjectName,ProjectManager)values('Integration Project1','Mr.Y1');
insert into ORM_SW_Project(ProjectName,ProjectManager)values('Integration Project2','Mr.Y2');
insert into ORM_SW_Project(ProjectName,ProjectManager)values('Integration Project3','Mr.Y3');
insert into ORM_SW_Project(ProjectName,ProjectManager)values('Integration Project4','Mr.Y4');
insert into ORM_SW_Project(ProjectName,ProjectManager)values('Integration Project5','Mr.Y5');
insert into ORM_SW_Project(ProjectName,ProjectManager)values('Integration Project6','Mr.Y6');
insert into ORM_SW_Project(ProjectName,ProjectManager)values('Integration Project7','Mr.Y7');

insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Sidney',34,1);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Rama',40,1);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Shiva',28,1);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Hary',35,1);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Avril',29,1);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Nancy',30,1);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('James',34,2);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Tommy',29,2);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('James',25,2);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Michael',29,2);

insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Sidney3',34,3);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Rama3',40,3);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Shiva3',28,3);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Hary3',35,3);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Avril3',29,3);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Nancy3',30,3);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('James4',34,4);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Tommy4',29,4);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('James4',25,4);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Michael4',29,4);

insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Sidney5',34,5);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Rama5',40,5);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Shiva5',28,5);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Hary5',35,5);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Avril5',29,5);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Nancy5',30,5);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('James6',34,6);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Tommy6',29,6);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('James6',25,6);
insert into ORM_SW_QA(QAName,QAAge,ProjectId)values('Michael6',29,6);


insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Kunaal',33,1);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Pavan',30,1);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Nitish',35,1);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Arvind',30,1);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Pankaj',28,1);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Mamu',29,1);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Preet',32,1);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Mohan',38,2);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Deepak',37,2);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Kanak',29,2);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Rashmi',34,2);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Jeril',30,2);

insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Kunaal3',33,3);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Pavan3',30,3);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Nitish3',35,3);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Arvind3',30,3);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Pankaj3',28,3);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Mamu3',29,3);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Preet3',32,3);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Mohan4',38,4);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Deepak4',37,4);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Kanak4',29,4);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Rashmi4',34,4);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Jeril4',30,4);

insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Kunaal5',33,5);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Pavan5',30,5);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Nitish5',35,5);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Arvind5',30,5);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Pankaj5',28,5);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Mamu5',29,5);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Preet5',32,5);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Mohan6',38,6);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Deepak6',37,6);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Kanak6',29,6);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Rashmi6',34,6);
insert into ORM_SW_Dev(DevName,DevAge,ProjectId)values('Jeril6',30,6);

insert into ORM_SW_Tech(TechName,TechVersion,ProjectId) values('Java','1.6',1);
insert into ORM_SW_Tech(TechName,TechVersion,ProjectId) values('Spring','3.0',1);
insert into ORM_SW_Tech(TechName,TechVersion,ProjectId) values('Hibernate','3.5',1);
insert into ORM_SW_Tech(TechName,TechVersion,ProjectId) values('Restful Webservices','1.2',1);
insert into ORM_SW_Tech(TechName,TechVersion,ProjectId) values('Oracle','9.0',1);
insert into ORM_SW_Tech(TechName,TechVersion,ProjectId) values('.Net','2.0',2);
insert into ORM_SW_Tech(TechName,TechVersion,ProjectId) values('Oracle','9.0',2);
insert into ORM_SW_Tech(TechName,TechVersion,ProjectId) values('Restful Webservices','1.2',2);
insert into ORM_SW_Tech(TechName,TechVersion,ProjectId) values('Oracle','1.2',8);
insert into ORM_SW_Tech(TechName,TechVersion,ProjectId) values('Restful Webservices','1.2',8);
insert into ORM_SW_Tech(TechName,TechVersion,ProjectId) values('Java','1.2',8);

Java Model Classes

SWProject.java having one to many relationship with QA,Dev, and Technology
package com.kunaal.model.fetchMode;

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

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.FetchProfile;
import org.hibernate.annotations.FetchProfile.FetchOverride;
import org.hibernate.annotations.FetchProfiles;

import com.kunaal.model.BaseModel;

/**
 * @author Kunaal A Trehan
 * 
 */
@Entity
@Table(name="ORM_SW_Project")
public class SWProject implements BaseModel<Long>{

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="ProjectId")
	private Long projId;
	
	@Column(name="ProjectName")
	private String projName;
	
	@Column(name="ProjectManager")
	private String projMgr;
	
	@OneToMany(cascade=CascadeType.ALL,mappedBy="project")
//	@BatchSize(size=5)
	@Fetch(FetchMode.SUBSELECT)
	private Set<SWQA> qaSet=new HashSet<SWQA>();
	
	@OneToMany(cascade=CascadeType.ALL,mappedBy="project")
//	@BatchSize(size=5)
	private Set<SWDev> devSet=new HashSet<SWDev>();
	
	@OneToMany(cascade=CascadeType.ALL,mappedBy="project")
//	@BatchSize(size=5)
	private Set<SWTech> techSet=new HashSet<SWTech>();
	
	public Long getPrimaryKey() {
		return getProjId();
	}

	/**
	 * @return the projId
	 */
	public Long getProjId() {
		return projId;
	}

	/**
	 * @param projId the projId to set
	 */
	public void setProjId(Long projId) {
		this.projId = projId;
	}

	/**
	 * @return the projName
	 */
	public String getProjName() {
		return projName;
	}

	/**
	 * @param projName the projName to set
	 */
	public void setProjName(String projName) {
		this.projName = projName;
	}

	/**
	 * @return the projMgr
	 */
	public String getProjMgr() {
		return projMgr;
	}

	/**
	 * @param projMgr the projMgr to set
	 */
	public void setProjMgr(String projMgr) {
		this.projMgr = projMgr;
	}

	/**
	 * @return the qaSet
	 */
	public Set<SWQA> getQaSet() {
		return qaSet;
	}

	/**
	 * @param qaSet the qaSet to set
	 */
	public void setQaSet(Set<SWQA> qaSet) {
		this.qaSet = qaSet;
	}

	/**
	 * @return the devSet
	 */
	public Set<SWDev> getDevSet() {
		return devSet;
	}

	/**
	 * @param devSet the devSet to set
	 */
	public void setDevSet(Set<SWDev> devSet) {
		this.devSet = devSet;
	}

	/**
	 * @return the techSet
	 */
	public Set<SWTech> getTechSet() {
		return techSet;
	}

	/**
	 * @param techSet the techSet to set
	 */
	public void setTechSet(Set<SWTech> techSet) {
		this.techSet = techSet;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((projId == null) ? 0 : projId.hashCode());
		return result;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		SWProject other = (SWProject) obj;
		if (projId == null) {
			if (other.projId != null)
				return false;
		} else if (!projId.equals(other.projId))
			return false;
		return true;
	}

}


SWDev.java showing Dev details
package com.kunaal.model.fetchMode;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import com.kunaal.model.BaseModel;

/**
 * @author Kunaal A Trehan
 *
 */
@Entity
@Table(name="ORM_SW_Dev")
public class SWDev implements BaseModel<Long>{

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="DevId")
	private Long devId;
	
	@Column(name="DevName")
	private String devName;
	
	@Column(name="DevAge")
	private Integer age;
	
	@ManyToOne
	@JoinColumn(name="ProjectId")
	private SWProject project;
	
	public Long getPrimaryKey() {
		return getDevId();
	}

	/**
	 * @return the devId
	 */
	public Long getDevId() {
		return devId;
	}

	/**
	 * @param devId the devId to set
	 */
	public void setDevId(Long devId) {
		this.devId = devId;
	}

	/**
	 * @return the devName
	 */
	public String getDevName() {
		return devName;
	}

	/**
	 * @param devName the devName to set
	 */
	public void setDevName(String devName) {
		this.devName = devName;
	}

	/**
	 * @return the age
	 */
	public Integer getAge() {
		return age;
	}

	/**
	 * @param age the age to set
	 */
	public void setAge(Integer age) {
		this.age = age;
	}

	/**
	 * @return the project
	 */
	public SWProject getProject() {
		return project;
	}

	/**
	 * @param project the project to set
	 */
	public void setProject(SWProject project) {
		this.project = project;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((devId == null) ? 0 : devId.hashCode());
		return result;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		SWDev other = (SWDev) obj;
		if (devId == null) {
			if (other.devId != null)
				return false;
		} else if (!devId.equals(other.devId))
			return false;
		return true;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "SWDev [devId=" + devId + ", devName=" + devName + "]";
	}

}

SWQA.java showing QA details
package com.kunaal.model.fetchMode;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.BatchSize;

import com.kunaal.model.BaseModel;

/**
 * @author Kunaal A Trehan
 *
 */
@Entity
@Table(name="ORM_SW_QA")
//@BatchSize(size=2)
public class SWQA implements BaseModel<Long>{

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="QAId")
	private Long id;
	
	@Column(name="QAName")
	private String name;
	
	@Column(name="QAAge")
	private Integer age;
	
	@ManyToOne
	@JoinColumn(name="projectId")
	private SWProject project;
	
	public Long getPrimaryKey() {
		return getId();
	}

	/**
	 * @return the id
	 */
	public Long getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the age
	 */
	public Integer getAge() {
		return age;
	}

	/**
	 * @param age the age to set
	 */
	public void setAge(Integer age) {
		this.age = age;
	}

	/**
	 * @return the project
	 */
	public SWProject getProject() {
		return project;
	}

	/**
	 * @param project the project to set
	 */
	public void setProject(SWProject project) {
		this.project = project;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		SWQA other = (SWQA) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "SWQA [id=" + id + ", name=" + name + "]";
	}

}

SWTech.java showing technology details
package com.kunaal.model.fetchMode;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import com.kunaal.model.BaseModel;

/**
 * @author Kunaal A Trehan
 *
 */
@Entity
@Table(name="ORM_SW_Tech")
public class SWTech implements BaseModel<Long>{

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="TechId")
	private Long id;
	
	@Column(name="TechName")
	private String name;
	
	@Column(name="TechVersion")
	private String version;
	
	@ManyToOne
	@JoinColumn(name="ProjectId")
	private SWProject project;
	
	public Long getPrimaryKey() {
		return getId();
	}

	/**
	 * @return the id
	 */
	public Long getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the version
	 */
	public String getVersion() {
		return version;
	}

	/**
	 * @param version the version to set
	 */
	public void setVersion(String version) {
		this.version = version;
	}

	/**
	 * @return the project
	 */
	public SWProject getProject() {
		return project;
	}

	/**
	 * @param project the project to set
	 */
	public void setProject(SWProject project) {
		this.project = project;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		SWTech other = (SWTech) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "SWTech [id=" + id + ", name=" + name + "]";
	}

}


So we have 8 projects in the database and in SWProject.java we have placed fetch mode as SUBSELECT and there is a commented entry for BatchSize(size=5).There is a test case in which we list all the projects and print the QA details.So when we have fetch mode as SUBSELECT,there will be 2 queries fired,one for project details and creating the proxy for all the associated collections and another for  hydrating the proxy for QA collection.

However when we use comment fetch mode as SUBSELECT and use BatchSize(size=5).There will be 3 queries fired,one for project details  and creating the proxy for associated collections and another 2 queries for hydrating the proxy for QA collection.Since batch size is 5 and no of projects is 8 so 2 queries will be fired.


Test Case(ProjectServiceTest.java)
package com.kunaal.service;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.FetchMode;
import org.hibernate.annotations.FetchProfile;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

import com.kunaal.model.fetchMode.SWProject;

/**
 * @author Kunaal A Trehan
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring-main.xml"})
@TransactionConfiguration(defaultRollback=false)
public class ProjectServiceTest  implements ApplicationContextAware {

	private ApplicationContext appCtx;
	
	@Autowired	
	private IProjectService projectService;
	
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.appCtx=applicationContext;
	}

        @Test
	@Transactional
	public void listAll(){
		List<SWProject> list = projectService.listAll();
		
		for(SWProject project:list){
			System.out.println("Project is:"+project);
			System.out.println("QA is:"+project.getQaSet());
			//System.out.println("Dev is:"+project.getDevSet());
			//System.out.println("Tech is:" +project.getTechSet());
		}
	}
}

Test execution log when @BatchSize(size=5) is enabled.If we look into highlighted italic section ,hibernate has fired 3 queries as explained earlier

Aug 31, 2011 5:19:08 PM org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
INFO: @TestExecutionListeners is not present for class [class com.kunaal.service.ProjectServiceTest]: using defaults.
Aug 31, 2011 5:19:08 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-main.xml]
Aug 31, 2011 5:19:08 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-hibernate-config.xml]
Aug 31, 2011 5:19:08 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-dao.xml]
Aug 31, 2011 5:19:08 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-service.xml]
Aug 31, 2011 5:19:08 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericApplicationContext@1befab0: startup date [Wed Aug 31 17:19:08 GMT+05:30 2011]; root of context hierarchy
Aug 31, 2011 5:19:08 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1506dc4: defining beans [sessionFactory,interceptor,transactionManager,baseDAO,addrDAO,deptDAO,emplDAO,personDAO,flatEmpDAO,apptDAO,nriBankAcctDAO,enumPersonDAO,qualEnumDAO,publisherDAO,parentDAO,paymentDAO,singlePymtDAO,empVersionDAO,stockDAO,stateDAO,singerDAO,hqlDAO,criteriaDAO,mfDAO,mappedDAO,plainDeptDAO,plainUniDeptDAO,projectDAO,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,baseService,addrService,emplService,deptService,personService,flatEmplService,apptService,nriBankAcctService,enumPersonService,qualEnumService,publisherService,parentService,paymentService,singlePymtService,empVersionService,stockService,stateService,singerService,hqlService,criteriaService,mfService,mappedService,plainDeptService,plainUniDeptService,projectService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor]; root of factory hierarchy
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Aug 31, 2011 5:19:09 PM org.springframework.orm.hibernate3.LocalSessionFactoryBean buildSessionFactory
INFO: Building new Hibernate SessionFactory
Aug 31, 2011 5:19:11 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
INFO: Began transaction (1): transaction manager [org.springframework.orm.hibernate3.HibernateTransactionManager@12a416a]; rollback [false]
Hibernate: select this_.ProjectId as ProjectId39_0_, this_.ProjectManager as ProjectM2_39_0_, this_.ProjectName as ProjectN3_39_0_ from ORM_SW_Project this_
Project is:com.kunaal.model.fetchMode.SWProject@20
Hibernate: select qaset0_.projectId as projectId39_1_, qaset0_.QAId as QAId1_, qaset0_.QAId as QAId40_0_, qaset0_.QAAge as QAAge40_0_, qaset0_.QAName as QAName40_0_, qaset0_.projectId as projectId40_0_ from ORM_SW_QA qaset0_ where qaset0_.projectId in (?, ?, ?, ?, ?)
QA is:[SWQA [id=3, name=Shiva], SWQA [id=1, name=Sidney], SWQA [id=5, name=Avril], SWQA [id=2, name=Rama], SWQA [id=4, name=Hary], SWQA [id=6, name=Nancy]]
Project is:com.kunaal.model.fetchMode.SWProject@21
QA is:[SWQA [id=10, name=Michael], SWQA [id=9, name=James], SWQA [id=7, name=James], SWQA [id=8, name=Tommy]]
Project is:com.kunaal.model.fetchMode.SWProject@22
QA is:[SWQA [id=14, name=Hary3], SWQA [id=16, name=Nancy3], SWQA [id=12, name=Rama3], SWQA [id=15, name=Avril3], SWQA [id=11, name=Sidney3], SWQA [id=13, name=Shiva3]]
Project is:com.kunaal.model.fetchMode.SWProject@23
QA is:[SWQA [id=20, name=Michael4], SWQA [id=19, name=James4], SWQA [id=17, name=James4], SWQA [id=18, name=Tommy4]]
Project is:com.kunaal.model.fetchMode.SWProject@24
Hibernate: select qaset0_.projectId as projectId39_1_, qaset0_.QAId as QAId1_, qaset0_.QAId as QAId40_0_, qaset0_.QAAge as QAAge40_0_, qaset0_.QAName as QAName40_0_, qaset0_.projectId as projectId40_0_ from ORM_SW_QA qaset0_ where qaset0_.projectId in (?, ?, ?)
QA is:[SWQA [id=22, name=Rama5], SWQA [id=24, name=Hary5], SWQA [id=26, name=Nancy5], SWQA [id=21, name=Sidney5], SWQA [id=25, name=Avril5], SWQA [id=23, name=Shiva5]]
Project is:com.kunaal.model.fetchMode.SWProject@25
QA is:[SWQA [id=27, name=James6], SWQA [id=29, name=James6], SWQA [id=30, name=Michael6], SWQA [id=28, name=Tommy6]]
Project is:com.kunaal.model.fetchMode.SWProject@26
QA is:[]
Project is:com.kunaal.model.fetchMode.SWProject@27
QA is:[]
Aug 31, 2011 5:19:11 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
INFO: Committed transaction after test execution for test context [[TestContext@1df3d59 testClass = ProjectServiceTest, locations = array<String>['classpath:spring-main.xml'], testInstance = com.kunaal.service.ProjectServiceTest@134c5ff, testMethod = listAll@ProjectServiceTest, testException = [null]]]
Aug 31, 2011 5:19:11 PM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.GenericApplicationContext@1befab0: startup date [Wed Aug 31 17:19:08 GMT+05:30 2011]; root of context hierarchy
Aug 31, 2011 5:19:11 PM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1506dc4: defining beans [sessionFactory,interceptor,transactionManager,baseDAO,addrDAO,deptDAO,emplDAO,personDAO,flatEmpDAO,apptDAO,nriBankAcctDAO,enumPersonDAO,qualEnumDAO,publisherDAO,parentDAO,paymentDAO,singlePymtDAO,empVersionDAO,stockDAO,stateDAO,singerDAO,hqlDAO,criteriaDAO,mfDAO,mappedDAO,plainDeptDAO,plainUniDeptDAO,projectDAO,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,baseService,addrService,emplService,deptService,personService,flatEmplService,apptService,nriBankAcctService,enumPersonService,qualEnumService,publisherService,parentService,paymentService,singlePymtService,empVersionService,stockService,stateService,singerService,hqlService,criteriaService,mfService,mappedService,plainDeptService,plainUniDeptService,projectService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor]; root of factory hierarchy
Aug 31, 2011 5:19:11 PM org.springframework.orm.hibernate3.AbstractSessionFactoryBean destroy
INFO: Closing Hibernate SessionFactory

Test execution log when @Fetch(FetchMode.SUBSELECT) is enabled.If we look into highlighted italic section ,hibernate has fired 2 queries as explained earlier


Aug 31, 2011 5:38:05 PM org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
INFO: @TestExecutionListeners is not present for class [class com.kunaal.service.ProjectServiceTest]: using defaults.
Aug 31, 2011 5:38:05 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-main.xml]
Aug 31, 2011 5:38:05 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-hibernate-config.xml]
Aug 31, 2011 5:38:05 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-dao.xml]
Aug 31, 2011 5:38:05 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-service.xml]
Aug 31, 2011 5:38:05 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericApplicationContext@1befab0: startup date [Wed Aug 31 17:38:05 GMT+05:30 2011]; root of context hierarchy
Aug 31, 2011 5:38:06 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1506dc4: defining beans [sessionFactory,interceptor,transactionManager,baseDAO,addrDAO,deptDAO,emplDAO,personDAO,flatEmpDAO,apptDAO,nriBankAcctDAO,enumPersonDAO,qualEnumDAO,publisherDAO,parentDAO,paymentDAO,singlePymtDAO,empVersionDAO,stockDAO,stateDAO,singerDAO,hqlDAO,criteriaDAO,mfDAO,mappedDAO,plainDeptDAO,plainUniDeptDAO,projectDAO,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,baseService,addrService,emplService,deptService,personService,flatEmplService,apptService,nriBankAcctService,enumPersonService,qualEnumService,publisherService,parentService,paymentService,singlePymtService,empVersionService,stockService,stateService,singerService,hqlService,criteriaService,mfService,mappedService,plainDeptService,plainUniDeptService,projectService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor]; root of factory hierarchy
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Aug 31, 2011 5:38:06 PM org.springframework.orm.hibernate3.LocalSessionFactoryBean buildSessionFactory
INFO: Building new Hibernate SessionFactory
Aug 31, 2011 5:38:08 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
INFO: Began transaction (1): transaction manager [org.springframework.orm.hibernate3.HibernateTransactionManager@12cb585]; rollback [false]
Hibernate: select this_.ProjectId as ProjectId39_0_, this_.ProjectManager as ProjectM2_39_0_, this_.ProjectName as ProjectN3_39_0_ from ORM_SW_Project this_
Project is:com.kunaal.model.fetchMode.SWProject@20
Hibernate: select qaset0_.projectId as projectId39_1_, qaset0_.QAId as QAId1_, qaset0_.QAId as QAId40_0_, qaset0_.QAAge as QAAge40_0_, qaset0_.QAName as QAName40_0_, qaset0_.projectId as projectId40_0_ from ORM_SW_QA qaset0_ where qaset0_.projectId in (select this_.ProjectId from ORM_SW_Project this_)
QA is:[SWQA [id=3, name=Shiva], SWQA [id=1, name=Sidney], SWQA [id=5, name=Avril], SWQA [id=2, name=Rama], SWQA [id=4, name=Hary], SWQA [id=6, name=Nancy]]
Project is:com.kunaal.model.fetchMode.SWProject@21
QA is:[SWQA [id=10, name=Michael], SWQA [id=9, name=James], SWQA [id=7, name=James], SWQA [id=8, name=Tommy]]
Project is:com.kunaal.model.fetchMode.SWProject@22
QA is:[SWQA [id=14, name=Hary3], SWQA [id=16, name=Nancy3], SWQA [id=12, name=Rama3], SWQA [id=15, name=Avril3], SWQA [id=11, name=Sidney3], SWQA [id=13, name=Shiva3]]
Project is:com.kunaal.model.fetchMode.SWProject@23
QA is:[SWQA [id=20, name=Michael4], SWQA [id=19, name=James4], SWQA [id=17, name=James4], SWQA [id=18, name=Tommy4]]
Project is:com.kunaal.model.fetchMode.SWProject@24
QA is:[SWQA [id=22, name=Rama5], SWQA [id=24, name=Hary5], SWQA [id=26, name=Nancy5], SWQA [id=21, name=Sidney5], SWQA [id=25, name=Avril5], SWQA [id=23, name=Shiva5]]
Project is:com.kunaal.model.fetchMode.SWProject@25
QA is:[SWQA [id=27, name=James6], SWQA [id=29, name=James6], SWQA [id=30, name=Michael6], SWQA [id=28, name=Tommy6]]
Project is:com.kunaal.model.fetchMode.SWProject@26
QA is:[]
Project is:com.kunaal.model.fetchMode.SWProject@27
QA is:[]
Aug 31, 2011 5:38:08 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
INFO: Committed transaction after test execution for test context [[TestContext@df824a testClass = ProjectServiceTest, locations = array<String>['classpath:spring-main.xml'], testInstance = com.kunaal.service.ProjectServiceTest@1e2c9bf, testMethod = listAll@ProjectServiceTest, testException = [null]]]
Aug 31, 2011 5:38:08 PM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.GenericApplicationContext@1befab0: startup date [Wed Aug 31 17:38:05 GMT+05:30 2011]; root of context hierarchy
Aug 31, 2011 5:38:08 PM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1506dc4: defining beans [sessionFactory,interceptor,transactionManager,baseDAO,addrDAO,deptDAO,emplDAO,personDAO,flatEmpDAO,apptDAO,nriBankAcctDAO,enumPersonDAO,qualEnumDAO,publisherDAO,parentDAO,paymentDAO,singlePymtDAO,empVersionDAO,stockDAO,stateDAO,singerDAO,hqlDAO,criteriaDAO,mfDAO,mappedDAO,plainDeptDAO,plainUniDeptDAO,projectDAO,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,baseService,addrService,emplService,deptService,personService,flatEmplService,apptService,nriBankAcctService,enumPersonService,qualEnumService,publisherService,parentService,paymentService,singlePymtService,empVersionService,stockService,stateService,singerService,hqlService,criteriaService,mfService,mappedService,plainDeptService,plainUniDeptService,projectService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor]; root of factory hierarchy