Querying your Spring Data JPA Repository - Basic Setup
Tags: java, spring, jpa, repository
Published: 2020-01-12 21:53:36

Well, nothing is simple nowadays. I'm a fan of complexity, so here's not the place you'll find quick answers. Nevertheless, I do seek to organize things in a way that's easy to refer to later, after reading and understand these blog posts.

With that in mind, in this second post of the series [Querying your Spring Data JPA Repository](https://dev.to/brunodrugowick/querying-your-spring-data-jpa-repository-introduction-4ani) I'll create a basic setup where everything that lacks are repository queries. The following sections explain the app architecture.

If you don't want to invest time creating the basic setup, here's the [code/app](https://github.com/brunodrugowick/jpa-queries-blog-post/releases/tag/v.1.0.1).

## Main considerations

I'll use the current version of Spring Boot [2.2.2](https://spring.io/blog/2019/12/06/spring-boot-2-2-2-is-now-available) with the starters:

- `spring-boot-starter-web`: adds Spring MVC with Tomcat as the application server.
- `spring-boot-starter-data-jpa`: adds Spring Data JPA, providing persistence of objects.
- `spring-boot-starter-thymeleaf`: adds Thymeleaf, the most popular templating engine to build pages dynamically on the server for the client (browser) to download. "Oh, you're not using Angular or React, you fool" you may say. Yeah, deal with with. Thymeleaf is actually a great solution if you don't want a "decoupled" frontend with Angular or other frontend framework.
- `h2`: provides H2 in-memory database. Adding this dependency is enough to start coding without any database configured. I'll make everything compatible with MySQL so later one can only add the MySQL driver and configure the connection on `application.properties`.

So my `pom.xml` starts with the following basic dependencies:

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>
```

And the following complementary dependencies to help in the development process:

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
```

## App Layers (packages)

- Web `web.*`: controllers for pages or endpoints go here.
- Business `domain.*`: models (for now), e.g. Restaurant and Cuisine definitions.
- Infrastructure `infrastructure.*`: repositories go here.

## The code

I encourage you to build your own project following the scarce instructions I'm providing. The reason is that only through hands on experience you really learn. Of course I provide the complete code if you don't want the trouble.

### Web layer

<a name="findAll"></a>The web layer is composed of one index controller:

```java
@Controller
public class IndexPage {

    private final RestaurantRepository restaurantRepository;

    public IndexPage(RestaurantRepository restaurantRepository) {
        this.restaurantRepository = restaurantRepository;
    }

    @RequestMapping
    public String index(Model model) {
        model.addAttribute("restaurants", restaurantRepository.findAll());
        return "index";
    }
}
``` 

Notice the `restaurantRepository.findAll()` that I'll explain in a bit.

### Business layer

In the business layer I included the domain, i.e., the objects that'll be persisted.

Restaurant:


```java
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = "name"))
public class Restaurant {

    @EqualsAndHashCode.Include
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private BigDecimal deliveryFee;

    @ManyToOne
    @JoinColumn(name = "cuisine_id", nullable = false)
    private Cuisine cuisine;

    private String address;

    @CreationTimestamp
    @Column(columnDefinition = "datetime", updatable = false)
    private LocalDateTime createdDate;

    @UpdateTimestamp
    @Column(columnDefinition = "datetime")
    private LocalDateTime updatedDate;

}
```

And Cuisine:

```java
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = "name"))
public class Cuisine {

    @EqualsAndHashCode.Include
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "cuisine")
    List<Restaurant> restaurants;

    private String name;

}
```

Notice they have a relationship between then. That's it for the Business layer. Notice also that no Service is provided: for brevity I'll query the Repositories from the Controllers.

### Infrastructure layer

An interface that extends an interface from Spring Data JPA project. This provides the `findAll()` method that [you saw earlier](#findAll).

```java
public interface RestaurantRepository extends JpaRepository<Restaurant, Long> {
}

```

## Initial Data

To provide initial data, there's a file `import.sql` into `resources` folder. This is JPA stuff and runs when starting the application.

```sql
insert into cuisine (name) values ('Italian');
insert into cuisine (name) values ('Brazilian');
insert into cuisine (name) values ('American');
insert into cuisine (name) values ('French');

insert into restaurant (name, address, delivery_fee, cuisine_id, created_date, updated_date) values ('Mamma Mia', 'Mamma Street, 14', 10.0, 1, current_timestamp, current_timestamp);
insert into restaurant (name, address, delivery_fee, cuisine_id, created_date, updated_date) values ('Churrascaria', 'Disorder Avenue, 5000', 8.0, 2, current_timestamp, current_timestamp);
insert into restaurant (name, address, delivery_fee, cuisine_id, created_date, updated_date) values ('Burguer Place', 'Clueless Alley, 10', 5.0, 3, current_timestamp, current_timestamp);
insert into restaurant (name, address, delivery_fee, cuisine_id, created_date, updated_date) values ('La Maison du Croissant ', 'Rue Paris, 7', 15.0, 4, current_timestamp, current_timestamp);

insert into restaurant (name, address, delivery_fee, cuisine_id, created_date, updated_date) values ('Pasta Buona', 'Pasta Street, 4', 2.0, 1, current_timestamp, current_timestamp);
insert into restaurant (name, address, delivery_fee, cuisine_id, created_date, updated_date) values ('Marcante Pizzaria', 'Bricks Street, 21', 9.0, 2, current_timestamp, current_timestamp);
```

More to come... this series isn't over.