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!