Read Only / Write Only Operations using Spring Data Repository Services

As we’ve learned about Spring Data Repository in our previous tutorials, it’s an abstraction layer that built in top of persistent store that leverage the ability of accessing that store for achieving CRUD operations.

Spring Data Repository had integrated seamlessly with all of the aspects that might be concerned by a developer, one of those aspect is the security. Security is the one of the main reason for why specific technology gets more importance, while others didn’t. Repository provides an open gate for accessing the persistent store with full capacity for any one has the ability of gaining its reference; either those developers have developed sub classes for it or any one has the ability of consuming that repository through whatever technique they want even using the RESTful principle.

also read:

However, there could be scenarios in which you would like to expose or restrict only the reading methods (The in CRUD) or simply prevent the delete methods from being exposed in your repository interface that can be accessed by every one. Spring Data now allows you to custom base repository with the following steps:

  • Create an interface either extending Repository or annotated with @Repository Definition.
  • Add all of the methods that you want to expose it, and make sure they actually match the signatures of methods provided by the Spring Data base repository interfaces.
  • Use this interface as a base interface for the interface declarations for your entities.

To illustrate this, let’s assume we’d like to expose only the findAll() method while preventing all writable methods. End of this tutorial, you can download the source code used in this example.

1. Spring Data Read Only / Write Only

MySQL database contained two tables, Address and Employee along with a Spring Data Repository for handling the all operations that you may need.

The repositories being used in two ways, ReadOnly and WriteOnly. ReadOnly repositories will provide us the ability to read the data that located in the database, while the write will be used for writing operation.

The JSF managed bean will use a Spring service for locating both of these repositories for handling the required operations, meanwhile, those repositories are also exported as a RESTful services.

You’ll see the ReadOnly REST repository for persisting a record inside the database and at the other hand, you’ll try to retrieve all the records of the database through using  WriteOnly REST repository.

2. Defining Read/Write Base Repositories

Along with the mentioned steps for creating the required repositories, we’ve developed two repositories, one for read only and second for just write operation.

BaseReadOnlyRepository.java

package net.javabeat.springdata.repo;

import java.io.Serializable;

import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.Repository;

@NoRepositoryBean

public interface BaseReadOnlyRepository<T, ID extends Serializable> extends Repository<T,ID>{
	Iterable<T> findAll();
}

BaseWriteOnlyRepository.java

package net.javabeat.springdata.repo;

import java.io.Serializable;

import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.Repository;

@NoRepositoryBean

public interface BaseWriteOnlyRepository<T, ID extends Serializable> extends Repository<T,ID>{
	<S extends T> S save(S entity);
}

3. Read Only Repository Example

In this section, we’re going to develop an Employee and Address read only repository that extends the read only base repository defined above. We would see what’s the impact of using Read-Only repositories for achieving the operations of write.

EmployeeReadOnlyRepository.java

package net.javabeat.springdata.repo;

import net.javabeat.springdata.jpa.data.Employee;

import org.springframework.data.rest.core.annotation.RestResource;

@RestResource(rel="employeesReadOnly",path="employeeReadOnlyRepository")
public interface EmployeeReadOnlyRepository extends BaseReadOnlyRepository<Employee,Integer>{

}

AddressReadOnlyRepository.java

package net.javabeat.springdata.repo;

import net.javabeat.springdata.jpa.data.Address;

import org.springframework.data.rest.core.annotation.RestResource;

@RestResource(rel="addressesReadOnly",path="addressReadOnlyRepository")
public interface AddressReadOnlyRepository extends BaseReadOnlyRepository<Address,Integer>{

}

RegistraionService.java

package net.javabeat.springdata.beans;

import net.javabeat.springdata.repo.EmployeeReadOnlyRepository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RegistrationService {

	@Autowired
	private EmployeeReadOnlyRepository readOnlyRepository;

	public EmployeeReadOnlyRepository getReadOnlyRepository() {
		return readOnlyRepository;
	}

	public void setReadOnlyRepository(EmployeeReadOnlyRepository readOnlyRepository) {
		this.readOnlyRepository = readOnlyRepository;
	}

}

RegistraionManagedBean.java

package net.javabeat.primefaces.managedbeans;

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

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;

import net.javabeat.springdata.beans.RegistrationService;
import net.javabeat.springdata.jpa.data.Employee;

import com.google.common.collect.Lists;

@ManagedBean
@SessionScoped
public class RegistrationManagedBean {

	private Employee employee = new Employee();

	private List<Employee> employees = new ArrayList<Employee>();

	@ManagedProperty(value="#{registrationService}")
	private RegistrationService service;

	public Employee getEmployee() {
		return employee;
	}

	public void setEmployee(Employee employee) {
		this.employee = employee;
	}

	public List<Employee> getEmployees() {
		this.employees = Lists.newArrayList(this.service.getReadOnlyRepository().findAll());
		return employees;
	}

	public void setEmployees(List<Employee> employees) {
		this.employees = employees;
	}

	public RegistrationService getService() {
		return service;
	}

	public void setService(RegistrationService service) {
		this.service = service;
	}

}

index.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:p="http://primefaces.org/ui">
	<h:head>
		<script name="jquery/jquery.js" library="primefaces"></script>
	</h:head>
	<f:view>
		<h:form prependId="false">
			<h2>JavaBeat Tutorials</h2>
			<h2>Spring Data + Exposing/Hiding Repository Services</h2>
			<h:panelGrid columns="2">
				<h:outputText value="Enter Employee Name:"/>
				<p:inputText value="#{registrationManagedBean.employee.employeeName}"></p:inputText>
				<h:outputText value="Enter Employee Address Country:"/>
				<p:inputText value="#{registrationManagedBean.employee.address.addressCountry}"></p:inputText>
				<h:outputText value="Enter Employee Address City:"/>
				<p:inputText value="#{registrationManagedBean.employee.address.addressCity}"></p:inputText>
			</h:panelGrid>
			<p:separator/>
			<h:panelGrid columns="1" width="50%">
				<p:dataTable value="#{registrationManagedBean.employees}" var="employee">
					<p:column headerText="Employee's Name">
						<h:outputText value="#{employee.employeeName}"/>
					</p:column>
					<p:column headerText="Employee's Country">
						<h:outputText value="#{employee.address.addressCountry}"/>
					</p:column>
					<p:column headerText="Employee's City">
						<h:outputText value="#{employee.address.addressCity}"/>
					</p:column>
				</p:dataTable>
			</h:panelGrid>
		</h:form>
	</f:view>
</html>

By defining all of the required classes as mentioned, your project structure should look like:

Project Is not compilable

As you’ve seen, the project isn’t compilable for one reason in that the EmployeeReadOnlyRepository no longer support service like save.

Save Is not defined

At the same time, no issues was reported for findAll() neither inside the Spring service nor through using of RESTful service as you’d see. Below two provided snapshots, one for using of RegistrationService, while the other for RESTful access.

ReadOnly - Employees Fetched Successfully

ReadOnly - Employees Fetched Successfully - REST

Also, for simplicity, we’ve tried to post a record of employee using the ReadOnly repository via REST-SHELL and we’ve got the following error:

Employee Insertion Using ReadOnly Repository

While by using the same operation against WriteOnly repository, we’ve got success message, and the employee has been created.

Employee Insertion Using WriteOnly Repository

And by looking into records in the database, we’ve ensured that the employee has really located there.

Real Employee Insertion

You may be asking about employeeId, which is vary from the one used in the post; the answer is the MySQL database has ignored the employeeId that passed and it’s used its sequence. Look below for another request without providing employeeId.

Create Employee Without Providing EmployeeId Real Employee Insertion - Without Using EmployeeId

By using Spring or RESTful ways, the employees are fetched successfully from the MySQL. In the first snapshot, a primefaces view has been used for displaying the employees and their respective addresses and in the second the all employees and their respective addresses got displayed using the RESTful invocation.

4. Write Only Repository Example

In this section, we’re going to develop an Employee and Address write only repository that extends the write only base repository defined above. We would see what’s the impact of using Write-Only repositories for achieving the operations of read using the RESTful service.

EmployeeWriteOnlyRepository.java

package net.javabeat.springdata.repo;

import net.javabeat.springdata.jpa.data.Employee;

import org.springframework.data.rest.core.annotation.RestResource;

@RestResource(rel="employeesWriteOnly",path="employeeWriteOnlyRepository")
public interface EmployeeWriteOnlyRepository extends BaseWriteOnlyRepository<Employee,Integer>{

}

AddressWriteOnlyRepository.java

package net.javabeat.springdata.repo;

import net.javabeat.springdata.jpa.data.Address;

import org.springframework.data.rest.core.annotation.RestResource;

@RestResource(rel="addressesWriteOnly",path="addressWriteOnlyRepository")
public interface AddressWriteOnlyRepository extends BaseWriteOnlyRepository<Address,Integer>{

}

RegistrationService.java

package net.javabeat.springdata.beans;

import net.javabeat.springdata.repo.EmployeeWriteOnlyRepository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RegistrationService {

	@Autowired
	private EmployeeWriteOnlyRepository writeOnlyRepository;

	public EmployeeWriteOnlyRepository getWriteOnlyRepository() {
		return writeOnlyRepository;
	}

	public void setWriteOnlyRepository(
			EmployeeWriteOnlyRepository writeOnlyRepository) {
		this.writeOnlyRepository = writeOnlyRepository;
	}

}

RegistrationManagedBean.java

package net.javabeat.primefaces.managedbeans;

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

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;

import net.javabeat.springdata.beans.RegistrationService;
import net.javabeat.springdata.jpa.data.Employee;

@ManagedBean
@SessionScoped
public class RegistrationManagedBean {

	private Employee employee = new Employee();

	private List<Employee> employees = new ArrayList<Employee>();

	@ManagedProperty(value="#{registrationService}")
	private RegistrationService service;

	public Employee getEmployee() {
		return employee;
	}

	public void setEmployee(Employee employee) {
		this.employee = employee;
	}

	public List<Employee> getEmployees() {
		return employees;
	}

	public void setEmployees(List<Employee> employees) {
		this.employees = employees;
	}

	public RegistrationService getService() {
		return service;
	}

	public void setService(RegistrationService service) {
		this.service = service;
	}

	public String register(){
		this.service.getWriteOnlyRepository().save(this.employee);
		return "";
	}

}

Likewise the impact of using the Read-Only repository, all of these services that are related to the Read will be eliminated and so the compiler will complaint about them.

Write Only - FindAll Causes Error

While the RESTful service won’t going far apart from the same result but using different words.

RESTful Read Employees Failed

Even if you’re using the REST-SHELL for reading an employees from WriteOnly Repository by executing the operation list which means findAll(), you will get the same result.

Read Employees By Using WriteOnly Repository - REST SHELL

5. Summary

Spring Data Repositories, have the ability of exposing/hiding the services provided by them. By making a base interface for just reading, all of those interfaces that extend it should serve as READ ONLY and if any part try to get read the data by READ ONLY repository it will get an error. The same issue when it comes for making interfaces for write.

This tutorial provides full explanation of how could you make your repositories for read only or for write. Also, it shows you the possible exceptions and errors that you may get when it comes to violate the rules of spring data repository.

Download Source Code

Comments

comments

About Amr Mohammed

Speak Your Mind

*