Configure Spring Data JPA in Jersey 2 JAX-RS App

In this blog post, I am going to share with you how to configure Spring Data JPA in a Jersey 2 Container Deployable Web App. Spring Data JPA makes it much easier and more convenient to persist and read data from the database and when I needed to tie together Spring Data JPA and Jersey 2 Web App I could not find a step by step instructions on how to do it. So in this blog post, I will try to document the steps I needed to do to integrate Spring Data JPA into Jersey 2 JAX-RS container deployable Web app and hopefully if you need to do this integration these steps will help you out and save you a lot of time.

So let’s begin.

Create Jersey 2 JAX-RS Web App with Maven

To create a new Jersey 2 container deployable web app I used the archetype and maven definition suggested on Jersey project website.

mvn archetype:generate -DarchetypeGroupId=org.glassfish.jersey.archetypes \
    -DarchetypeArtifactId=jersey-quickstart-webapp -DarchetypeVersion=2.26

POM.XML Dependencies

Open with your favorite IDE the created with the above maven definition Jersey 2 Web app and update it’s pom.xml file. Below is my pom.xml file which contains dependencies for:

  • Jersey 2
  • Spring Framework
  • Spring Data JPA
  • Hibernate
  • MySQL database
<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>ru.altair</groupId>
    <artifactId>jersey_jpa</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>jersey_jpa</name>

    <build>
        <finalName>jersey_jpa</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <inherited>true</inherited>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.glassfish.jersey</groupId>
                <artifactId>jersey-bom</artifactId>
                <version>${jersey.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
            <!-- use the following artifactId if you don't need servlet 2.x compatibility -->
            <!-- artifactId>jersey-container-servlet</artifactId -->
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.inject</groupId>
            <artifactId>jersey-hk2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-binding</artifactId>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.ext</groupId>
            <artifactId>jersey-spring4</artifactId>
            <version>2.26</version>
        </dependency>

        <!--Spring Framework Dependencies-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>${spring-data.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- Hibernate Dependencies -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <!-- Database Dependencies -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.187</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>


    </dependencies>
    <properties>
        <jersey.version>2.26</jersey.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>4.3.4.RELEASE</spring.version>
        <spring-data.version>1.10.5.RELEASE</spring-data.version>
        <hibernate.version>5.2.5.Final</hibernate.version>
    </properties>
</project>

WEB-XML File

It is very important up update the WEB-XML file to contain the below details:

  • contextConfigLocation with the location of you beans xml file,
  • Set up the ContextLoaderListener and the RequestContextListener,
  • Configure the jersey.config.server.provider.packagesspecify to specify which packages to scan,
  • And configure the URL pattern to specify the root URL path of your Web Service.
<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container,
     see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/root-context.xml</param-value>
    </context-param>

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.appsdeveloperblog.app.ws</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/webapi/*</url-pattern>
    </servlet-mapping>
</web-app>

Spring Root Context XML file

The location of this file is specified in the above WEB-XML file.  I have placed it in /WEB-XML/spring folder.

I also configure here the location of application.properties file which contains the database connection details and also other the Hibernate related properties.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:task="http://www.springframework.org/schema/task"
       xmlns:sec="http://www.springframework.org/schema/security"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
                           http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">

    <context:component-scan base-package="com.appsdeveloperblog.app.ws" />

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="ignoreUnresolvablePlaceholders" value="true"/>
        <property name="ignoreResourceNotFound" value="true"/>
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
        <property name="locations">
            <list>
                <value>classpath:application.properties</value>
                <value>file:${catalina.base}/conf/Catalina/localhost/application.properties</value>
            </list>
        </property>
    </bean>

    <import resource="classpath:db-context.xml"/>
</beans>

application.properties file

The application.properties file is also mentioned in the above beans XML file. In my project, this file is located in /src/main/resources folder. This application.properties file contains the details needed to connect to MySQL database and also the Hibernate properties to make our Jersey 2 Web app be able to use Spring Data JPA and to use hibernate and persist data into MySQL database.

#Database properties:
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/photo_app
db.username=sergey
db.password=sergey

#Hibernate Configuration:
db.hibernate.dialect=org.hibernate.dialect.MySQLDialect
db.hibernate.show_sql=true
db.entitymanager.packages.to.scan=com.appsdeveloperblog.app.ws
db.hibernate.hbm2ddl.auto=update

db-context.xml file

The location of this db-context.xml file is in /src/main/resources folder and is right next to the application.properties file.

In this file we define the:

  • dataSource bean
  • entityManagerFactory bean
  • transactionManager bean
  • we specify that our application is annotation driven
  • and we specify the base package for Spring Data JPA Repositories
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.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.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${db.driver}" />
        <property name="url" value="${db.url}" />
        <property name="username" value="${db.username}" />
        <property name="password" value="${db.password}" />
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="${db.entitymanager.packages.to.scan}"/>
        <property name="persistenceProviderClass" value="org.hibernate.jpa.HibernatePersistenceProvider"/>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">${db.hibernate.dialect}</prop>
                <prop key="hibernate.jdbc.time_zone">UTC</prop>
                <prop key="db.hibernate.show_sql">${db.hibernate.show_sql}</prop>
                <prop key="db.hibernate.hbm2ddl.auto">${db.hibernate.hbm2ddl.auto}</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <tx:annotation-driven />

    <jpa:repositories base-package="com.appsdeveloperblog.app.ws" />
    <context:annotation-config />
</beans>

And this is all we need to do for configuration. Is it not very straightforward if you are just starting with Web Services Development and this is your first time working with Spring Framework and Jersey at the same time.

Below I will provide source code to my Root Resouce class, Service class, Entity object that needs to be persisted into MySQL database and the CrudRepository interface. I will also attach the source code to this project so that you can download it. Update the database connection details and run it to see how it works.

Root Resouce Class example

I have created only two methods here. One for creating a new user and one for returning a list of all users. It is just a sample code, so do not be too picky that I am not returning a correct Response object here and I do not handle runtime exceptions.

Since my application is annotation driven please note the annotations I used here.

package com.appsdeveloperblog.app.ws.ui.entrypoints;

import com.appsdeveloperblog.app.ws.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.appsdeveloperblog.app.ws.shared.dto.UserDto;
import com.appsdeveloperblog.app.ws.ui.model.request.SaveUserRequest;
import com.appsdeveloperblog.app.ws.ui.model.response.UserRest;
import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import org.springframework.beans.BeanUtils;
 
@Component
@Path("users")
public class UsersEntryPoint {

    @Autowired
    UserServiceImpl springService;
 
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<UserRest> getAllUsers() {
       
        List<UserRest> returnValue = new ArrayList();
        List<UserDto> foundUsers = springService.getAllUsers();
        
        for(UserDto userDto:foundUsers)
        {
            UserRest userRest = new UserRest();
            BeanUtils.copyProperties(userDto, userRest);
            returnValue.add(userRest);
        }
        return returnValue;
    }

    @POST
    @Produces(MediaType.TEXT_PLAIN)
    public String save(SaveUserRequest saveUserRequest) {
       UserDto userDto = new UserDto(); 
       BeanUtils.copyProperties(saveUserRequest, userDto);
 
       springService.save(userDto);
 
       return "Ok";
    }
 
}

Service class

This class Authowires the Spring Data JPA CrudRespository interface.

package com.appsdeveloperblog.app.ws.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.appsdeveloperblog.app.ws.io.entities.UserEntity;
import com.appsdeveloperblog.app.ws.io.repositories.UserRepository;
import com.appsdeveloperblog.app.ws.service.UserService;
import com.appsdeveloperblog.app.ws.shared.dto.UserDto;
import com.appsdeveloperblog.app.ws.shared.exceptions.UserServiceException;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import org.glassfish.jersey.internal.guava.Lists;
import org.springframework.beans.BeanUtils;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserRepository userRepository;

    @PostConstruct
    private void init() {
    }

    @Override
    public List<UserDto> getAllUsers() {
        List<UserDto> returnValue = new ArrayList();
        
       List<UserEntity> foundRecords = Lists.newArrayList(userRepository.findAll());
       
       for(UserEntity userEntity:foundRecords)
       {
            UserDto userDto = new UserDto();
            BeanUtils.copyProperties(userEntity, userDto);
            returnValue.add(userDto);
       }
        
        return returnValue;
    }
 

    @Override
    public void save(UserDto userDto) throws UserServiceException {
   
        UserEntity userEntity = new UserEntity();
        BeanUtils.copyProperties(userDto, userEntity);
        
        // Perform needed validation and other required business logic here
        // Set other required fields like enctypted password for example
        String encryptedPassword = "encrypted password here";
        
        userEntity.setEncryptedPassword(encryptedPassword);
        
        try {
            userRepository.save(userEntity);
        } catch (Exception ex) {
            throw new UserServiceException(ex.getMessage());
        }
    }

}

Entity class

package com.appsdeveloperblog.app.ws.io.entities;

import javax.persistence.*;
 
@Entity(name = "Users")
public class UserEntity {

    @Id
    @GeneratedValue
    private Long id;

    private String firstName;
    private String lastName;
    private String email;
    private String encryptedPassword;
 

    public Long getId() {
        return id;
    }

    public UserEntity setId(Long id) {
        this.id = id;
        return this;
    }

    public String getFirstName() {
        return firstName;
    }

    public UserEntity setFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public String getLastName() {
        return lastName;
    }

    public UserEntity setLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

    /**
     * @return the email
     */
    public String getEmail() {
        return email;
    }

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

    /**
     * @return the encryptedPassword
     */
    public String getEncryptedPassword() {
        return encryptedPassword;
    }

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

I hope this tutorial was helpful for you!

Happy learning!

Leave a Reply

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