/
Part 3: Data Access and Service Layer

Part 3: Data Access and Service Layer

Now that we know what we are aiming for, let's start thinking about the storage layer of our application. As mentioned earlier, we will use ObjectDB to store our objects. We already have a document class that will hold information about our uploaded documents. So, let's create the Data Access Object to retrieve, store, modify, and delete our documents.

First, we need to add the dependencies that ObjectDB requires. Since ObjectDB is not in Maven Central but in its own repository, that means we also have to add that repository to our pom.xml. 

  1. Let's add the ObjectDB repository after our "dependencies" tag:

    pom.xml - <repository>
    <repositories>
    	<repository>
    		<id>objectdb</id>
    		<name>ObjectDB Repository</name>
    		<url>http://m2.objectdb.com</url>
    	</repository>
    </repositories>
  2. Then add the following dependencies:

    pom.xml - <dependencies>
    <!-- Object DB -->
    <dependency>
    	<groupId>com.objectdb</groupId>
    	<artifactId>objectdb</artifactId>
    	<version>2.6.3</version>
    </dependency>
    <dependency>
    	<groupId>org.eclipse.persistence</groupId>
    	<artifactId>javax.persistence</artifactId>
    	<version>2.1.0</version>
    </dependency>
    <dependency>
    	<groupId>javax.transaction</groupId>
    	<artifactId>jta</artifactId>
    	<version>1.1</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-tx</artifactId>
    	<version>${org.springframework-version}</version>
    </dependency>
    <dependency>
    	<groupId>aopalliance</groupId>
    	<artifactId>aopalliance</artifactId>
    	<version>1.0</version>
    </dependency>
    <dependency>
    	<groupId>cglib</groupId>
    	<artifactId>cglib</artifactId>
    	<version>2.2</version>
    </dependency>
    <dependency>
    	<groupId>org.aspectj</groupId>
    	<artifactId>aspectjweaver</artifactId>
    	<version>1.6.10</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-orm</artifactId>
    	<version>${org.springframework-version}</version>
    </dependency>
  3. In the last dependency, the version tag contains the following string '${org.springframework-version}'. This is a variable and helps keeping your pom.xml more readable. If you look through your dependencies, you will see that many of them have the same version. This makes sense, because we don't want to use different versions within the same framework. To make it easier to change to a different (e.g. newer) version, we can use a variable to hold the version information and use that variable in all the dependencies that should have the same version. The version number then only needs to be declared once in the beginning of the pom.xml like that:

    pom.xml - <properties>
    <properties>
    	<org.springframework-version>4.2.5.RELEASE</org.springframework-version>
    </properties>
  4. Now, replace all occurrences in your pom.xml of '4.2.5.RELEASE' with '${org.springframework-version}' (of course only for the spring framework dependency, not for dependencies that incidentally have the same version number). Your final pom.xml should look like this:

    pom.xml
    <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/maven-v4_0_0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>edu.asu.diging.tutorial.spring</groupId>
    	<artifactId>fileManager</artifactId>
    	<packaging>war</packaging>
    	<version>0.0.1-SNAPSHOT</version>
    	<name>fileManager Maven Webapp</name>
    	<url>http://maven.apache.org</url>
    	
    	<properties>
    		<org.springframework-version>4.2.5.RELEASE</org.springframework-version>
    	</properties>
    	<dependencies>
    		<!-- Spring dependencies -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-web</artifactId>
    			<version>${org.springframework-version}</version>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-webmvc</artifactId>
    			<version>${org.springframework-version}</version>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-core</artifactId>
    			<version>${org.springframework-version}</version>
    		</dependency>
    		<dependency>
    			<groupId>junit</groupId>
    			<artifactId>junit</artifactId>
    			<version>3.8.1</version>
    			<scope>test</scope>
    		</dependency>
    		<dependency>
    			<groupId>commons-logging</groupId>
    			<artifactId>commons-logging</artifactId>
    			<version>1.2</version>
    		</dependency>
    
    		<dependency>
    			<groupId>javax.servlet</groupId>
    			<artifactId>jstl</artifactId>
    			<version>1.2</version>
    		</dependency>
    		
    		<!-- Object DB -->
    		<dependency>
    			<groupId>com.objectdb</groupId>
    			<artifactId>objectdb</artifactId>
    			<version>2.6.3</version>
    		</dependency>
    		<dependency>
    			<groupId>org.eclipse.persistence</groupId>
    			<artifactId>javax.persistence</artifactId>
    			<version>2.1.0</version>
    		</dependency>
    		<dependency>
    			<groupId>javax.transaction</groupId>
    			<artifactId>jta</artifactId>
    			<version>1.1</version>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-tx</artifactId>
    			<version>${org.springframework-version}</version>
    		</dependency>
    		<dependency>
    			<groupId>aopalliance</groupId>
    			<artifactId>aopalliance</artifactId>
    			<version>1.0</version>
    		</dependency>
    		<dependency>
    			<groupId>cglib</groupId>
    			<artifactId>cglib</artifactId>
    			<version>2.2</version>
    		</dependency>
    		<dependency>
    			<groupId>org.aspectj</groupId>
    			<artifactId>aspectjweaver</artifactId>
    			<version>1.6.10</version>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-orm</artifactId>
    			<version>${org.springframework-version}</version>
    		</dependency>
    	</dependencies>
    
    	<repositories>
    		<repository>
    			<id>objectdb</id>
    			<name>ObjectDB Repository</name>
    			<url>http://m2.objectdb.com</url>
    		</repository>
    	</repositories>
    	
    	<build>
    		<finalName>fileManager</finalName>
    	</build>
    </project>


persistence.xml

ObjectDB supports using JPA (Java Persistence API). Using JPA decouples our data access object we will implement from the concrete implementation, which makes it easier later on to switch to a different storage solution. To use ObjectDB/JPA, we first need to add a file called persistence.xml in which we configure what database to use. Create a new folder in 'src/main/resources' called "META-INF". In there create a file called 'persistence.xml' and paste the following content:

src/webapp/resources/META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
 http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
 
  <persistence-unit name="FileManagerPU">
    <provider>com.objectdb.jpa.Provider</provider>
    <properties>
      <property name="javax.persistence.jdbc.url" value="/path/to/your/dbfiles/folder/fileManager.odb"/>
      <property name="javax.persistence.jdbc.user" value="admin"/>
      <property name="javax.persistence.jdbc.password" value="admin"/>
    </properties>
  </persistence-unit>
 
</persistence>

In the property 'javax.persistence.jdbc.url' replace the path to a folder in your system that should hold the db files. The last part of the path (fileManager.odb) is the file name of the file that will hold your objects.

root-context.xml

Next, add the following declaration to your root-context.xml:

root-context.xml
<!-- Add JPA support -->
<bean id="emf"
	class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
	<property name="loadTimeWeaver">
		<bean
			class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
	</property>
</bean>

<!-- Add Transaction support -->
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
	<property name="entityManagerFactory" ref="emf" />
</bean>

<!-- Use @Transaction annotations for managing transactions -->
<tx:annotation-driven transaction-manager="myTxManager" />

You will most likely will see an error message after you saved the files. This is because you also need to declare the 'tx' namespace. Adjust the first tag (beans) so that it looks like that:

root-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">


Persistent entities

To be able to store objects using JPA, we need to let the framework know what classes it should be able to store. We do that by annotating our classes with '@Entity'. Open the Document class and add the following annotations:

  • @Entity before the class definition
  • @Id before the id property

Your class should now look like this:

Document.java
package edu.asu.diging.tutorial.spring.core.impl;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.Id;

import edu.asu.diging.tutorial.spring.core.IDocument;

/**
 * This class represents one document in the system. It points to a file in the 
 * file system and contains some metadata about the file such as who uploaded it,
 * a title, and the date a file was uploaded.
 * 
 * @author jdamerow
 *
 */
@Entity
public class Document implements IDocument {

	@Id
	private String id;
	private String title;
	private String uploader;
	private int version;
	private Date date;
	private boolean current;
	private String pathToFile;	
	
	// getter and setters
	
}

DAO class


The last part of this layer is our DAO class. This class will be responsible for storing, retrieving, modifying, and deleting objects from the database. This class is the connector between our the service class that will encapsulate the business logic and the actual database (in our case represented through JPA). If we later on decide that we want to switch to a different type of storage (e.g. in the cloud), we simply modify or replace this class. Similarly, any service class that needs to retrieve documents can use our document DAO to do so without worrying about where documents are stored. 

First, we create a new subpackage of 'edu.asu.diging.tutorial.spring' called 'dao'. In there we create a new class called 'DocumentDAO'. First of all, since we want Spring to handle the life cycle of the document DAO, we'll annotate the class with @Service. Next, our DAO needs to know where to get get the object from. JPA provides an interface for that called 'EntityManager' (javax.persistence.EntityManager) that we annotate with '@PersistenceContext' (javax.persistence.PersistenceContext) to tell the framework, we need an object to be injected into this field. Your class should so far look like this:

DocumentDAO.java
package edu.asu.diging.tutorial.spring.dao;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Service;

@Service
public class DocumentDAO {

	@PersistenceContext 
	protected EntityManager manager;
}

Now, let's create our crud methods and a method to retrieve all documents. Add the following methods to your class:

CRUD methods
@Transactional
public void update(IDocument doc) {
	manager.merge(doc);
}

@Transactional
public void store(IDocument doc) {
	manager.persist(doc);
	manager.flush();
}

@Transactional
public IDocument get(String id) {
	return manager.find(Document.class, id);
}

@Transactional
public boolean delete(String id) {
	Document doc = manager.find(Document.class, id);
	manager.remove(doc);
	return true;
}
 
@Transactional
public List<IDocument> getAllDocuments() {
	TypedQuery<IDocument> query =
			  manager.createQuery("SELECT d FROM Document d", IDocument.class);
	return query.getResultList();
}

Make sure that the @Transactional annotation is the following one: org.springframework.transaction.annotation.Transactional.

Last but not least, extract an interface from your DocumentDAO called 'IDocumentDAO' and move your DocumentDAO into a new subpackage called 'impl'. Only Spring will know about the actual implementation of your DAO. All the following classes will only use IDocumentDAO. This way if at any point you want to exchange the concrete implementation of the document class, you can simply exchange the DocumentDAO class with no changes to any other class depending on it. Your package structure should now look like this:

Document Service

Now that we have our database layer implemented, let's turn our attention to the service layer. We need a manager class that does our business logic. For now this class won't do much, but you will see that we will extend it quite a bit throughout this tutorial. 

Create a new package in your base package (edu.asu.diging.tutorial.spring) called "service.impl". In it create a new class called "DocumentService". Annotate your class with @Service to let Spring know that you want the framework to manage your class.

For now, let's implement three methods in our document service: store and retrieve a document and get all stored documents. Add an autowired IDocumentDAO field to your DocumentService. Then use the injected instance of IDocumentDAO to implement a getDocuments method that returns all stored documents. Similarly implement a method to get a document by its id. Last but not least, implement a method that creates a new document, generates an id for it, sets the current date as upload date, and then stores it. Note that we will extend this method later to also store the uploaded file.

Once you're done, extract an interface and move it to 'edu.asu.diging.tutorial.spring.service'. Your class should now look like this:

DocumentService.java
package edu.asu.diging.tutorial.spring.service.impl;

import java.util.Date;
import java.util.List;
import java.util.Random;

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

import edu.asu.diging.tutorial.spring.core.IDocument;
import edu.asu.diging.tutorial.spring.dao.IDocumentDAO;

/**
 * Class providing methods to manage documents.
 * 
 * @author jdamerow
 *
 */
@Service
public class DocumentService implements IDocumentService  {

	@Autowired
	private IDocumentDAO documentDao;
	
	public List<IDocument> getDocuments() {
		return documentDao.getAllDocuments();
	}
	
	public IDocument getDocument(String id) {
		return documentDao.get(id);
	}
	
	/**
	 * Method to store a new document. This method will create a new document, assign a new id to it,
	 * and set its date to the current date/time.
	 * 
	 * @param doc Document to be stored.
	 */
	public void storeDocument(String title, String username, int version) {
		IDocument doc = new Document();
		doc.setCurrent(true);
		doc.setTitle(title);
		doc.setUploader(username);
		doc.setVersion(version);

		String id = "DOC" + generateId();
		while(true) {
			if (documentDao.get(id) == null) {
					break;
			}
			id = "DOC" + generateId();
		}
		doc.setId(id);
		doc.setDate(new Date());
	
		// still need to set path to file

		documentDao.store(doc);
	}
	
	/**
	 * This methods generates a new 6 character long id. Note that this method
	 * does not assure that the id isn't in use yet. 
	 * 
	 * @return 6 character id
	 */
	private String generateId() {
		char[] chars = 
		        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
		
		Random random = new Random();
		StringBuilder builder = new StringBuilder();
		for (int i=0; i<6; i++) {
			builder.append(chars[random.nextInt(62)]);
		}
		
		return builder.toString();
	}
}

File List Controller

Now, let's go back to the FileListController. Replace the dummy data you've created before with an actual call to the DocumentService. Note that you have to autowire the DocumentService to be able to use it in the method. Your controller method should now just be a 3-liner:

FileListController.java
@RequestMapping(value = "files")
public String listFiles(Model model) {
	List<IDocument> documents = documentService.getDocuments();
	model.addAttribute("documents", documents);
	return "files";
}

Now that the backend is set up, let's take a look at the front end again.



Related content

Tutorial #3: How to create a Java/Spring web application (part 1)
Tutorial #3: How to create a Java/Spring web application (part 1)
More like this
Part 4: Back to the frontend
Part 4: Back to the frontend
More like this
Tutorial #1 - Spring
Tutorial #1 - Spring
More like this
Part 2: The File List Controller
Part 2: The File List Controller
More like this
Java Resources Home
Java Resources Home
More like this