© Felipe Gutierrez 2016

Felipe Gutierrez, Pro Spring Boot, 10.1007/978-1-4842-1431-2_7

7. Data Access with Spring Boot

Felipe Gutierrez

(1)Albuquerque, New Mexico, USA

Data has become the most important part of the IT world, from trying to access, persist, and analyze it, to using a few bytes to petabytes of information. There have been many attempts to create frameworks and libraries to facilitate a way for developers to interact with the data, but sometimes this becomes too complicated.

The Spring Framework after version 3.0 created different teams that specialized in the different technologies, and the Spring-Data project team was born. This particular project’s goal is to make easier uses of data access technologies, from relational and non-relational databases, to map-reduce frameworks and cloud-based data services. This Spring-Data project is a collection of subprojects specific to a given database.

This chapter covers data access with Spring Boot using the simple application from Chapter 2—the Spring Boot journal app. You are going to make this simple app work with SQL and NoSQL databases. From the journal app, you are going to use only the model—nothing about the web, just pure data. Let’s get started.

SQL Databases

Do you remember those days when (in the Java world) you needed to deal with all the JDBC (Java Database Connectivity) tasks? You had to download the correct drivers and connection strings, open and close connections, SQL statements, result sets, and transactions, and convert from result sets to objects. In my opinion, these are all very manual tasks. Then a lot of ORM (Object Relational Mapping) frameworks started to emerge to manage these tasks—frameworks like Castor XML, Object-Store, and Hibernate to mention a few. They allowed you to identify the domain classes and create XML that was related to the database’s tables. At some point you also needed to be an expert to manage those kind of frameworks.

The Spring Framework helped a lot with those frameworks by following the template design pattern. It allowed you create an abstract class that defined ways to execute the methods and created the database abstractions that allowed you to focus only on your business logic. It left all the hard lifting to the Spring Framework, including handling connections (open, close, and pooling), transactions, and the way you interact with the frameworks.

It’s worth mentioning that the Spring Framework relies on several interfaces and classes (like the javax.sql.DataSource interface) to get information about the database you are going to use, how to connect to it (by providing a connection string), and its credentials. Now, if you have some kind of transaction management to do, DataSource is essential. Normally DataSource requires the Driver class, the JDBC URL, the username, and password to connect to the database.

Data Access Using the JDBC Template with Spring Boot

This section shows you the basics involved in data access by using only the JDBC abstraction from the Spring Framework using Spring Boot. You will be using the spring-boot-starter-jdbcpom. In the example, you are going to use the H2 in-memory database, which is a very effective engine for testing purposes.

Start by executing the Spring Boot CLI and using the init command:

$ spring init -d=jdbc,h2 -g=com.apress.spring -a=simple-jdbc-app --package-name=com.apress.spring -name=simple-jdbc-app -x

As you can see, this command will create a simple application that depends on the spring-boot-starter-jdbc pom and the H2 (the H2 is an in-memory database engine) dependency. See Listing 7-1.

Listing 7-1. pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>com.apress.spring</groupId>
        <artifactId>simple-jdbc-app</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>


        <name>simple-jdbc-app</name>
        <description>Demo project for Spring Boot</description>


        <parent>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>1.3.2.RELEASE</version>
                <relativePath/> <!-- lookup parent from repository -->
        </parent>


        <properties>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                <java.version>1.8</java.version>
        </properties>


        <dependencies>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-jdbc</artifactId>
                </dependency>


                       <dependency>
                                       <groupId>com.h2database</groupId>
                                       <artifactId>h2</artifactId>
                        <scope>runtime</scope>
                       </dependency>


                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>
                        <scope>test</scope>
                </dependency>
        </dependencies>


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


</project>

Listing 7-1 shows you the pom.xml file. You can see that the spring-boot-starter-jdbc pom and the H2 dependency are included. Next, let’s reuse the Journal Java class from Chapter 2 as the main domain class. You need to create the directory structure. This class must be in the src/main/java/com/apress/spring/domain folder. See Listing 7-2.

Listing 7-2. src/main/java/com/apress/spring/domain/Journal.java
package com.apress.spring.domain;

import java.text.SimpleDateFormat;
import java.util.Date;


public class Journal {

        private Long id;
        private String title;
        private Date created;
        private String summary;


        private SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");

        public Journal(Long id, String title, String summary, Date date){
                this.id = id;
                this.title = title;
                this.summary = summary;
                this.created = date;
        }


        Journal(){}

        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 Date getCreated() {
                return created;
        }


        public void setCreated(Date created) {
                this.created = created;
        }


        public String getSummary() {
                return summary;
        }


        public void setSummary(String summary) {
                this.summary = summary;
        }


        public String getCreatedAsShort(){
                return format.format(created);
        }


        public String toString(){
                StringBuilder value = new StringBuilder("* JournalEntry(");
                value.append("Id: ");
                value.append(id);
                value.append(",Title: ");
                value.append(title);
                value.append(",Summary: ");
                value.append(summary);
                value.append(",Created: ");
                value.append(getCreatedAsShort());
                value.append(")");
                return value.toString();
        }
}

Listing 7-2 shows you the Journal.java class. If you copied this class from Chapter 2, you need to remove all the annotations, because you don’t need them now. This class is a simple POJO (plain old Java object). Next you will create a service in the src/main/java/com/apress/spring/service directory, which is the JournalService.java class. The actions of this service are to insert data into the database and get all the information from the database. See Listing 7-3 and analyze its contents.

Listing 7-3. src/main/java/com/apress/spring/service/JournalService.java
package com.apress.spring.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;


import com.apress.spring.domain.Journal;

@Service
public class JournalService {
        private static final Logger log = LoggerFactory.getLogger(JournalService.class);


        @Autowired
        JdbcTemplate jdbcTemplate;


        public void insertData(){
                log.info("> Table creation");
                jdbcTemplate.execute("DROP TABLE JOURNAL IF EXISTS");
                jdbcTemplate.execute("CREATE TABLE JOURNAL(id SERIAL, title VARCHAR(255), summary VARCHAR(255), created TIMESTAMP)");
                log.info("> Inserting data...");
                jdbcTemplate.execute("INSERT INTO JOURNAL(title,summary,created) VALUES('Get to know Spring Boot','Today I will learn Spring Boot','2016-01-01 00:00:00.00')");
                jdbcTemplate.execute("INSERT INTO JOURNAL(title,summary,created) VALUES('Simple Spring Boot Project','I will do my first Spring Boot project','2016-01-02 00:00:00.00')");
                jdbcTemplate.execute("INSERT INTO JOURNAL(title,summary,created) VALUES('Spring Boot Reading','Read more about Spring Boot','2016-02-01 00:00:00.00')");
                jdbcTemplate.execute("INSERT INTO JOURNAL(title,summary,created) VALUES('Spring Boot in the Cloud','Learn Spring Boot using Cloud Foundry','2016-01-01 00:00:00.00')");
                log.info("> Done.");
        }


        public List<Journal> findAll(){
                List<Journal> entries = new ArrayList<>();
                jdbcTemplate.query("SELECT * FROM JOURNAL",
                                        new Object[]{},
                                        (rs,row) -> new Journal(rs.getLong("id"), rs.getString("title"), rs.getString("summary"),new Date(rs.getTimestamp("created").getTime())))
                                .forEach(entry -> entries.add(entry));
                return entries;
        }
}

Listing 7-3 shows you the JournalService.java class. Let’s take a look at its contents:

  • JdbcTemplate. It auto-wires a JdbcTemplate class that will be the responsible for executing tasks against the database. This particular class is based on the template design pattern that I mentioned that allows developers to focus only on the data and leave all the database tasks (insert, delete, etc.) to the template. How it knows which database to connect to is discussed shortly.

  • insertData. This method will first try to drop a Journal table if it exists, then it will create the Journal table with its fields and, finally, it will insert the data into the database. All these actions will be through the jdbcTemplate instance by executing its method execute (this execute method accepts the SQL query syntax).

  • findAll. This method will use the jdbcTemplate instance and the query method (that accepts a SQL syntax) to get all the data; it will return a collection of Journal instances.

  • Logger. A log instance that prints out what is going on in the method calls.

Next, modify the SimpleJdbcAppApplication.java class to look like Listing 7-4.

Listing 7-4. src/main/java/com/apress/spring/SimpleJdbcAppAplication.java
package com.apress.spring;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


import com.apress.spring.service.JournalService;

@SpringBootApplication
public class SimpleJdbcAppApplication implements CommandLineRunner{
        private static final Logger log = LoggerFactory.getLogger(SimpleJdbcAppApplication.class);


        @Autowired
        JournalService service;


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


        @Override
        public void run(String... arg0) throws Exception {
                log.info("@@ Inserting Data....");
                service.insertData();
                log.info("@@ findAll() call...");
                service.findAll().forEach(entry -> log.info(entry.toString()));
        }
}

Listing 7-4 shows you the SimpleJdbcAppApplication.java class. As you already know, this is the main class that will be executed:

  • It declares the auto-wired version of the JournalService, making it available when the run method executes.

  • It implements the CommandLineRunner interface, and of course you need to implement its method as well, called public void run(String... args). Just remember that this run method will be executed after the Spring Boot has started. This is a good place to call the JournalService instance and execute the data insertion and to call the findAll method.

  • The Logger log instance prints out what is going on in the execution of the class.

To run the application, execute the following command:

$ ./mvnw spring-boot:run

This command will run the app using the Maven wrapper that comes with the Spring Initializr. If you have Maven as a global tool, just run this command:

$ mvn spring-boot:run
Note

If you are using the Maven wrapper (mvnw) command and you are getting the following error—Error: Could not find or load main class org.apache.maven.wrapper.MavenWrapperMain—this means that you don’t have the .mvn folder and its JAR files in the current directory. So, you need to install them manually. I know this sound redundant, but for this, you need to use Maven (a global installation and available in your PATH environment variable) and execute $ mvn –N io.takari:maven:wrapper. Remember that the idea of the Maven wrapper is portability, so if you want to send your code to somebody, just make sure to include the .mvn folder and its content. That way, that person doesn’t need to have/install Maven.

After executing either of these commands, you should see the following output:

INFO - [main] c.a.spring.SimpleJdbcAppApplication      : @@ Inserting Data....
INFO - [main] c.apress.spring.service.JournalService   : > Table creation
INFO - [main] c.apress.spring.service.JournalService   : > Inserting data...
INFO - [main] c.apress.spring.service.JournalService   : > Done.
INFO - [main] c.a.spring.SimpleJdbcAppApplication      : @@ findAll() call...
INFO - [main] c.a.spring.SimpleJdbcAppApplication      : * JournalEntry(Id: 1,Title: Get to know Spring Boot,Summary: Today I will learn Spring Boot,Created: 01/01/2016)
INFO - [main] c.a.spring.SimpleJdbcAppApplication      : * JournalEntry(Id: 2,Title: Simple Spring Boot Project,Summary: I will do my first Spring Boot project,Created: 01/02/2016)
INFO - [main] c.a.spring.SimpleJdbcAppApplication      : * JournalEntry(Id: 3,Title: Spring Boot Reading,Summary: Read more about Spring Boot,Created: 02/01/2016)
INFO - [main] c.a.spring.SimpleJdbcAppApplication      : * JournalEntry(Id: 4,Title: Spring Boot in the Cloud,Summary: Learn Spring Boot using Cloud Foundry,Created: 01/01/2016)
INFO - [main] c.a.spring.SimpleJdbcAppApplication      : Started SimpleJdbcAppApplication in 1.736 seconds (JVM running for 6.581)

As you can see from this output, the app is creating the table, inserting the data, and then finding all the data persisted into the database. But how? You didn’t install any database engine or something to persist the data and you didn’t create any DataSource or add any URL string connections. Remember that this simple app is using the H2 in-memory database. The magic happens within Spring Boot for all related actions against the database, like connection, query execution, and transactions (if you use the @Transactional annotation as a marker in the class). But again, how does Spring Boot know about it?

Remember that everything starts with the auto-configuration (provided by the @SpringBootApplication annotation). It will detect that you have a H2 in-memory database dependency and it will create the right javax.sql.DataSource implementation. This means that by default it will have the org.h2.Driver driver class, which is the connection URL as jdbc:h2:mem:testdb and the username: sa and password: empty to connect to the H2 engine.

The H2 engine offers a console where you can see all the tables and its data; however, this console is a web application. So, what do you think you will need to get access to the H2 console? You are correct! You need to include the spring-boot-starter-web pom dependency to your pom.xml.

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

You also need to add the following property to the src/main/resources/application.properties file. See Listing 7-5.

Listing 7-5. src/main/resources/application.properties.
spring.h2.console.enabled=true

Listing 7-5 shows you the contents of the application properties. This property will enable the H2 web console. Now, you can run your application again and the first thing you will notice is that it no longer stops; it keeps running. You can see the logs that the Tomcat embedded server started. Now go to your browser and go to http://localhost:8080/h2-console. You should see something similar to Figure 7-1.

A340891_1_En_7_Fig1_HTML.jpg
Figure 7-1. H2 web console (http://localhost:8080/h2-console)

Figure 7-1 shows you the H2 web console. You should see the Driver class, the JDBC URL, and the credentials. If by some reason the JDBC URL is not the same, modify its value to jdbc:h2:mem:testdb. If you then click the Connect button, you should see something similar to Figure 7-2.

A340891_1_En_7_Fig2_HTML.jpg
Figure 7-2. H2 web console in-memory testdb connection

Figure 7-2 shows you the in-memory database, including the testdb and the Journal table. You can expand it and see its definition. You can also execute some SQL queries. For example, you can run the SELECT * FROM JOURNAL to see all the data that the application inserted. See Figure 7-3.

A340891_1_En_7_Fig3_HTML.jpg
Figure 7-3. SQL statements

Figure 7-3 shows you the query result—all the data from the application. The H2 in-memory database is a very good option for creating applications that need a persistence mechanism, which is normally used for developing and testing purposes.

Don’t forget to terminate your application by pressing Ctrl+C.

You can expose and persist data by using the JdbcTemplate, but there are easier ways. Let’s take a look at another option, something that is familiar to you from Chapter 2, which is to use the JPA technology.

Data Access Using JPA with Spring Boot

The JPA (Java Persistence API, a J2EE specification. There is a nice article about JPA at http://www.oracle.com/technetwork/articles/java/jpa-137156.html ) is another alternative to using lightweight persistence objects. Hibernate and Eclipse TopLink are the primary implementations of the JPA. The Spring Framework has been part since its inception and played a very important role by providing helpers and abstraction classes to make life easier for developers.

You are going to continue to use the same journal app and make it work using the JPA technology. So, to start, you can open a terminal and execute the Spring Initializr.

$ spring init -d=data-jpa,h2 -g=com.apress.spring -a=simple-jpa-app --package-name=com.apress.spring -name=simple-jpa-app -x

Now, let’s take a look at the pom.xml file. As you likely know, you’ll need the spring-boot-starter-data-jpa starter pom. See Listing 7-6.

Listing 7-6. pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>com.apress.spring</groupId>
        <artifactId>simple-jpa-app</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>


        <name>simple-jpa-app</name>
        <description>Demo project for Spring Boot</description>


        <parent>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>1.3.2.RELEASE</version>
                <relativePath /> <!-- lookup parent from repository -->
        </parent>


        <properties>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                <java.version>1.8</java.version>
        </properties>


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


                <dependency>
                        <groupId>com.h2database</groupId>
                        <artifactId>h2</artifactId>
                        <scope>runtime</scope>
                </dependency>


                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>
                        <scope>test</scope>
                </dependency>
        </dependencies>


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


</project>

Listing 7-6 shows you the pom.xml, and as you guessed, it required the spring-boot-starter-data-jpaand the h2 dependencies. Next, you are going to keep using the domain class, so let’s take a look at the src/main/java/com/apress/spring/domain/Journal.java class. See Listing 7-7.

Listing 7-7. src/main/java/com/apress/spring/domain/Journal.java
package com.apress.spring.domain;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;


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


@Entity
public class Journal {


        @Id
        @GeneratedValue(strategy=GenerationType.AUTO)
        private Long id;
        private String title;
        private Date created;
        private String summary;


        @Transient
        private SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");


        public Journal(String title, String summary, String date) throws ParseException{
                this.title = title;
                this.summary = summary;
                this.created = format.parse(date);
        }


        Journal(){}

        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 Date getCreated() {
                return created;
        }


        public void setCreated(Date created) {
                this.created = created;
        }


        public String getSummary() {
                return summary;
        }


        public void setSummary(String summary) {
                this.summary = summary;
        }


        public String getCreatedAsShort(){
                return format.format(created);
        }


        public String toString(){
                StringBuilder value = new StringBuilder("* JournalEntry(");
                value.append("Id: ");
                value.append(id);
                value.append(",Title: ");
                value.append(title);
                value.append(",Summary: ");
                value.append(summary);
                value.append(",Created: ");
                value.append(getCreatedAsShort());
                value.append(")");
                return value.toString();
        }
}

Listing 7-7 shows the Journal.java class where it’s using the javax.persistence package classes and interfaces, including the @Entity, @Id, and @Transient annotations. All these annotations belong to the JPA specification and are going to be used to denote an entity (the class marked with @Entity annotation) that will be mapped to a table (in this case to a Journal table) and to its fields (all private fields with setters and getters, except for the one annotated with the @Transient annotation, which won’t be persistent to the database). The Long id property is marked with the @Id and @GeneratedValue annotations, making this field the primary key of the Journal table.

Next let’s see the service; you are still going to use a service that will insert data and find all the data in the database. See Listing 7-8.

Listing 7-8. src/main/java/com/apress/spring/service/JournalService.java
package com.apress.spring.service;

import java.text.ParseException;
import java.util.List;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


import com.apress.spring.domain.Journal;
import com.apress.spring.repository.JournalRepository;


@Service
public class JournalService {
     private static final Logger log = LoggerFactory.getLogger(JournalService.class);


     @Autowired
     JournalRepository repo;


     public void insertData() throws ParseException{
          log.info("> Inserting data...");
          repo.save(new Journal("Get to know Spring Boot","Today I will learn Spring Boot","01/01/2016"));
          repo.save(new Journal("Simple Spring Boot Project","I will do my first Spring Boot Project","01/02/2016"));
          repo.save(new Journal("Spring Boot Reading","Read more about Spring Boot","02/01/2016"));
          repo.save(new Journal("Spring Boot in the Cloud","Spring Boot using Cloud Foundry","03/01/2016"));
          log.info("> Done.");
     }


     public List<Journal> findAll(){
          return repo.findAll();
    }


}

Listing 7-8 shows you the service you will be using, so let’s examine its code:

  • @Service. This annotation marks the class as a stereotype that will be recognized as a bean by the Spring container, so it can be used, for example, with the @Autowired annotation.

  • JournalRepository. This instance is being auto-wired, but where is this JournalRepository interface? Don’t worry, you are going to see it in the next segment. For now, you need to think of it as an instance that has the knowledge of how to use the data, from connecting to the database, to accessing it for its usage.

  • insertData. This method will insert the data into the database. Note that there is no database or table creation; everything will be done by the abstraction of the JournalRepository.

  • findAll. This method will call the JournalRepository instance to get all the data from the database, returning a list of Journal instances.

Next, let’s see the JournalRepositoryinterface. See Listing 7-9.

Listing 7-9. src/main/java/com/apress/spring/repository/JournalRepository.java
package com.apress.spring.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.apress.spring.domain.Journal;

public interface JournalRepository extends JpaRepository<Journal, Long> { }

Listing 7-9 shows you the JournalRepository interface, but let’s dig into it. The JournalRepository interface extends from another interface, the JpaRepository. The JpaRepository uses generics and requires a marked class by the @Entity annotation and the Id as a java.io.Serializable object. In this case the entity is the Journal.java class and the ID is a Long class.

The JpaRepository interface looks like Listing 7-10.

Listing 7-10. <spring-data-jpa>/org/springframework/data/jpa/repository/JpaRepository.java
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {

        List<T> findAll();

        List<T> findAll(Sort sort);

        List<T> findAll(Iterable<ID> ids);

        <S extends T> List<S> save(Iterable<S> entities);

        void flush();

        <S extends T> S saveAndFlush(S entity);

        void deleteInBatch(Iterable<T> entities);

        void deleteAllInBatch();

        T getOne(ID id);
}

Listing 7-10 shows you the JpaRepository that belongs to the spring-data-jpa JAR, and it provides all those action methods that will run against the provided database. It’s important to note that you don’t need to implement any of these methods, you only need to extend from this interface. But if you take a closer look, you have additional behavior because it also extends from the PagingAndSortingRepository interface, giving you out-of-the-box extra features when you need them.

The Spring Framework and in this case the spring-data technology will be in charge of creating dynamic proxies that will implement these methods for you. This is because all these actions are very generic and repetitive, so you don’t have to implement them—you can let the spring-data do it on your behalf.

Now, let’s take a look at the main application. See Listing 7-11.

Listing 7-11. src/main/java/com/apress/spring/SimpleJpaAppApplication.java
package com.apress.spring;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;


import com.apress.spring.repository.JournalRepository;
import com.apress.spring.service.JournalService;


@SpringBootApplication
public class SimpleJpaAppApplication {
        private static final Logger log = LoggerFactory.getLogger(SimpleJpaAppApplication.class);


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


        @Bean
        CommandLineRunner start(JournalService service){
                return args -> {
                        log.info("@@ Inserting Data....");
                        service.insertData();
                        log.info("@@ findAll() call...");
                        service.findAll().forEach(entry -> log.info(entry.toString()));
                };
        }
}

Listing 7-11 shows you the main application, the SimpleJpaAppApplication.java class. Let’s examine its code:

  • @SpringBootApplication. This is the main annotation that will trigger the auto-configuration and will identify that you are using the spring-boot-starter-data-jpa. It will treat your application as a JPA app. It will also identify that you have declared the H2 in-memory database and will create the javax.sql.DataSource for you. It will be implemented with the H2 drivers and use the testdb database with the default credentials.

  • start. This method is marked as a Bean and will return a CommandLineRunner interface. This is another way to tell the Spring Boot app to run this method after the Spring application is started. In this example it’s using the Java 8 features to return a lambda where it’s using the JournalService instance (the start method’s parameter) to insert the data and then call the findAll method to get all the data from the database.

It’s cool to see that you don’t need to write any code for common database tasks—insert, update, and delete are covered—but what happens if you need to perform a very particular find? What if you need to find the journals that are after certain date, or you want to create a custom query with joins or stored procedures?

By extending to the JpaRepository, you can create “query” methods using the following a property naming convention. This provides extensibility in the behavior of the class. So for example, taking the Journal.java class, it contains the title property, so if you want to find all the titles that contain the word Spring, you can write a method like this:

public List<Journal> findByTitleContaining(String word);

This method will be translated to the SQL query: select * from JOURNAL where title like %?1%. Where the ?1 parameter will be the word Spring. So it would be something like this:

select * from journal where title like %Spring%

What if you need to look for all the journal entries after certain date? It is easy as create a method like so:

public List<Journal> findByCreatedAfter(Date date);

This method will be translated to the SQL query: select * from JOURNAL where created > ?1. Very easy. But what if you needed to run a particular query? For example, you can modify the findByTitleContaining method and write something equivalent like this:

@Query("select j from Journal j where j.title like %?1%")
List<Journal> findByCustomQuery(String word);

As you can see, you have many options. See Listing 7-12, which is a modified version of JournalRepository.java.

Listing 7-12. Modified Version of src/main/java/com/apress/spring/repository/JournalRepository.java
package com.apress.spring.repository;

import java.util.Date;
import java.util.List;


import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;


import com.apress.spring.domain.Journal;

public interface JournalRepository extends JpaRepository<Journal, Long> {

        List<Journal> findByCreatedAfter(Date date);

        @Query("select j from Journal j where j.title like %?1%")
        List<Journal> findByCustomQuery(String word);
}

Listing 7-12 shows you another version of the JpaRepository, which contains the “query” method declarations based on its properties (the journal class properties) and marks a method (with any name) with the @Query annotation. The @Query annotation accepts the JPQL syntax.

If you want to know more about the options for naming the “query” methods and the keywords that you can use, I recommend you look at the spring-data reference at http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation .

Another Spring Boot feature using the spring-data enables you to use the schema.sql and data.sql files (in the root of the classpath) to create the database and insert data. This feature is useful when you have a dump of data and must initialize the database. So instead of using a service to insert the data, you can write data.sql and remove the insertData call from your service. See Listing 7-13.

Listing 7-13. src/main/resources/data.sql
INSERT INTO JOURNAL(title,summary,created) VALUES('Get to know Spring Boot','Today I will learn Spring Boot','2016-01-02 00:00:00.00');
INSERT INTO JOURNAL(title,summary,created) VALUES('Simple Spring Boot Project','I will do my first Spring Boot project','2016-01-03 00:00:00.00');
INSERT INTO JOURNAL(title,summary,created) VALUES('Spring Boot Reading','Read more about Spring Boot','2016-02-02 00:00:00.00');
INSERT INTO JOURNAL(title,summary,created) VALUES('Spring Boot in the Cloud','Learn Spring Boot using Cloud Foundry','2016-02-05 00:00:00.00');

Listing 7-13 shows the SQL statements that will be detected by Spring Boot. Now you can remove insertData from your JournalService.java class and see the same effect.

Note

If you want to see the SQL statements that the JPA/Hibernate engine is executing, you can use the following property in the src/main/resources/application.properties file: spring.jpa.show-sql=true.

You can test this code as usual:

$ ./mvnw spring-boot:run

If you want to learn more about JPA, I recommend the Apress book entitled Pro JPA 2, Second Edition as well as the Pro Spring Fourth Edition and Spring Recipes Third Edition.

NoSQL Databases

NoSQL databases are another way to persist data, but in different way from the tabular relationships of the relational databases. There is already a classification system for these emergent NoSQL databases. You can find it based on its data model:

  • Column (Cassandra, HBase, etc.)

  • Document (CouchDB, MongoDB, etc.)

  • Key-Value (Redis, Riak, etc.)

  • Graph (Neo4J, Virtuoso, etc.)

  • Multi-Model (OrientDB, ArangoDB, etc.)

As you can see, you have many options. I think the most important kind of feature here nowadays is to find a database that is scalable and can handle millions of records easily.

This section covers the MongoDB, a NoSQL document database. You are going to use the previous journal application, but before you start, you need to make sure that you have the MongoDB server installed on your computer.

If you are using Mac/Linux with the brew command ( http://brew.sh/ ), execute the following command:

$ brew install mongodb

You can run it with this command:

$ mongod

Or you can install MongoDB by downloading it from the web site at https://www.mongodb.org/downloads#production and following the instructions.

Next, let’s start by creating a new folder and a new application:

$ mkdir simple-mongo-app
$ cd simple-mongo-app
$ spring init -d=data-mongodb -g=com.apress.spring -a=simple-mongo-app --package-name=com.apress.spring -name=simple-mongo-app -x

The mandatory question is which starter pom will you need for this example? The spring-boot-starter-data-mongodbpom will be required for this example. See Listing 7-14.

Listing 7-14. pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>com.apress.spring</groupId>
        <artifactId>simple-mongo-app</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>


        <name>simple-mongo-app</name>
        <description>Demo project for Spring Boot</description>


        <parent>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>1.3.2.RELEASE</version>
                <relativePath/> <!-- lookup parent from repository -->
        </parent>


        <properties>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                <java.version>1.8</java.version>
        </properties>


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


                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>
                        <scope>test</scope>
                </dependency>
        </dependencies>


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


</project>

Listing 7-14 shows you the pom.xmlfile with the spring-boot-starter-data-mongodb pom as a dependency. Next let’s look at the src/main/java/com/apress/spring/domain/Journal.java class. See Listing 7-15.

Listing 7-15. src/main/java/com/apress/spring/domain/Journal.java
package com.apress.spring.domain;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;


import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;


public class Journal {

        @Id
        private String id;
        private String title;
        private Date created;
        private String summary;


        @Transient
        private SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");


        public Journal(String title, String summary, String date) throws ParseException{
                this.title = title;
                this.summary = summary;
                this.created = format.parse(date);
        }


        Journal(){}

        public String getId() {
                return id;
        }


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


        public String getTitle() {
                return title;
        }


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


        public Date getCreated() {
                return created;
        }


        public void setCreated(Date created) {
                this.created = created;
        }


        public String getSummary() {
                return summary;
        }


        public void setSummary(String summary) {
                this.summary = summary;
        }


        public String getCreatedAsShort(){
                return format.format(created);
        }


        public String toString(){
                StringBuilder value = new StringBuilder("* JournalEntry(");
                value.append("Id: ");
                value.append(id);
                value.append(",Title: ");
                value.append(title);
                value.append(",Summary: ");
                value.append(summary);
                value.append(",Created: ");
                value.append(getCreatedAsShort());
                value.append(")");
                return value.toString();
        }
}

Listing 7-15 shows you the Journal.java class. Let’s review it:

  • This time it uses the org.springframework.data.annotation.Id and the org.springframework.data.annotation.Transient annotations , which are different from the javax.persistence package (because they belong to the JPA specification). They allow you to have unique key (with the @Id annotation) and the @Transient marked property won’t be persisted to the database.

  • Another important difference is the ID. In the previous code, it was a Long type, but now it’s String, which is required for the MongoDB. The rest of the code remains the same with its getters and setters.

Next, let’s take a look at the src/main/java/com/apress/spring/repository/JournalRepository.java interface, as shown in Listing 7-16.

Listing 7-16. src/main/java/com/apress/spring/repository/JournalRepository.java
package com.apress.spring.repository;

import java.util.List;

import org.springframework.data.mongodb.repository.MongoRepository;

import com.apress.spring.domain.Journal;

public interface JournalRepository extends MongoRepository<Journal, String> {

                public List<Journal> findByTitleLike(String word);
}

Listing 7-16 shows you the JournalRepository.java interface. Let’s review it:

  • Because this application is using the spring-data project and the spring-data-mongodb subproject libraries, you can extend it from the MongoRepository interface. This interface has common actions that run against the MongoDB. This interface needs a Document (in this case, the Journal class) that will contain an id and a String.

  • Again, because you are using the spring-data and spring-data-mongodb abstractions, you can have “query” methods. In this example it will find a title that contains a word. The “query” method findByTitleLike will be translated to MongoDB query syntax. Something like db.journal.find({"title": /.*?1*/}) or similar.

Now let’s take a look at the main application. See Listing 7-17.

Listing 7-17. src/main/java/com/apress/spring/SimpleMongoAppApplication.java
package com.apress.spring;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;


import com.apress.spring.domain.Journal;
import com.apress.spring.repository.JournalRepository;


@SpringBootApplication
public class SimpleMongoAppApplication {
      private static final Logger log = LoggerFactory.getLogger(SimpleMongoAppApplication.class);


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


      @Bean
      CommandLineRunner start(JournalRepository repo){
          return args -> {
               log.info("> Deleting existing data...");
               repo.deleteAll();


               log.info("> Inserting new data...");
               repo.save(new Journal("Get to know Spring Boot","Today I will learn Spring Boot","01/02/2016"));
                       repo.save(new Journal("Simple Spring Boot Project","I will do my first Spring Boot Project","01/03/2016"));
                       repo.save(new Journal("Spring Boot Reading","Read more about Spring Boot","02/02/2016"));
                       repo.save(new Journal("Spring Boot in the Cloud","Spring Boot using Cloud Foundry","03/01/2016"));


        log.info("> Getting all data...");
        repo.findAll().forEach(entry -> log.info(entry.toString()));


        log.info("> Getting data using like...");
        repo.findByTitleLike("Cloud").forEach(entry -> log.info(entry.toString()));


          };
      }
}

Listing 7-17 shows you the main application. Does this app look familiar? It’s not that different from its previous “relatives”. Here, it’s using the start method, which will be called after the Spring Boot app starts. It will delete all existing data, it will insert them, and then it will use some of the finder methods.

You can run it as usual, using the Maven wrapper or the global Maven installation:

$ ./mvnw spring-boot:run

You should see the following output:

> Deleting existing data...
> Inserting new data...
> Getting all data...
* JournalEntry(Id: 56b192d377c83f89cae51f5f,Title: Get to know Spring Boot,Summary: Today I will learn Spring Boot,Created: 01/02/2016)
* JournalEntry(Id: 56b192d377c83f89cae51f60,Title: Simple Spring Boot Project,Summary: I will do my first Spring Boot Project,Created: 01/03/2016)
* JournalEntry(Id: 56b192d377c83f89cae51f61,Title: Spring Boot Reading,Summary: Read more about Spring Boot,Created: 02/02/2016)
* JournalEntry(Id: 56b192d377c83f89cae51f62,Title: Spring Boot in the Cloud,Summary: Spring Boot using Cloud Foundry,Created: 03/01/2016)
> Getting data using like...
* JournalEntry(Id: 56b192d377c83f89cae51f62,Title: Spring Boot in the Cloud,Summary: Spring Boot using Cloud Foundry,Created: 03/01/2016)

If you want to see the actual data in your MongoDB server, you can open a terminal and execute the following commands:

$ mongo
MongoDB shell version: 3.2.1
connecting to: test
> show collections
blog
journal
system.indexes
> db.journal.find()
{ "_id" : ObjectId("56b192d377c83f89cae51f5f"), "_class" : "com.apress.spring.domain.Journal", "title" : "Get to know Spring Boot", "created" : ISODate("2016-01-02T07:00:00Z"), "summary" : "Today I will learn Spring Boot" }
{ "_id" : ObjectId("56b192d377c83f89cae51f60"), "_class" : "com.apress.spring.domain.Journal", "title" : "Simple Spring Boot Project", "created" : ISODate("2016-01-03T07:00:00Z"), "summary" : "I will do my first Spring Boot Project" }
{ "_id" : ObjectId("56b192d377c83f89cae51f61"), "_class" : "com.apress.spring.domain.Journal", "title" : "Spring Boot Reading", "created" : ISODate("2016-02-02T07:00:00Z"), "summary" : "Read more about Spring Boot" }
{ "_id" : ObjectId("56b192d377c83f89cae51f62"), "_class" : "com.apress.spring.domain.Journal", "title" : "Spring Boot in the Cloud", "created" : ISODate("2016-03-01T07:00:00Z"), "summary" : "Spring Boot using Cloud Foundry" }

When you use the mongo client shell, you will be connected directly to the test database, which is what Spring Boot will use as main database to create the document collection. In this case, it’s the name of the Java class: journal. Then you can use the db.journal.find() query to get all the data.

Spring Boot allows you to define the name of your database if you don’t want to use the default one. You only need to add the following property to the src/main/resources/application.properties file:

spring.data.mongodb.database=myjournal

Then the MongoRepositorywill create the database using the myjournal name and will create the journal collection as well.

You can take a peek at the MongoDB server by using its client. You can see the database, the collection, and the data with the following commands:

$ mongo
MongoDB shell version: 3.2.3
connecting to: test
> show databases;
local        0.078GB
myjournal    0.078GB
test         0.203GB
> use myjournal
switched to db myjournal
> show collections
journal
system.indexes
> db.journal.find()
{ "_id" : ObjectId("56b0ef2d77c8a628197f0aa4"), "_class" : "com.apress.spring.domain.Journal", "title" : "Get to know Spring Boot", "created" : ISODate("2016-01-02T07:00:00Z"), "summary" : "Today I will learn Spring Boot" }
{ "_id" : ObjectId("56b0ef2d77c8a628197f0aa5"), "_class" : "com.apress.spring.domain.Journal", "title" : "Simple Spring Boot Project", "created" : ISODate("2016-01-03T07:00:00Z"), "summary" : "I will do my first Spring Boot Project" }
{ "_id" : ObjectId("56b0ef2d77c8a628197f0aa6"), "_class" : "com.apress.spring.domain.Journal", "title" : "Spring Boot Reading", "created" : ISODate("2016-02-02T07:00:00Z"), "summary" : "Read more about Spring Boot" }
{ "_id" : ObjectId("56b0ef2d77c8a628197f0aa7"), "_class" : "com.apress.spring.domain.Journal", "title" : "Spring Boot in the Cloud", "created" : ISODate("2016-03-01T07:00:00Z"), "summary" : "Spring Boot using Cloud Foundry" }
>

This feature (the properties specified in the application.properties file) works not only for Mongo but for every spring-data application. You can get more info about the right property setting at https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html .

Summary

This chapter discussed relational and NoSQL databases and explained how the Spring Data project and subprojects define several helpers and abstraction classes that will help you have data access regardless of the database engine you use.

I started by showing you the JdbcTemplate that is based on the template design pattern. You saw execute methods that allow you to interact with the relational database. The relational database examples used the H2 in-memory database, which is a very good technology for prototyping and testing purposes.

The chapter showed you the H2 web console by adding the spring-boot-starter-web pom and setting the spring.h2.console.enabled=true property to true. The chapter showed you the JPA and explained how you can avoid writing common CRUD (Create, Read, Update, and Delete) tasks by creating an interface that extends from the JpaRepository. You also learned that you can have “query” methods to support more tasks for your data applications.

You saw the NoSQL document database, the MongoDB, and learned how you can use the MongoRepository, which is very similar to the regular JPA.

In the next chapter, you are going to start using all the data code from this chapter because you are going to create web applications with Spring Boot.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.34.197