maandag 12 januari 2015

Spring-boot rest/jpa full blown

Introduction

For a while I was looking for a suiting solution where I could use spring rest services combined with the Spring/JPA repositories. What I actual was looking for was a query driven rest service where I did not had to convert one bean to another. This awfull boilerplate code and is a maintainance nightmare.

After a lot of searching I ran into Spring-boot where the got a couple of steps further than I was looking for. After studying it for a while I got used to their ideas and must say I like them.

While I was searching I ran into a lot of examples but none of them was showing me how to deal with spring-boot in a professional and serious mather. So i picked up a lot from the reference guide and put the pieces of the puzzle together.

The configuration
I build the whole story in Maven for the gradle fans have a look at the reference guide. There is fully explained howto do it in gradle.

The basiscs of spring-boot is the simple fact that they provide you with a super pom for maven where you can cherry pick the parts you really like to use. Here are the cherries I picked from that tree:

The project pom 
<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">
    <parent>
        <artifactId>abstractservice</artifactId>
        <groupId>com.purat.services</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>userservice</artifactId>
    <packaging>jar</packaging>

    <properties>
        <tomcat.version>8.0.3</tcat.version>
        <!-- use UTF-8 for everything -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <start-class>com.purat.Application</start-class>
        <spring-boot-version>1.1.10.RELEASE</spring-boot-version>
    </properties>
// The super pom provided by spring-boot
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.10.RELEASE</version>
    </parent>

    <dependencies>
// these are the depedency to deliver all the necessary jpa libraries to approach the database. 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency
//This is the library to deliver all the necassary rest libraries. To create for example your own endpoints
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
//This is the library that gives us the web application part. 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
//This is the library that gives us the production-ready parts. For more details look at boot-starter-actuator
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
//For the database I used postgress.
        <dependency>
            <groupId>postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.0-801.jdbc4</version>
        </dependency>
// For handling the getters and setters I used lombock.
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
                <version>1.12.6</version>
                <scope>compile</scope>
        </dependency>
    </dependencies>   <build>
        <pluginManagement>
        <plugins>
//This plugin is responsible for the kind of packaging you desire. You can choose  a war or a jar
This plugin will build it with the desired content.  
To deploy a war file there are still a couple of steps to take. You can find the description for that here:
traditional deployment
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
        </pluginManagement>
    </build>

    <repositories>
        <repository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
        <repository>
            <id>org.jboss.repository.releases</id>
            <name>JBoss Maven Release Repository</name>
            <url>https://repository.jboss.org/nexus/content/repositories/releases</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>
</project>

The Application class and application.properties

Despite of what you see on the internet I like to keep the Application class to a bare minimum. We need this baby only to start the project up after we build a war or a jar. 


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * Created by compurat on 1/11/15.
 */
//Indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime.
@Configuration
// This annotation configures  and uses the standard beans that are on the classpath of the diverse libraries.
@EnableAutoConfiguration
//Configures component scanning directives for use with @Configuration classes.
@ComponentScan
public class Application {

    public static void main(String[] args) {
//This is the only line you need to start up you application.
        ConfigurableApplicationContext context = SpringApplication.run(Application.class);
    }

}

The application.properties file under the resources folder:

The way this works, looks to me like the Wicket framework. 
//The configuration of the database
spring.datasource.url=jdbc:postgresql://localhost:5432/people
spring.datasource.username=Pieter
spring.datasource.password=Pieter01
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
hibernate.hbm2ddl.auto=create-drop
spring.jpa.show-sql=true
// Setting the end point properties give it an id,give the possibility to shut the endpoints down and sensitive is security wise. Does an endpoint need username and password to be approached.
endpoints.beans.id=springbeans
endpoints.beans.sensitive=false
endpoints.shutdown.enabled=true
//Tomcat serverlog enabling and patterns.
server.tomcat.access-log-enabled=true
server.tomcat.access-log-pattern=%a asdasd
The log file propeties.
logging.file= logging/userservice.log
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR

Basicly we now have ready to run spring-boot webservice with JPA. It does not work yet we still need to fill in the missing pieces here. 

The application
Lets start with the rest endpoint. That is where it all begins. This is the profile endpoint and does not take any parameters in this case. But it give you an idea how an endpoint in spring-boot works:

import com.purat.data.People;
import com.purat.data.repository.PeopleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created by compurat on 1/11/15.
 */
//Tells spring that this is obvious a rest controller
@RestController
//Tells spring what the base url is.
@RequestMapping("peopleservice")
public class ProfileEndpoint {
    @Autowired
//spring/jpa repository to approsch the database
    private PeopleRepository peopleRepository;
//Tells spring what the endpoint url is.
    @RequestMapping("/profile")
    public Person profile() {
   The jpa entity
    Person person 
//The query parameter names are in the repository method name. in this case EmailadressAndPassword
= peopleRepository.findByEmailadressAndPassword("pieter.roskam@gmail.com","Roskam01");
If you Return the entity in the rest controller spring will underwater serialize the object to an json file.
        return person;
    }

As second part I like to handle the jpa entity:
import lombok.Getter;
import lombok.Setter;

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

/**
 * Created by compurat on 1/11/15.
 */
//Lombock getters and setters keep you code nice and clean.
@Getter
@Setter
//What table to approach
@Entity(name="people")
public class Person {
// the unique id and all the fields as named in the table.
    @Id
    private long id;
    private String firstname;
    private String lastname;
    private String address;
    private String housenumber;
    private String postalcode;
    private String city;
    private String telephone;
    private String verification;
    private String emailadress;
    private String password;

}

As third and last part I would like to show the jpa repository:
package com.purat.data.repository;

import com.purat.data.User;
import org.springframework.data.repository.CrudRepository;

 
The spring/jpa repository gives some benefits. For simple queries you can use the parameter names in the method, but for more complicated queries you can also use the @Query annotation to write a jql query .
public interface PeopleRepository extends CrudRepository<Person,Long> {

    User findByEmailadressAndPassword(String emailadress, String password);
}

Conclusion

Spring-boot took a complete new road to developing and deploying rest services. The most interesting part is probably that you can have a rest service in a jar file. That was a mind boggle to me for a while. The secret lies in the fact that it is a self executing jar. While starting up your Application class it will also startup tomcat if you configured it right (dependencies and properties file).

The second part that might be of interest, is the fact that it looks like that the separation of tiers is gone. But if you look closely it is not:

1. The separation of the database is still intact because it is hidden behind the spring/jpa repositories. They are not reachable from the outside. 

2. The entity bean seams to be exposed at the frontend but again that is not completely true. The bean will be serialized to a json file by Spring. That is the only visible thing on the outside. The only one that should reach this service is the frontend. Which is also separate.

I love the part that you can package a rest service into a jar. You put it somewhere on your server and start it with java -jar servicewhatever.jar. That will fire up everything you build for this rest service.


have fun!  

Geen opmerkingen:

Een reactie posten