EP02 – Spring Data and Why it Rocks (Part 1)

spring data logo

CRUD

It stands for Create, Read, Update and Delete.

We, as programmers do CRUD all day, every day. It’s at the centre of any app in any language on any platform.

Thankfully, the good folks at Spring.io realized that since our jobs revolve around CRUD, there’s room to make our lives easier.

They’ve gone and taken out all the boring work of writing the code to support CRUD operations.

So much so, that you can write one line of code, and your application can do all the standard CRUD operations on an Entity of your choice.

Note: an Entity is “code” for a database table. You can create Entities using JPA… You’ll see some examples of this soon.

Prerequisites for Spring Data

In this article I’m going to assume you have already set up a Spring Boot app. If you haven’t, be sure to check out this article on how to do just that.

Once you’ve got a basic Spring Boot app created, here’s how to modify your pom.xml file to “activate” Spring Data.


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Note: be sure to paste the above code inside of your <dependencies> tags

Spring Data in Action

Okay, first thing’s first… We need to have an Entity to perform our CRUD on!

An Entity is just a Java object that will represent a table in our database.

So what do you feel like storing in a database?

How about a diary of what you ate? Why the hell not?

So what should our diary contain?

* Food eaten (pizza, apple, cookie, etc)
* Meal type (breakfast, lunch, dinner, snack)
* Time of day (8am, 12pm, 8pm, etc)
* Cost ($10, $5, free etc)

Okay, now with this information we can not only track what we’re eating, but we can also see where we’re spending too much money as well! Neat.

Let’s create this Entity as a Java object.

public class FoodDiary
{
  private String food;
  private String mealType;
  private Time timeOfDay;
  private Double cost;

  // getters and setters
}

That’s a standard Java object with all the appropriate instance variables (and getters and setters). But this code doesn’t yet have any JPA to tell it how to map this Java object to a database table. So let’s populate the code with some JPA.


@Entity
public class FoodDiary
{
  @Id
  @GeneratedValue
  private Long id;
  private String food;
  private String mealType;
  private Time timeOfDay;
  private Double cost;

  // getters and setters
}

Pretty easy right?

Okay, so now I can start talking about Spring Data.

We want to be able to do our CRUD operations on this “FoodDiary” object.

So to do this, we need to create a repository… Specifically we’ll create an interface that extends a Spring Data object called JpaRepository.

Let’s have a look at this code.

public interface FoodDiaryRepository extends JpaRepository<FoodDiary, Long> 
{
  // it can be empty inside if you want, or later, you can add any custom queries if the standard set of CRUD operations aren't enough. 
}

That’s it.

You’ve now created an object that you can use to perform CRUD operations on your FoodDiary.

Note: you need to tell it which object you’ll be performing the CRUD operations on, you see that in the area. The FoodDiary part is you telling Spring Data which Entity you’d like to interact with… The Lomg part is you telling Spring Data what the data type of the ID field is. We declared the FoodDiary’s ID to be a Long.

Putting it all together

In order to make use of our new repository, we need to call it from a Service or Controller.

Typically it’s good form to call repositories from Services as opposed to Controllers, as you shouldn’t be doing any business logic inside your Controllers. But for the purposes of this example, I’ll be bad and call the repository from my Controller (don’t hate me!).

Let’s assume we have some sort of a RootController that handles any requests coming into the app:

@Controller
public class RootController
{
  @RequestMapping(value="", method=RequestMethod.GET)
  public String rootMapping (ModelMap model)
  {
    model.put("foodDiary", new FoodDiary());
    return "foodDiary";
  }
}

Okay, so with this Controller, we will be able to get access to a “foodDiary” view if we visit the root URL of our application.

So let’s code up this “foodDiary” view.

I’ll make it an HTML file (called foodDiary.html)



  
    Food Diary Page
  
  
    

My Food Diary

Food:
Meal Type:
Time of Day:
Cost of Meal:

Okay, so now that we’ve got a very simple view put together, let’s take a look at how we can make use of our Spring Data repository.

In our view, we have created a form that executes a POST when someone clicks on our submit button.

Problem is, we haven’t yet created a method that will accept a POST from our view… so we’ll need to modify our RootController:

public class RootController
{
  @Autowired
  private FoodDiaryRepository foodDiaryRepository;

  @RequestMapping(value="", method=RequestMethod.GET)
  public String rootMapping (ModelMap model)
  {
    model.put("foodDiary", new FoodDiary());
    return "foodDiary";
  }

  @RequestMapping(value="", method=RequestMethod.POST)
  public String rootMappingPost(@ModelAttribute FoodDiary foodDiary, ModelMap model)
  {
    foodDiaryRepository.save(foodDiary);
    
    model.put("foodDiary", new FoodDiary());

    return "redirect:/";
  }
}

Now we’re getting somewhere!

As you can see, the code that relates to our Spring Data repository only takes up one line: foodDiaryRepository.save(foodDiary).

This will insert a new row into our database… of course, the foodDiaryRepository wouldn’t exist if we hadn’t declared it using an @Autowired annotation, but the Spring framework makes that part easy with dependency injection (via @Autowired)

Alright, so that covers the “C” of “CRUD”, what about all the rest of those letters?

Why don’t we tackle the “R”, let’s see how we can use Spring Data to read existing data from our database.

Thankfully, this is also dead simple.

You see, by default we have access to two useful methods:

  1. findAll()
  2. findOne()

The findAll() method does just what it sounds like it does… it finds ALL the rows in the table that relate to the Entity that you’re interacting with.

Which means, if we do a foodDiaryRepository.findAll(), it will return all the food diary rows.

Note: it returns a List of Entities, so in our case it would return a List

For the purposes of our code, I’d like to be more specific. I’d like to have our app return ONE row based on an ID that I give it.

So if I give it ID=1, then I want it to return the row in the database table that has ID equal to 1. I’ll use a URL to specify which ID I’d like to have returned.

So for example, if I go to: localhost:8080/food-diaries/1 I want it to return a row in the food_diary table with ID=1… or if I go to localhost:8080/food-diaries/34 it will return the row in food_diary where id=34.

Make sense?

Okay, so to accomplish this, we’ll just need to add a new method in our controller:

// this method would go inside of our RootController.java class
@RequestMapping(value="food-diaries/{foodDiaryId}", method=RequestMethod.GET)
public String getFoodDiaryItem (@PathVariable Long foodDiaryId, ModelMap model)
{
  FoodDiary foodDiaryItem = foodDiaryRepository.findOne(foodDiaryId);
  
  model.put("foodDiary", foodDiaryItem);

  return "foodDiary";
}

What this new method does is that it will turn any number in the URL into a variable (known as a Path Variable), then use that path variable to query our database via the foodDiaryRepository.findOne() method.

We then populate the model (our “foodDiary” entry) with the row we grabbed from the database. This, in turn, will populate our view with all the data from the database!

Neat.

Okay, so we’re all set with the “CR” of the “CRUD”… in the next lesson/podcast episode we’ll dive into how to do updates and deletes. Stay tuned.