[Java] Spring Boot Getting Started Guide [Accessing Data with JPA]

7 minute read

Purpose

For those who have completed Spring Quickstart Guide, those who have started learning Spring Boot, and those who want to review,

The official guide is popular, so give it a try! I will share what I learned by actually working on Accessing Data with JPA.

Development environment


OS: macOS Mojave version 10.14.6
Text Editor: Visual Studio Code (VSCode)
Java: 11.0.2

Review of Quickstart Guide is here Review of Building a RESTful Web Service Review of Consuming a RESTful Web Service

Let’s start #1.SpringBoot project!

First, access spring initializr.

  1. Click the ADD DEPENDENCIES button and add Spring Data JPA and H2 Database. ![Screenshot 2020-07-06 13.52.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/581b30da-97c3-4bb9-d9c8-(a6c85e99c8fc.png) Screenshot 2020-07-06 13.52.27.png

**Spring Data JPA (Java Persistence API) is an API for storing and retrieving Java objects in the database (O/R mapping). **

**H2 Database toha is a standard database for Java. It is convenient because you do not need to set up a separate server such as MySQL by adding it here. **

2.Artifact, Name changed to accessing-data-jpa.

  1. Change Java to 11.

Then click the GENERATE button to download the Zip file.

Screenshot 2020-07-06 13.55.11.png

Extract the downloaded Zip file and you’re ready to go.

2.Let’s add code!

Open the previous folder with VS Code. We recommend that you install the extension Java Extension Pack. It is said that you should install it. (Only for non-installers)

![Screenshot 2020-06-30 10.08.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/80331ebc-b6e3-af7b-181a-(3c8921288b00.png)

Let’s create ##Customer.java!

Create a Customer.java file in src/main/java/com/example/accessingdatajpa/.

![Screenshot 2020-07-06 14.11.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/4b7bef09-6883-0e85-0da4-(2f11bd7a1e41.png)

Add code inside the Customer.java file.

Customer.java


package com.example.accessingdatajpa;

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

@Entity
public class Customer {

  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private Long id;
  private String firstName;
  private String lastName;

  protected Customer() {}

  public Customer(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  @Override
  public String toString() {
    return String.format(
        "Customer[id=%d, firstName='%s', lastName='%s']",
        id, firstName, lastName);
  }

  public Long getId() {
    return id;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }
}

Let’s dig deeper into the code we added to the Customer.java file.

@Entity

Customer.java


@Entity
public class Customer {
  // omitted
}

The @Entity annotation is a class that is linked to the DB table (entity class). Variables defined in this entity class are associated with DB columns.

@Id, @GeneratedValue(strategy=GenerationType.AUTO), constructor

Customer.java


@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;

protected Customer() {}

public Customer(String firstName, String lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

The variable attached to the @Id annotation is the primary key of the table. Also, with the @GeneratedValue(strategy=GenerationType.AUTO) annotation, ids will be automatically generated with sequential numbers.

In the Customer table this time, the primary key is id, and the other columns have firstName and lastName.

As a requirement of entity class, a constructor without arguments is required, so define a constructor without arguments, The constructor with arguments is defined for creating an instance to be saved in DB.

③ Definition of toString method and getter method

Customer.java


@Override
public String toString() {
  return String.format(
     "Customer[id=%d, firstName='%s', lastName='%s']",
      id, firstName, lastName);
}

public Long getId() {
  return id;
}

public String getFirstName() {
  return firstName;
}

public String getLastName() {
  return lastName;
}

The toString method is a method that returns a string representation defined in the java.lang.Object class. The @Override annotation overrides the toString method defined in the Object class in this class. It is an annotation to specify .

If not overridden correctly, an error will occur. Therefore, if you make a typo like toStrign(), you will get an error at compile time and tell you.

This time I’m overriding a method that returns a string to display the id, firstName and lastName.

When using the method called String.format, the format determined in the first argument must be specified, so %s specifies a character string, and %d specifies a decimal integer and format.

After that, we have defined getter methods to get each variable.

Let’s create ##CustomerRepository.java!

Create a CustomerRepository.java file in src/main/java/com/example/accessingdatajpa/.

![Screenshot 2020-07-07 12.01.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/8e8c8cb7-cddc-025b-57b3-(be8c7448e0f1.png)

Add code inside the CustomerRepository.java file.

``

CustomerRepository.java
package com.example.accessingdatajpa;

import java.util.List;import org.springframework.data.repository.CrudRepository;

public interface CustomerRepository extends CrudRepository<Customer, Long> {

  List<Customer> findByLastName(String lastName);

  Customer findById(long id);

}

Let’s dig deeper into the code we added to the CustomerRepository.java file.

① Create repository interface

``

CustomerRepository.java
public interface CustomerRepository extends CrudRepository<Customer, Long> {
  // omitted
}

We are creating an interface called CustomerRepository. At that time, it inherits an interface called CrudRepository. CrudRepository interface specifies the entity type Customer and ID type Long as generic parameters in the argument.

The CrudRepository interface is an interface that allows operations such as Create, Read, Update, and Delete (CRUD). Therefore, the CustomerRepository interface will also be able to perform the above operations.

② Method implementation

``

CustomerRepository.java
List<Customer> findByLastName(String lastName);

Customer findById(long id);

In Spring Data JPA, findBy 〇 〇 and methods that meet a specific naming convention are defined as ** query methods from their contents. **

This time, findByLastName(String lastName) is applicable. It is a method that can get all the data that matches lastName received in the argument. findById(long id) is a method that allows you to get the data that matches a specific id.

Edit AccessingDataJpaApplication.java!

I think the default state is as follows.

``

AccessingDataJpaApplication.java
package com.example.accessingdatajpa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AccessingDataJpaApplication {
    
    public static void main(String[] args) {
      SpringApplication.run(AccessingDataJpaApplication.class, args);
    }

}

Add code while referring to the formula.

``

AccessingDataJpaApplication.java
package com.example.accessingdatajpa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// added code
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class AccessingDataJpaApplication {

  // added code
  private static final Logger log = LoggerFactory.getLogger(AccessingDataJpaApplication.class);

  public static void main(String[] args) {
    SpringApplication.run(AccessingDataJpaApplication.class);
  }

  // added code
  @Bean
  public CommandLineRunner demo(CustomerRepository repository) {
    return (args) -> {
      // save a few customers
      repository.save(new Customer("Jack", "Bauer"));
      repository.save(new Customer("Chloe", "O'Brian"));
      repository.save(new Customer("Kim", "Bauer"));
      repository.save(new Customer("David", "Palmer"));
      repository.save(new Customer("Michelle", "Dessler"));

      // fetch all customers
      log.info("Customers found with findAll():");
      log.info("-------------------------------");
      for (Customer customer: repository.findAll()) {
        log.info(customer.toString());
      }
      log.info("");

      // fetch an individual customer by ID
      Customer customer = repository.findById(1L);
      log.info("Customer found with findById(1L):");
      log.info("--------------------------------");
      log.info(customer.toString());
      log.info("");

      // fetch customers by last name
      log.info("Customer found with findByLastName('Bauer'):");
      log.info("--------------------------------------------" );
      repository.findByLastName("Bauer").forEach(bauer -> {
        log.info(bauer.toString());
      });
      // for (Customer bauer: repository.findByLastName("Bauer")) {
      // log.info(bauer.toString());
      //}
      log.info("");
    };
  }

}

Let’s dig deeper into the code we added to the AccessingDataJpaApplication.java file.

① log for log output

``

AccessingDataJpaApplication.java
// added code
private static final Logger log = LoggerFactory.getLogger(AccessingDataJpaApplication.class);

Logger and LoggerFactory are used to display the log in the terminal. You can get the log by specifying the class in the argument of LoggerFactory.getLogger().

② Data registration and acquisition

``

AccessingDataJpaApplication.java
// added code
@Bean
public CommandLineRunner demo(CustomerRepository repository) {
  return (args) -> {
    // save a few customers
    repository.save(new Customer("Jack", "Bauer"));
    repository.save(new Customer("Chloe", "O'Brian"));
    repository.save(new Customer("Kim", "Bauer"));
    repository.save(new Customer("David", "Palmer"));
    repository.save(new Customer("Michelle", "Dessler"));

    // fetch all customers
    log.info("Customers found with findAll():");
    log.info("-------------------------------");
    for (Customer customer: repository.findAll()) {
      log.info(customer.toString());
    }
    log.info("");

    // fetch an individual customer by ID
    Customer customer = repository.findById(1L);
    log.info("Customer found with findById(1L):");
    log.info("--------------------------------");
    log.info(customer.toString());
    log.info("");

    // fetch customers by last name
    log.info("Customer found with findByLastName('Bauer'):");
    log.info("--------------------------------------------" );repository.findByLastName("Bauer").forEach(bauer -> {
      log.info(bauer.toString());
    });
    // for (Customer bauer: repository.findByLastName("Bauer")) {
    // log.info(bauer.toString());
    //}
    log.info("");
  };
}

Registering and acquiring data SpringBoot will call the method CommandLineRunner demo(CustomerRepository repository) after running the application.

First of all, we are registering the data. When instantiating Customer, the string (firstName, lastName) is passed to the argument of the constructor. Then, the Java method generated by the save method of repository is registered in the Customer table.

``


repository.save(new Customer("Jack", "Bauer"));

Next, we get all the data with findAll(). The loop is extended by the extended for statement, and the acquired multiple data are received by the argument customer and output one by one. I am using the overridden toString() when outputting.

``


for (Customer customer: repository.findAll()) {
  log.info(customer.toString());
}

Next, the data whose ID is 1 is acquired by findById(1L). Since this can acquire only one specific data, it is output without turning the loop.

``


// fetch an individual customer by ID
Customer customer = repository.findById(1L);
log.info(customer.toString());

Finally, in findByLastName("Bauer"), we get the data with the lastName value Bauer. Since this data may be multiple, it was defined to be received as a List type.

The obtained multiple data is looped by the forEach statement, received by the argument bauer, and output one by one. I think that the extended for statement that is commented out can be written in this way.

``


// fetch customers by last name
repository.findByLastName("Bauer").forEach(bauer -> {
    log.info(bauer.toString());
});
// for (Customer bauer: repository.findByLastName("Bauer")) {
  // log.info(bauer.toString());
//}

3.Let’s run!

Now that the application is ready to run, let’s check it.

Enter the following command in the terminal and press Enter.

terminal


$ ./mvnw spring-boot:run

After a while, the following characters will appear in the terminal.

terminal


Customers found with findAll():
- ------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=2, firstName='Chloe', lastName='O'Brian']
Customer[id=3, firstName='Kim', lastName='Bauer']
Customer[id=4, firstName='David', lastName='Palmer']
Customer[id=5, firstName='Michelle', lastName='Dessler']

Customer found with findById(1L):
- -------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']

Customer found with findByLastName('Bauer'):
- -------------------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=3, firstName='Kim', lastName='Bauer']

Reference site

The basics of JPA related annotations-Part 1- Entity Class Requirements CrudRepository ** [Spring Data JPA] Naming rules for automatically implemented methods**