Übung Book Service Teil 1
Setup Projekt
Analog dem jumpstart Projekt erstellen wir ein Spring Boot Projekt mit Rest Support. Die Screenshots zeigen die Variante mit Eclipse, Spring Initializer und Maven:
JPA Entity Book
Der Book Service liest Bücher aus der Datenbank. Das Modell der Datenbank soll über die JPA Entity Book wie folgt definiert werden:
package ch.std.book.jpa;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "isbn", unique = true, length = 32)
private String isbn;
@Column(name = "title", length = 256)
private String title;
@Column(name = "description", length = 1024)
private String description;
@Column(name = "publisher", length = 256)
private String publisher;
public Book() {
}
public Book(String isbn) {
this.id = null;
this.isbn = isbn;
this.title = null;
this.description = null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((isbn == null) ? 0 : isbn.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Book other = (Book) obj;
if (isbn == null) {
if (other.isbn != null)
return false;
} else if (!isbn.equals(other.isbn))
return false;
return true;
}
public Long getId() {
return id;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public String getPublisher() {
return publisher;
}
public void setPublisher(String publisher) {
this.publisher = publisher;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Book [id=" + id + ", isbn=" + isbn + ", title=" + title + ", description=" + description
+ ", publisher=" + publisher + "]";
}
}
Integrieren Sie die Klasse Book in Ihr Projekt. In Eclipse können Sie den Programmcode direkt aus dem Clipboard via Paste hineinkopieren.Für den JPA Support benötigen wir noch das folgende Spring Starter Package als Maven Dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
mysql database
Wir arbeiten mit dieser Übung mit einer mysql Datenbank. Wir setzen die Datenbank mit dem folgenden Schema und Credentials auf:
mysql –u root –p
create database jumpstart;
create user 'jumpstart'@'%' identified by 'jumpstart';
grant all on jumpstart.* to 'jumpstart'@'%';
Die notwendigen Treiber für mysql definieren wir via Maven Dependency:<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
application.properties
Damit unsere Anwendung mit der mysql Datenbank arbeitet, definieren wir die application.properties:
spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.url=jdbc:mysql://localhost:3306/jumpstart?serverTimezone=UTC
spring.datasource.username=jumpstart
spring.datasource.password=jumpstart
spring.jpa.hibernate.dialect-platform=org.hibernate.dialect.MySQL5InnoDBDialect
Mit der create-drop ddl-auto Anweisung erstellen wir das Schema mit jedem Start vollständig neu. In der Praxis macht die create-drop Option keinen Sinn, für unsere Übung ist es aber hilfreich.
Das SQL Debugging belassen wir wie bisher und erweitern es noch wie folgt:
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.type=trace
Damit zeigen wir alle angewendeten SQL Statements im Log schön formatiert an.Verify Startup und DB Schema
In der Praxis sollte man die Aufgabe in kleine einfach verifzierbare Steps unterteilen. Wir starten deshalb die Spring Boot Appliktion und verifzieren das Startup Log (ein Auszug):
...
:: Spring Boot :: (v2.3.5.RELEASE)
2020-11-08 11:55:04.833 INFO 4385 --- [ main] ch.std.book.BookserviceApplication : Starting BookserviceApplication on ubuntu with PID 4385 (/home/user/sbrs2/bookservice/target/classes started by user in /home/user/sbrs2/bookservice)
2020-11-08 11:55:04.836 INFO 4385 --- [ main] ch.std.book.BookserviceApplication : No active profile set, falling back to default profiles: default
2020-11-08 11:55:05.583 INFO 4385 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFERRED mode.
2020-11-08 11:55:05.603 INFO 4385 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 12ms. Found 0 JPA repository interfaces.
2020-11-08 11:55:06.411 INFO 4385 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-11-08 11:55:06.424 INFO 4385 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-11-08 11:55:06.424 INFO 4385 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.39]
2020-11-08 11:55:06.542 INFO 4385 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-11-08 11:55:06.543 INFO 4385 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1661 ms
2020-11-08 11:55:06.872 INFO 4385 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-11-08 11:55:06.945 INFO 4385 --- [ task-1] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-11-08 11:55:06.988 INFO 4385 --- [ task-1] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.22.Final
2020-11-08 11:55:07.114 INFO 4385 --- [ task-1] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-11-08 11:55:07.214 INFO 4385 --- [ task-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-11-08 11:55:07.358 INFO 4385 --- [ task-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-11-08 11:55:07.377 INFO 4385 --- [ task-1] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL55Dialect
2020-11-08 11:55:07.919 DEBUG 4385 --- [ task-1] org.hibernate.SQL :
drop table if exists book
Hibernate:
drop table if exists book
2020-11-08 11:55:07.943 DEBUG 4385 --- [ task-1] org.hibernate.SQL :
create table book (
id bigint not null auto_increment,
description varchar(1024),
isbn varchar(32),
publisher varchar(256),
title varchar(256),
primary key (id)
) engine=InnoDB
Hibernate:
create table book (
id bigint not null auto_increment,
description varchar(1024),
isbn varchar(32),
publisher varchar(256),
title varchar(256),
primary key (id)
) engine=InnoDB
2020-11-08 11:55:07.948 DEBUG 4385 --- [ task-1] org.hibernate.SQL :
alter table book
add constraint UK_ehpdfjpu1jm3hijhj4mm0hx9h unique (isbn)
Hibernate:
alter table book
add constraint UK_ehpdfjpu1jm3hijhj4mm0hx9h unique (isbn)
2020-11-08 11:55:07.959 INFO 4385 --- [ task-1] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-11-08 11:55:07.969 INFO 4385 --- [ task-1] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-11-08 11:55:08.307 WARN 4385 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2020-11-08 11:55:08.957 INFO 4385 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-11-08 11:55:08.959 INFO 4385 --- [ main] DeferredRepositoryInitializationListener : Triggering deferred initialization of Spring Data repositories…
2020-11-08 11:55:08.959 INFO 4385 --- [ main] DeferredRepositoryInitializationListener : Spring Data repositories initialized!
2020-11-08 11:55:08.969 INFO 4385 --- [ main] ch.std.book.BookserviceApplication : Started BookserviceApplication in 4.524 seconds (JVM running for 5.385)
Weiter prüfen wir ob das DB Schema mit der Tabelle Book korrekt erstellt wurde:sudo mysql -u root -p
show databases;
use jumpstart;
describe book;
Das book Schema sollte wie folgt angezeigt werden:MariaDB [jumpstart]> describe book;
+-------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| description | varchar(1024) | YES | | NULL | |
| isbn | varchar(32) | YES | UNI | NULL | |
| publisher | varchar(256) | YES | | NULL | |
| title | varchar(256) | YES | | NULL | |
+-------------+---------------+------+-----+---------+----------------+
PostConstruct
Nach dem Startup möchten wir Dateien aus einer CSV Datei einlesen. In einem ersten Schritt speichern wir die Datei books.csv im main resources Verzeichnis:
Das Einlesen der CSV Datei programmieren wir mit den folgenden Java Klassen:
Integrieren Sie die Java Klassen in Ihr Projekt. Für das Lesen der CSV Datei verwenden wir die Jackson Dataformat Library. Solche bedingt die folgende Maven Dependency:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
</dependency>
Das Laden der book csv Datei wird über @ConditionalProperties und damit das Property book.scheduler.initial aktiviert. Definieren Sie für die Aktivierung das folgende Property in der Datei application.properties:
book.scheduler.initial.delay=1000
book.scheduler.initial.enabled=true
Das initiale Delay von 1s ist wichtig um nach dem Drop Table ausgeführt zu werden.
Final Run
Nun folgt der finale Run mit dem erneuten Start der Applikation. Die Bücher sollten mit dem Startup geladen werden (siehe Log):
...
2020-11-08 12:31:31.724 INFO 6248 --- [pool-1-thread-1] ch.std.book.tasks.impl.InitialTask : IntialTask.run start
2020-11-08 12:31:46.537 INFO 6248 --- [pool-1-thread-1] ch.std.book.tasks.impl.InitialTask : InitialTask.run, insert book done using key = 1
2020-11-08 12:31:50.960 INFO 6248 --- [pool-1-thread-1] ch.std.book.tasks.impl.InitialTask : InitialTask.run, insert book done using key = 2
2020-11-08 12:31:51.007 INFO 6248 --- [pool-1-thread-1] ch.std.book.tasks.impl.InitialTask : InitialTask.run, insert book done using key = 3
2020-11-08 12:31:51.064 INFO 6248 --- [pool-1-thread-1] ch.std.book.tasks.impl.InitialTask : InitialTask.run, insert book done using key = 4
2020-11-08 12:31:51.121 INFO 6248 --- [pool-1-thread-1] ch.std.book.tasks.impl.InitialTask : InitialTask.run, insert book done using key = 5
2020-11-08 12:31:51.181 INFO 6248 --- [pool-1-thread-1] ch.std.book.tasks.impl.InitialTask : InitialTask.run, insert book done using key = 6
2020-11-08 12:31:51.183 INFO 6248 --- [pool-1-thread-1] ch.std.book.tasks.impl.InitialTask : IntialTask.run done
Verifzieren Sie via mysql Konsole ob die Bücher auch wirklich in der Datenbank gespeichert sind:
MariaDB [jumpstart]> select id, title from book;
+----+---------------------------------------------------------+
| id | title |
+----+---------------------------------------------------------+
| 1 | Spring Boot in Action |
| 2 | Cloud Native Java |
| 3 | Spring Microservices in Action |
| 4 | Spring Boot 2: Moderne Softwareentwicklung mit Spring 5 |
| 5 | Learning Spring Boot 2.0 |
| 6 | Mastering Spring Boot 2.0 |
+----+---------------------------------------------------------+
6 rows in set (0.001 sec)
Damit ist dieser 1. Teil der Book Service Übung beendet.
Lösung
Eine mögliche Lösung finden Sie als Maven Projekt bookservice1.zip