Setting up a data repository service
Connecting to a database and then executing good old SQL—while simplistic and straightforward—is not the most convenient way to operate on the data, map it in a set of domain objects, and manipulate the relational content. This is why multiple frameworks emerged in order to aid you with mapping the data from tables into objects, better known as Object Relational Mapping. The most notable example of such a framework is Hibernate.
In the previous example, we covered how to set up a connection to a database and configure the settings for the username, password, which driver to use, and so on. In this recipe, we will enhance our application by adding a few entity objects that define the structure of the data in the database and a CrudRepository
interface to access the data.
As our application is a book tracking catalogue, the obvious domain objects would be the Book
, Author
, Reviewers
, and Publisher
.
How to do it…
- Create a new package folder named
entity
under thesrc/main/java/org/test/bookpub
directory from the root of our project. - In this newly created package, create a new class named
Book
with the following content:@Entity public class Book { @Id @GeneratedValue private Long id; private String isbn; private String title; private String description; @ManyToOne private Author author; @ManyToOne private Publisher publisher; @ManyToMany private List<Reviewers> reviewers; protected Book() {} public Book(String isbn, String title, Author author, Publisher publisher) { this.isbn= isbn; this.title = title; this.author= author; this.publisher= publisher; } //Skipping getters and setters to save space, but we do need them }
- As any book should have an author and a publisher, and ideally some reviewers, we need to create these entity objects as well. Let's start by creating an
Author
entity class under the same directory as ourBook
on, as follows:@Entity public class Author { @Id @GeneratedValue private Long id; private String firstName; private String lastName; @OneToMany(mappedBy = "author") private List<Book> books; protected Author() {} public Author(String firstName, String lastName) {...} //Skipping implementation to save space, but we do need it all }
- Similarly, we will create the
Publisher
andReviewer
classes, as shown in the following code:@Entity public class Publisher { @Id @GeneratedValue private Long id; private String name; @OneToMany(mappedBy = "publisher") private List<Book> books; protected Publisher() {} public Publisher(String name) {...} } @Entity public class Reviewer { @Id @GeneratedValue private Long id; private String firstName; private String lastName; protected Reviewer() {} public Reviewer(String firstName, String lastName) { //Skipping implementation to save space } }
- Now, we will create our
BookRepository
interface by extending Spring'sCrudRepository
under thesrc/main/java/org/test/bookpub/repository
package, as follows:@Repository public interface BookRepository extends CrudRepository<Book, Long> { public Book findBookByIsbn(String isbn); }
- Finally, let's modify our
StartupRunner
in order to print the number of books in our collection instead of some random DataSource string by auto-wiring a newly createdBookRepository
and printing the result of a.count()
call to the log, as follows:public class StartupRunner implements CommandLineRunner { @Autowired private BookRepository bookRepository; public void run(String... args) throws Exception { logger.info("Number of books: " + bookRepository.count()); } }
How it works…
As you have probably noticed, we didn't write a single line of SQL or even mentioned anything about database connections, building queries, or things like that. The only hint that we are dealing with the database-backed data that we have in our code are the class and field annotations: @Entity
, @Repository
, @Id
, @GeneratedValue
, and @ManyToOne
along with @ManyToMany
and @OneToMany
. These annotations, which are a part of the Java Persistance API, along with the extension of the CrudRepository
interface are our ways of communicating with Spring about the need to map our objects to the appropriate tables and fields in the database and provide us with the programmatic ability to interact with this data.
Let's go through the following annotations:
@Entity
indicates that the annotated class should be mapped to a database table. The name of the table will be derived from the name of the class but it can be configured, if needed. It is important to note that every entity class should have a defaultprotected
constructor, which is needed for automated instantiation and Hibernate interactions.@Repository
indicates that the interface is intended to provide you with the access and manipulation of data for a database. It also serves as an indication to Spring during the component scan that this instance should be created as a bean that will be available for use and injection into other beans in the application.- The
CrudRepository
interface defines the basic common methods to read, create, update, and delete data from a data repository. The extra methods that we will define in ourBookRepository
extension,public Book findBookByIsbn(String isbn)
, indicate that Spring JPA should automatically translate the call to this method to a SQL finder query selecting a Book by its ISBN field. This is a convention-named mapping that translates the method name into a SQL query. It can be a very powerful ally, allowing you to build queries such asfindByNameIgnoringCase(String name)
and others. - The
@Id
and@GeneratedValue
annotations provide you with an indication that an annotated field should be mapped to a primary key column in the database and the value for this field should be generated, instead of being explicitly entered. - The
@ManyToOne
and@ManyToMany
annotations define the relational field associations that refer to the data stored in the other tables. In our case, multipleBooks
belong to oneAuthor
and manyReviewers
review multipleBooks
. ThemappedBy
attribute in@OneToMany
annotation declaration defines a reverse association mapping. It indicates to Hibernate that the mapping source of truth is defined in theBook
class, in theauthor
orpublisher
fields. TheBooks
references from withinAuthor
andPublisher
classes are merely reverse associations.Tip
For more information about all the vast capabilities of Spring Data, visit http://docs.spring.io/spring-data/data-commons/docs/current/reference/html/.