Springboot example with REST and MySQL

Just a further fullstack example on how to bootstrap simple CRUD operations from Springboot application on MySQL persistence.

preparations: setting up the persistence MySQL on my playground machine:

-- user=root, password=Test_123
Create database playground;
Create table `movie`(
	`id` int(3) NOT NULL AUTO_INCREMENT, 
	`title` VARCHAR(100) NOT NULL, 
	`year` int (4), 
	primary key (`id`)
);

The Model that will fit to the created table:

package eu.christophburmeister.playground.models;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "movie")
public class Movie {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private long id;
	private String title;
	private int year;

	public Movie() {
		// nop
	}

	public Movie(String title, int year) {
		this.title = title;
		this.year = year;
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}

	@Override
	public String toString() {
		return String.format("Movie[id=%d, title='%s', year='%d']", id, title, year);
	}
}

The Interface IMovieRepository will be extended from Spring’s CrudRepository that will enrich it with all the basic CRUD stuff. Additionally the findByYearLessThan method is declared here. The naming of those methods has to match the conditions from Spring’s conventions:
http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/#jpa.query-methods.query-creation
In this case it will return a list of all the movies that came out before a given year.

package eu.christophburmeister.playground;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

import eu.christophburmeister.playground.models.Movie;

public interface IMovieRepository extends CrudRepository<Movie, Long> {

	List<Movie> findByYearLessThan(int year);

}

In the Controller first the Repo is pushed in via Autowire. Then the 4 example methods for CRUD are implemented. Every method is pulled via REST.
The fifth implementation calls our additional method from IMovieRepository. You see, there is no further implementation needed, as the implementation for the persistence part is created automatically on the fly.

package eu.christophburmeister.playground;

import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import eu.christophburmeister.playground.models.Movie;

@RestController
public class RestServiceController {

	@Autowired
	private IMovieRepository repo;

	static final Logger logger = LogManager.getLogger(RestServiceController.class.getName());

	// CREATE
	@RequestMapping("/movies/create")
	@ResponseBody
	public String createMovie(String title, int year) {
		Movie movie = new Movie(title, year);
		try {
			repo.save(movie);
		} catch (Exception e) {
			logger.error(e.getMessage());
			return e.getMessage();
		}
		return "creation successful: " + String.valueOf(movie.getId());
	}

	// READ
	@RequestMapping("/movies/read")
	@ResponseBody
	public String readMovie(long id) {
		Movie movie;
		try {
			movie = repo.findOne(id);
		} catch (Exception e) {
			logger.error(e.getMessage());
			return e.getMessage();
		}
		if (movie == null) {
			String errorMst = "no movie found for id " + id;
			logger.error(errorMst);
			return errorMst;
		} else {
			return movie.getTitle() + " : " + movie.getYear();
		}
	}

	// UPDATE
	@RequestMapping("/movies/update")
	@ResponseBody
	public String readMovie(long id, String title, int year) {
		Movie movie;
		try {
			movie = repo.findOne(id);
			movie.setTitle(title);
			movie.setYear(year);
			repo.save(movie);
		} catch (Exception e) {
			logger.error(e.getMessage());
			return e.getMessage();
		}
		return movie.getTitle() + " : " + movie.getYear();
	}

	// DELETE
	@RequestMapping("/movies/delete")
	@ResponseBody
	public String deleteMovie(long id) {
		try {
			repo.delete(id);
		} catch (Exception e) {
			logger.error(e.getMessage());
			return e.getMessage();
		}
		return "deletion successful";
	}

	@RequestMapping("/movies/readAllBeforeYear")
	public List<Movie> getMoviesBeforeYear(@RequestParam(value = "year") int year) {
		List<Movie> movies = repo.findByYearLessThan(year);
		return movies;
	}
}

The Starter class:

package eu.christophburmeister.playground;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

	static final Logger logger = LogManager.getLogger(Application.class.getName());

	public static void main(String[] args) {
		logger.info("entered application");
		SpringApplication.run(Application.class, args);
	}
}

The two src/main/resource files:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console" />
        </Root>
        <Logger name="eu.christophburmeister.playground" level="info" additivity="false">
            <AppenderRef ref="Console" />
        </Logger>
    </Loggers>
</Configuration>
spring.datasource.url = jdbc:mysql://localhost:3306/playground
spring.datasource.username = root
spring.datasource.password = Test_123

# Keep the connection alive if idle for a long time (needed in production)
spring.datasource.testWhileIdle = true
spring.datasource.validationQuery = SELECT 1

# Show or not log for each sql query
spring.jpa.show-sql = true

# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = update

# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy

# Use spring.jpa.properties.* for Hibernate native properties (the prefix is
# stripped before adding them to the entity manager)

# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

And the Maven project orchestration:

<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>eu.christophburmeister</groupId>
	<artifactId>springboot-rest</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.3.1.RELEASE</version>
	</parent>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
			<exclusions>
				<!-- we want to rely on external log4j2 -->
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-ws</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<!-- JPA -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

		<!-- logging -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j2</artifactId>
		</dependency>
	</dependencies>
</project>