Build a basic Spring Boot application that uses JPA to access a database
Download ZIP View on GitHubIn this exercise you’ll build a basic Spring Boot application that uses JPA to access a database. When run locally (un-attached to any DB) it will use an in memory instance of H2, but if a datasource is present (eg: by adding it to the properties) it’ll use it. When pushed to Cloud Foundry and bound with a MySQL or Postgres service it will auto-magically use it instead.
You’ll start with a shell project from start.spring.io, create a Domain, add an Interface which will tell Spring Data to create a Repository, and then add a bit of code to initialize it with some data. After that you’ll be able to start it as a Web application and browse the data via a ReSTful API. With that done, you’ll enhance the service. Finally, you can try pushing it to Cloud Fundry and binding it to a sql database instance.
IMAGE
IMAGE
If you’re not familiar with Spring Boot apps spend a little time exploring the project. There’s a “main” class that tells Spring to start up and initialize everything, an applications.properties that’s a default location for key/value pairs, and the POM is setup with dependancies that will tell Spring Boot to do things for us. For example, adding the Web starter tells Boot to embed a Tomcat server in our app and setup its context so it just works.
First create a basic class to model a domain. This will be nothing more then a string with an id.
// imports for JPA (put them at the top)
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
class Greeting {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String text;
public Greeting(String text) {
super();
this.text = text;
}
@Override
public String toString() {
return "Greeting [id=" + id + ", text=" + text + "]";
}
public Integer getId() {
return id;
}
public String getText() {
return text;
}
public Greeting() {}
}
Next, create an Interface that will tell Spring Data that you want to setup a Repository to manage our new Domain class. The empty repository definition will create it with only basic operations.
// import for Spring Data
import org.springframework.data.jpa.repository.JpaRepository;
public interface GreetingRepository extends JpaRepository<Greeting, Integer> {
}
Yes, it really is this easy to define a Repository! The presence of this interface will result in Spring Data creating a repository in the context that can then be accessed by the app.
In this step you will generate a Bean of type ApplicationRunner. Instances of this class are run by Spring Boot when it starts up. We’ll use Spring’s dependancy injection to pass in our Spring Data created Repository, and then populate it with some data.
// imports for the Application runner
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
// public class MyDemoApplication {
Logger logger = LoggerFactory.getLogger(MyDemoApplication.class);
// Loads the database on startup using the repository created by Spring Data.
@Bean
ApplicationRunner loadDatabase(GreetingRepository gr) {
return appRunner -> {
logger.debug("loading database..");
gr.save(new Greeting("Hello"));
gr.save(new Greeting("Hola"));
gr.save(new Greeting("Ohai"));
logger.debug("record count: {}", gr.count());
gr.findAll().forEach(x -> logger.debug(x.toString()));
};
}
...
logging:
level:
io:
pivotal: DEBUG
spring:
jpa:
hibernate:
ddl-auto: create-drop
Spring Boot uses a convention where it loads applications.yml (or .properties) by default. The first property sets the logging level for everything under the package io.pivotal to DEBUG. If you named you package something different change the properties to reflect the proper name. This will allow you to see your logging messages in the Configuration class.
The second property is a Hibernate specific setting. This will create a schema in the DB to support our applicaiton (destroying any existing version), and when the app closes the Session the schema will be deleted. This is good for demos where you want to keep your DB clean. (See the hibernate documentation on more options for this setting.)
With all that done, launch the app and browse the data!
IMAGE
Spring Boot brought us Actuator which generates a set of endpoints that provides information about what’s happening in your application. Boot 2 has added extra security to the actuator so enable env and beans in the property file.
management:
endpoints:
web:
exposure:
include: health,info,env,beans
Take a minute and look at all the good things you have access to view. Note: the default URL has changed back to the original style and security is off by default.
http://localhost:8080/actuator/env
http://localhost:8080/actuator/beans
Now add a method to the Repository to do some searching.
import java.util.List;
import org.springframework.data.repository.query.Param;
//In the Interface
List<Greeting> findByText(@Param("text") String text);
gr.save(new Greeting("Hello"));
This will add a new record with the same value so we can search and find 2 entries.
Notice the format that it gives you to search. You can now find the two records you entered that have the value Hello with a URL like this:
http://localhost:8080/greetings/search/findByText?text=Hello
In this step you will build the application into a self-executing jar file and deploy it onto the Pivotal WebServices instance of Cloud Foundry using the command line tool cf
. The tool is already installed on the demo machine, and logged in to an account. For more information on all this, check out the demo and talk with the technical team at the PCF stations.
$ cd ~/S1P2017/workspace/<your_project>
$ ./mvnw clean package
(lots of output from build)
$ cf push your_app_name -p target/your_app_name-0.0.1-SNAPSHOT.jar --random-route
(lots of output from the push)
The –random-route flag tells Cloud Fundry to add random words to the URL for your app so that it won’t conflict with any other applications that might use the same name you did.
So the environment is ready for the next person, it would be helpful if you deleted files. This keeps the project explorer clean, and if the someone happens to use the same name as you did there won’t be any conflicts.
Thanks!
$ cf delete your_app_name
Really delete the app your_app_name?> y