In this short post, I will show how to build a simple JPA Microservice using Spring Boot. Spring Boot makes it easy to create stand-alone based applications that you can run and need very little Spring configuration as we will see in this short tutorial.
For an explanation about microservices, read this article of Martin Fowler.
For the code, see https://github.com/hugohendriks1978/climbing-api
As I was saying we are going to use Spring Boot. First start of with a simple java maven enabled project in the IDE of your choice. We will first start with the .pom file to get all the Spring dependencies right. I am going to build a simple ReferenceDataService microservice which can deliver some simple reference data such as a list of countries. Let take a look at the pom file:
4.0.0 ReferenceDataService nl.redrock jar ReferenceData Microservice ReferenceData Service 1.0 org.springframework.boot spring-boot-starter-parent 1.5.2.RELEASE org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-test test com.oracle ojdbc7 7.0 org.springframework.boot spring-boot-maven-plugin
As you can see, we the 1.5.2.RELEASE as a basis. We then have 4 dependencies. We need spring-boot-starter-web for libraries to creating the rest service, we need spring-boot-starter-data-jpa for the jpa capability, we need spring-boot-starter-test for testing capabilities and last an ojdbc.jar because the data is located in a Oracle database. I am also adding the spring-boot-maven-plugin to be able to run it from maven using Tomcat.
Let start of with the entry point….the application. This is the starting point of our simple service. It will look like this:
package nl.redrock.referencedataservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ReferenceDataApplication { public static void main(String[] args) { SpringApplication.run(ReferenceDataApplication.class, args); } }
As you can see, it is very simple. Use the @SpringBootApplication annotation to tag it as a Spring Boot application and that is it. Next we want a class which can retrieve data from database. Spring has a bunch helpful classes and templates for you to help with this. I already have a pre-filled oracle database running with 1 table called country. It has 3 columns….ID, CODE and NAME.
So as a first step we will create a Country class which maps to the table.
package nl.redrock.referencedataservice.data; import javax.persistence.Entity; import javax.persistence.Id; @Entity public class Country { @Id private int id; private String code; private String name; /** * @return the id */ public int getId() { return id; } /** * @param id the id to set */ public void setId(int id) { this.id = id; } /** * @return the code */ public String getCode() { return code; } /** * @param code the code to set */ public void setCode(String code) { this.code = code; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } @Override public String toString() { return String.format("Country[id=%d, code='%s', name='%s']", id, code, name); } }
As you can see this is also a very simple POJO with the 3 attributes mapping to the 3 columns with getter and setters. The annotation which does al the magic here is @Entity. This tells spring that it can be used for Object Relational Mapping. Next we we will create a class which will fetch the data.
package nl.redrock.referencedataservice.repository; import nl.redrock.referencedataservice.data.Country; import org.springframework.data.repository.CrudRepository; public interface CountryRepository extends CrudRepository{ Country findById(int id); }
And again….not much coding here. Just a simple interface which extends Spring’s CRUDRepository. We define just 1 extra interface for retrieving a country by its id. And basically that is it. We just have 1 thing left to do, and that is telling Spring which database to connect to. This is easily done by adding an application.properties to you classpath with all the settings in so it is automatically picked up by Spring.
#port to run apache on server.port=8888 # Oracle settings spring.datasource.url=jdbc:oracle:thin:@private.eu-west-1.compute.amazonaws.com:1521:xe spring.datasource.username=SECRET spring.datasource.password=SECRET spring.datasource.driver-class-oracle.jdbc.driver.OracleDriver #set sql level to debug to see all the sql statements logging.level.org.hibernate.SQL=debug logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n
The server.port is a setting which you can use to adjust the port tomcat runs on. Next up are the oracle database connection settings. And last of all some logging tweaking. A last thing to do is to make a unit test to see if it al works.
package nl.redrock.referencedataservice.repository; import junit.framework.TestCase; import nl.redrock.referencedataservice.data.Country; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestDatabase.Replace; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @DataJpaTest @AutoConfigureTestDatabase(replace=Replace.NONE) public class CountryRepositoryTest extends TestCase { @Autowired CountryRepository countryRepository; @Test public void testCountryRepository(){ Country c = this.countryRepository.findById(1); assertTrue(c != null); assertTrue(c.getCode().equals("ac")); assertTrue(c.getName().equals("Ascension Island")); } }
The things to look for here are the @DataJpaTest annotation which tells Spring it is a JPA test. The @AutoConfigureTestDatabase(replace=Replace.NONE) annotation tells Spring to not replace the application default DataSource.
Run the test:
As you can see, we can fetch data from the database with minimal coding.
Now for the service part. Spring also has easy ways to accommodate this using the @RestController annotation.
package nl.redrock.referencedataservice.controller; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import nl.redrock.referencedataservice.data.Country; import nl.redrock.referencedataservice.repository.CountryRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/referencedataservice/countries") public class ReferenceDataController { private final static Logger LOGGER = Logger.getLogger(ReferenceDataController.class.getName()); @Autowired CountryRepository countryRepository; @RequestMapping("/{id}") public Country getCountry(@PathVariable int id) { Country result; LOGGER.log(Level.INFO, "Getting country with id " + id); result = countryRepository.findById(id); return result; } @RequestMapping(method = RequestMethod.GET) ListgetCountries() { List result; LOGGER.log(Level.INFO, "Getting all countries"); result = new ArrayList(); Iterable countryList = countryRepository.findAll(); for (Country country : countryList) { result.add(country); } return result; } }
As you can see we implement 2 operations. 1 to get all the countries and 1 to get a specific country by its id. We use @Autowired to inject the countryRepository. And now for the proof of the pudding. Run mvn clean spring-boot:run and watch maven spin up a Tomcat instance with the referencedataservice application deployed on it. Open up the browser and call:
http://localhost:8888/referencedataservice/countries
Now call http://localhost:8888/referencedataservice/countries/160 to get a specific country
As you can see Spring makes it very easy to create rest services with minimal coding. If you want to look into some more advanced microservice features Spring has to offer, have a look here and here to see how you can use microservices in conjunction with Netflixs Eureka server.
Pingback: Running a simple SpringBoot API as a Docker container on AWS
Pingback: Add some swagger to your SpringBoot API