One of the important features in JPA 2.1 is the ability to specify fetch plans using Entity Graphs. This is useful since it allows you to customize the data that is retrieved with a query or find operation. It is expected to display data from the same entity in different and several ways when working with mid to large size applications. In other cases, you just need to select a smallest set of information to optimize the performance of your application.

Typically you do not have many mechanisms to control what is loaded or what is not loaded in a JPA Entity. You could use EAGER/LAZY fetching, but these definitions are pretty much static. You are unable to change their behaviour at runtime when retrieving data, meaning that you are stuck with what was defined in the entity. Changing these amid development is a nightmare, since it can cause queries to behave unexpectedly. Another way to control loading is to write specific JPQL queries.

When an object is retrieved from the datastore by JPA typically not all fields are retrieved immediately. This is because for efficiency purposes only particular field types are retrieved in the initial access of the object, and then any other objects are retrieved when required (lazy loading). The group of fields that are loaded is called an entity graph. There are three types of “entity graphs”

Default Entity Graph: implicitly defined in all JPA specs, specifying the fetch setting for each field/property (LAZY/EAGER).
Named Entity Graphs: a new feature in JPA 2.1 allowing the user to define Named Entity Graphs in metadata, via annotations or XML
Unnamed Entity Graphs: a new feature in JPA 2.1 allowing the user to define Entity Graphs via the JPA API at runtime

For more information on above three types of Entity Graphs you can refer to http://www.datanucleus.org/products/datanucleus/jpa/entity_graphs.html

Example

Step 1. Create below tables in MySQL database

/*Table structure for table `department` */

DROP TABLE IF EXISTS `department`;

CREATE TABLE `department` (
  `dept_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `dept_name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`dept_id`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

/*Data for the table `department` */

insert  into `department`(`dept_id`,`dept_name`) values (10,'ACCOUNTING'),(20,'RESEARCH'),(30,'SALES'),(40,'OPERATIONS');

/*Table structure for table `employee` */

DROP TABLE IF EXISTS `employee`;

CREATE TABLE `employee` (
  `emp_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `emp_first_name` varchar(30) COLLATE utf8_unicode_ci NOT NULL,
  `emp_last_name` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `emp_mgr_id` int(11) DEFAULT NULL,
  `emp_designation` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL,
  `dept_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`emp_id`),
  KEY `fk_emp_dept` (`dept_id`),
  CONSTRAINT `fk_emp_dept` FOREIGN KEY (`dept_id`) REFERENCES `department` (`dept_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7935 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

/*Data for the table `employee` */

insert  into `employee`(`emp_id`,`emp_first_name`,`emp_last_name`,`emp_mgr_id`,`emp_designation`,`dept_id`) 
values (7369,'SMITH','JHON',7782,'CLERK',20),
(7499,'ALLEN','BORDER',7698,'SALESMAN',30),
(7521,'WARD','SPACE',7698,'SALESMAN',30),
(7654,'MARTIN','FOWLER',7698,'SALESMAN',30),
(7698,'BLAKE','RAY',NULL,'MANAGER',30),
(7782,'CLARK','MICHAEL',NULL,'MANAGER',10),
(7788,'SCOTT','TIGER',7566,'ANALYST',20),
(7839,'KING','ROY',NULL,'VICE PRESIDENT',10),
(7844,'TURNER','RICK',7698,'SALESMAN',30),
(7876,'ADAMS','EVE',7788,'CLERK',20),
(7900,'JAMES','BOND',7698,'CLERK',30),
(7902,'FORD','LAMBDA',7782,'ANALYST',20),
(7934,'MILLER','JOHN',7782,'CLERK',10);

Now we will create Spring Data JPA project in Eclipse.

Step 2. Create below pom.xml file

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.jeejava</groupId>
	<artifactId>spring-data-jpa</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>spring-data-jpa</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<jdk.version>1.8</jdk.version>
		<spring.version>4.3.8.RELEASE</spring.version>
		<logback.version>1.1.3</logback.version>
		<slf4j.version>1.7.12</slf4j.version>
		<mysql.version>5.1.27</mysql.version>
	</properties>

	<dependencies>
		<!-- spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
			<exclusions>
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<!-- mysql driver -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>${mysql.version}</version>
		</dependency>

		<!-- spring data jpa -->
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-jpa</artifactId>
			<version>1.8.2.RELEASE</version>
		</dependency>

		<!-- hibernate -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>4.3.11.Final</version>
		</dependency>

		<!-- slf4j logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${slf4j.version}</version>
		</dependency>

		<!-- logback -->
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.6.1</version>
				<configuration>
					<source>${jdk.version}</source>
					<target>${jdk.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Step 3. Put the below logback.xml file under src/main/resources folder

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<!-- print to console -->
	<appender name="CA" class="ch.qos.logback.core.ConsoleAppender">
		<!-- Log message format -->
		<encoder>
			<pattern>%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36} -
				%msg%n
			</pattern>
		</encoder>
	</appender>

	<!-- print to log file -->
	<appender name="RFA" class="ch.qos.logback.core.FileAppender">
		<file>spring-data-jpa.log</file>
		<encoder>
			<pattern>%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36} -
				%msg%n
			</pattern>
		</encoder>
	</appender>

	<root>
		<level value="INFO" />
		<appender-ref ref="CA" />
		<appender-ref ref="RFA" />
	</root>
</configuration>

Step 4. Create below applicatio.properties file under src/main/resources folder

jdbc.url=jdbc:mysql://localhost/roytuts
jdbc.username=root
jdbc.password=root
jdbc.driverClassName=com.mysql.jdbc.Driver

Step 5. Create below model classes

package com.jeejava.model;

import java.io.Serializable;

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;

/**
 *
 * @author jeejava.com
 */
@Entity
@Table(name = "employee")
public class Employee implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "emp_id")
	private Integer empId;

	@Column(name = "emp_first_name")
	private String empFirstName;

	@Column(name = "emp_last_name")
	private String empLastName;

	@Column(name = "emp_mgr_id")
	private Integer empMgrId;

	@Column(name = "emp_designation")
	private String empDesignation;

	@JoinColumn(name = "dept_id", referencedColumnName = "dept_id")
	@ManyToOne(optional = false)
	private Department deptId;

	public Employee() {
	}

	public Employee(Integer empId) {
		this.empId = empId;
	}

	public Employee(Integer empId, String empFirstName, String empLastName) {
		this.empId = empId;
		this.empFirstName = empFirstName;
		this.empLastName = empLastName;
	}

	public Integer getEmpId() {
		return empId;
	}

	public void setEmpId(Integer empId) {
		this.empId = empId;
	}

	public String getEmpFirstName() {
		return empFirstName;
	}

	public void setEmpFirstName(String empFirstName) {
		this.empFirstName = empFirstName;
	}

	public String getEmpLastName() {
		return empLastName;
	}

	public void setEmpLastName(String empLastName) {
		this.empLastName = empLastName;
	}

	public Integer getEmpMgrId() {
		return empMgrId;
	}

	public void setEmpMgrId(Integer empMgrId) {
		this.empMgrId = empMgrId;
	}

	public String getEmpDesignation() {
		return empDesignation;
	}

	public void setEmpDesignation(String empDesignation) {
		this.empDesignation = empDesignation;
	}

	public Department getDeptId() {
		return deptId;
	}

	public void setDeptId(Department deptId) {
		this.deptId = deptId;
	}

	@Override
	public int hashCode() {
		int hash = 0;
		hash += (empId != null ? empId.hashCode() : 0);
		return hash;
	}

	@Override
	public boolean equals(Object object) {
		if (!(object instanceof Employee)) {
			return false;
		}
		Employee other = (Employee) object;
		if ((this.empId == null && other.empId != null) || (this.empId != null && !this.empId.equals(other.empId))) {
			return false;
		}
		return true;
	}

	@Override
	public String toString() {
		return "Employee [empId=" + empId + ", empFirstName=" + empFirstName + ", empLastName=" + empLastName
				+ ", empMgrId=" + empMgrId + ", empDesignation=" + empDesignation + ", deptId=" + deptId + "]";
	}

}
package com.jeejava.model;

import java.io.Serializable;
import java.util.Collection;

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.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.OneToMany;
import javax.persistence.Table;

/**
 *
 * @author jeejava.com
 */
@Entity
@Table(name = "department")
@NamedEntityGraph(name = "department.employees", attributeNodes = { @NamedAttributeNode("employeeCollection") })
public class Department implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "dept_id")
	private Integer deptId;

	@Column(name = "dept_name")
	private String deptName;

	@OneToMany(cascade = CascadeType.ALL, mappedBy = "deptId")
	private Collection<Employee> employeeCollection;

	public Department() {
	}

	public Department(Integer deptId) {
		this.deptId = deptId;
	}

	public Department(Integer deptId, String deptName) {
		this.deptId = deptId;
		this.deptName = deptName;
	}

	public Integer getDeptId() {
		return deptId;
	}

	public void setDeptId(Integer deptId) {
		this.deptId = deptId;
	}

	public String getDeptName() {
		return deptName;
	}

	public void setDeptName(String deptName) {
		this.deptName = deptName;
	}

	public Collection<Employee> getEmployeeCollection() {
		return employeeCollection;
	}

	public void setEmployeeCollection(Collection<Employee> employeeCollection) {
		this.employeeCollection = employeeCollection;
	}

	@Override
	public int hashCode() {
		int hash = 0;
		hash += (deptId != null ? deptId.hashCode() : 0);
		return hash;
	}

	@Override
	public boolean equals(Object object) {
		if (!(object instanceof Department)) {
			return false;
		}
		Department other = (Department) object;
		if ((this.deptId == null && other.deptId != null)
				|| (this.deptId != null && !this.deptId.equals(other.deptId))) {
			return false;
		}
		return true;
	}

	@Override
	public String toString() {
		return "Department [deptId=" + deptId + ", deptName=" + deptName + "]";
	}

}

Here in the above Department model class, we have NamedEntityGraph to include employeeCollection field loaded when Department object is loaded.

Step 5. Create below JPA Repositories in order to perform database activities

package com.jeejava.jparepo;

import org.springframework.data.jpa.repository.JpaRepository;

import com.jeejava.model.Employee;

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {

}
package com.jeejava.jparepo;

import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.JpaRepository;

import com.jeejava.model.Department;

public interface DepartmentRepository extends JpaRepository<Department, Integer> {

	@EntityGraph(value = "department.employees", type = EntityGraphType.LOAD)
	Department findByDeptId(int deptId);
}

Here in the above DepartmentRepository, we want to fetch the department by department id and also we want to fetch all employees for this department using Entity Graph.

Step 6. Create below configuration class with MySQL datasource, transaction manager and entity manager

package com.jeejava.config;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableJpaRepositories(basePackages = "com.jeejava.jparepo")
@PropertySource(value = { "classpath:application.properties" })
@EnableTransactionManagement
public class MySqlDbConfig {

	@Autowired
	private Environment environment;

	@Bean
	public DataSource dataSource() {
		DriverManagerDataSource ds = new DriverManagerDataSource();
		ds.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
		ds.setUrl(environment.getRequiredProperty("jdbc.url"));
		ds.setUsername(environment.getRequiredProperty("jdbc.username"));
		ds.setPassword(environment.getRequiredProperty("jdbc.password"));
		return ds;
	}

	@Bean
	public EntityManagerFactory entityManagerFactory() {
		HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
		vendorAdapter.setDatabase(Database.MYSQL);
		vendorAdapter.setGenerateDdl(true);
		LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
		factory.setJpaVendorAdapter(vendorAdapter);
		factory.setPackagesToScan("com.jeejava.model");
		factory.setDataSource(dataSource());
		factory.afterPropertiesSet();
		return factory.getObject();
	}

	@Bean
	public PlatformTransactionManager transactionManager() {
		JpaTransactionManager txManager = new JpaTransactionManager();
		txManager.setEntityManagerFactory(entityManagerFactory());
		return txManager;
	}

}

Step 7. Create below service classes

package com.jeejava.service;

public interface EmployeeService {

}
package com.jeejava.service;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.jeejava.jparepo.EmployeeRepository;

@Service
public class EmployeeServiceImpl implements EmployeeService {

	@Resource
	private EmployeeRepository employeeRepository;

}
package com.jeejava.service;

import com.jeejava.model.Department;

public interface DepartmentService {

	Department getDepartmentById(int deptId);

}
package com.jeejava.service;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.jeejava.jparepo.DepartmentRepository;
import com.jeejava.model.Department;

@Service
public class DepartmentServiceImpl implements DepartmentService {

	@Resource
	private DepartmentRepository departmentRepository;

	@Override
	public Department getDepartmentById(int deptId) {
		return departmentRepository.findByDeptId(deptId);
	}

}

Step 8. Create below application configuration class

package com.jeejava.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "com.jeejava.service")
public class AppConfig {

}

Step 9. Now create the main class which will test our application code

package com.jeejava.main;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;

import com.jeejava.config.AppConfig;
import com.jeejava.config.MySqlDbConfig;
import com.jeejava.model.Department;
import com.jeejava.service.DepartmentService;
import com.jeejava.service.EmployeeService;

@Configuration
public class Application {

	private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);

	@Autowired
	private DepartmentService departmentService;

	@SuppressWarnings("unused")
	@Autowired
	private EmployeeService employeeService;

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class, MySqlDbConfig.class,
				Application.class);

		Application app = context.getBean(Application.class);

		Department department = app.departmentService.getDepartmentById(10);

		LOGGER.info("Department name: " + department);

		department.getEmployeeCollection().stream().forEach(emp -> LOGGER.info(emp.toString()));
	}

}

Step 10. Run the above main class you will see the below output with other logs in the console as well as in the log file created at the root level

30-08-2017 07:10:15.845 [main] INFO  com.jeejava.main.Application -
				Department name: Department [deptId=10, deptName=ACCOUNTING]
30-08-2017 07:10:15.857 [main] INFO  com.jeejava.main.Application -
				Employee [empId=7782, empFirstName=CLARK, empLastName=MICHAEL, empMgrId=null, empDesignation=MANAGER, deptId=Department [deptId=10, deptName=ACCOUNTING]]
30-08-2017 07:10:15.857 [main] INFO  com.jeejava.main.Application -
				Employee [empId=7839, empFirstName=KING, empLastName=ROY, empMgrId=null, empDesignation=VICE PRESIDENT, deptId=Department [deptId=10, deptName=ACCOUNTING]]
30-08-2017 07:10:15.857 [main] INFO  com.jeejava.main.Application -
				Employee [empId=7934, empFirstName=MILLER, empLastName=JOHN, empMgrId=7782, empDesignation=CLERK, deptId=Department [deptId=10, deptName=ACCOUNTING]]

Thanks for reading.

Tags:

I am a professional Web developer, Enterprise Application developer, Software Engineer and Blogger. Connect me on Roy Tutorials | TwitterFacebook Google PlusLinkedin | Reddit

Leave a Reply

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