This document provides an introduction to Spring Boot. It discusses prerequisites like Java and Maven. It introduces key Spring Boot concepts like inversion of control and dependency injection. It explains what Spring Boot is and how it makes it easier to create Java applications. Spring Boot starters are explained, which add functionality through Maven dependencies. The document walks through creating a basic web application with Spring Initializr, and adding a test and controller for a "/hello" endpoint.
2. Change Log
2023-10-09 First version
2023-10-11 Added Section 5 “Library (More Complex Example using Spring MVC Web Controller, JPA,
Repositories, H2)”
2023-10-18 Using external services, first steps in GitLab
2
CSU33012 Goetz Botterweck
4. References
Online Courses
(Bowman 2019) Bowman, Mary Ellen. Creating Your First Spring Boot Microservice, 1h
48min, Linked-in Learning, 2019.
https://www.linkedin.com/learning/creating-your-first-spring-boot-microservice/
4
CSU33012 Goetz Botterweck
7. Prerequisites
• Java
• Version 17
• java --version
should print a version number like 17.x.x
• Maven
• mvn --version
should print a version number like 3.9.x
• IDE
• IntelliJ IDEA Ultimate or
• Visual Studio Code
• Spring Tools for VSCode, https://spring.io/tools
CSU33012 Goetz Botterweck 7
8. Also useful
• Tools for Working with REST APIs
• postman
• https://www.postman.com/downloads/
• mostly app, there is also online version at https://www.postman.com/
• httpie
• https://httpie.io/
• mostly for terminal, there is also beta version of app
• Account on the local GitLab installation
• http://gitlab.scss.tcd.ie/
CSU33012 Goetz Botterweck 8
10. Apache Maven
• Download, https://maven.apache.org/download.cgi
• Install, https://maven.apache.org/install.html
• Unzip
• Make sure that Maven’s bin directory is in your path
• mvn --version
should print a version number like 3.9.x
• Apache in 5 minutes (see below)
https://maven.apache.org/guides/getting-started/maven-in-five-
minutes.html
CSU33012 Goetz Botterweck 10
Image: https://en.m.wikipedia.org/wiki/File:Apache_Maven_logo.svg
11. Apache Maven
• Tool to build and manage software projects
• Key Features
• Dependency Management
• Building Projects
• Plugin-based Architecture
• Benefits
• Standardized Build Process
• Consistent Project Structures
• Alternative: Gradle
CSU33012 Goetz Botterweck 11
12. Maven Standard Directory Structure
CSU33012 Goetz Botterweck 12
https://www.baeldung.com/maven-directory-structure
13. Maven in 5min
CSU33012 Goetz Botterweck 13
https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -
DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -
DinteractiveMode=false
If running in Windows Powershell, Maven will raise a false error
(pom.xml missing). Use cmd.exe or enclose parameters in quotes.
https://stackoverflow.com/questions/16348459/error-the-goal-
you-specified-requires-a-project-to-execute-but-there-is-no-pom
14. Maven in 5min
CSU33012 Goetz Botterweck 14
https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html
cd my-app
mvn package
16. Maven in 5min
CSU33012 Goetz Botterweck 16
https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html
java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
Later in Spring Boot you can start the jar directly
without classpath, somewhat like
java -jar target/my-app-1.0-SNAPSHOT.jar
Here that does not work.
18. Inversion of Control
Previously
• Main program (our program)
• Controls flow of data and
execution
• Calls components
• Sets up references between
components
CSU33012 Goetz Botterweck 18
With Inversion of Control
• Framework (or Container)
• Controls flow of data and
execution
• Is responsible for„plumbing“
everything together
• Our Program
• We only have to specify what we
need
• In Spring Boot
• through annotating parts of a Java
program with tags like
@Service
@SpringBootApplication
• Uses Dependency Injection
19. Key Concepts in Inversion of Control
• Inversion of Control (IoC)
• Framework (a.k.a. the container) is responsible for handling the start-up,
including the instantiation of components (Beans), connecting them
• Dependency Injection
• Mechanism to implement IoC
• Component does not set up dependency, instead the framework/container
„injects“ them into the component.
• Application Context
• Spring Boot‘s version of framework/container
CSU33012 Goetz Botterweck 19
20. Inversion of Control Examples
CSU33012 Goetz Botterweck 20
package ie.tcd.scss.learningspring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class LearningSpringApplication {
public static void main(String[] args) {
SpringApplication.run(LearningSpringApplication.class, args);
}
}
Upon starting the
application Spring Boot will
scan for annotations and
instantiate and connect the
necessary components.
21. Inversion of Control Examples
CSU33012 Goetz Botterweck 21
(Turnquist, p. 6-7)
...
class BookExample {
@Bean
public BookRepository bookRepository(DataSource dataSource) {
return new BookRepository();
}
...
}
• bookRepository needs a DataSource.
• Application context needs to provide one, either has it or will go create one and return it.
• Application context is responsible for management during execution
• bookRepository executes its code while referencing the app context’s DataSource.
• BookRepository is registered in the application context under the name
bookRepository.
22. 1.4 What is Spring Boot?
CSU33012 Goetz Botterweck 22
23. Spring Boot
• Open-source, Java-based framework
• “Beginners version” of Spring
(based on Spring, makes it easier to get started)
• Conventions over Configuration
• Autoconfiguration
• Upon start application examines the parts of that application
• Adds additional Spring beans to application context
• Standalone
• No deployment, instead web server embedded in the application
CSU33012 Goetz Botterweck 23
25. Spring Boot Starters
CSU33012 Goetz Botterweck 25
https://www.baeldung.com/spring-boot-starters
<?xml version="1.0" encoding="UTF-8"?>
<project …>
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
...
</project>
Adding Starter to pom.xml as dependency … … ensures additional libraries are
included and annotations are available
These are what is added to your project,
when you select „Dependencies“ in
Spring Initializr (more later)
26. Spring Boot Starters – Examples
• Spring Web – applications that offer REST-based APIs, uses Spring
MVC, uses Apache Tomcat as the default embedded container.
• Spring WebFlux – alternative to Spring Web, based on reactive
streams
• See examples in (Larsson 2023)
• See https://www.baeldung.com/spring-mvc-async-vs-webflux
• Spring Data JPA – accessing data via Java Persistence API (JPA)
• Spring Test – testing your application
• Spring Mail – sending emails
Source: https://www.baeldung.com/spring-boot-starters
CSU33012 Goetz Botterweck 26
27. 2. Creating a Web
Application with Spring
Boot
CSU33012 Goetz Botterweck 27
28. So, what do we need?
• We want to setup a basic project and its file structure
• Based on Java, Maven, Spring Boot
• We want to build a simple web application
(that responds to a HTTP request with a response)
/hello → Hello World!
CSU33012 Goetz Botterweck 28
30. Spring Initializr
CSU33012 Goetz Botterweck 30
• Fast way to set up a Spring Boot
project
• Build management
• Maven
• Gradle
• Language
• Java
• Kotlin
• Groovy
• Spring Boot Version
• Project Metadata
• Packaging
• Java Version
• Dependencies (to add
functionality)
31. Spring Initializr
CSU33012 Goetz Botterweck 31
• Fast way to set up a Spring Boot
project
• Build management
• Maven
• Gradle
• Language
• Java
• Kotlin
• Groovy
• Spring Boot Version
• Project Metadata
• Packaging
• Java Version
• Dependencies (to add
functionality)
Spring Initializr
API
start.spring.io
Website
Spring Initializr
Support in
IntelliJ IDEA
„Spring Initializr
Java Support“
vscode-spring-initializr
In VS Code
32. Spring Initializr
CSU33012 Goetz Botterweck 32
• http://start.spring.io
• Project = Maven
• Language = Java
• Spring Boot = default (3.x)
• Group = ie.tcd.scss
• Artifact = learningspring
• Packaging = Jar
• Java = 17
• Dependencies
• Spring Web
• Generate
• Download and unpack
• Open project in your IDE
44. • Go to src/test/java/ie.tcd.scss.learningspring
• Create new class HelloControllerTest
• Add content on next slide
• Right click src/test/java > Run all tests (Ctrl-Shift-F10)
• Inspect test results
CSU33012 Goetz Botterweck 44
49. Copying Text from Slides PDF
CSU33012 Goetz Botterweck 49
If text copies from PDF looks garbled
Edit > Find > Replace (Ctrl + R)
Search for: u000B (or select the weird VT character)
Replace with: n
Activate regular expressions .* (Alt + X)
Replace all (Alt + A)
67. 4. Music Styles
(Storing and Retrieving Info with Spring MVC Web
Controller)
CSU33012 Goetz Botterweck 67
68. Music Styles
• Can store mapping artist->genre
• The Weeknd → R&B
• Eminem → Hip Hop
• Michael Jackson → Pop
• Adele → Pop
• Can retrieve info on given artist
• In: Eminem
Out: Hip Hop
• Can list all artists for a given genre
• In: Pop
Out: Michael Jackson, Adele
CSU33012 Goetz Botterweck 68
69. class ArtistInfo
package com.example.musicstyles;
public class ArtistInfo {
private String artist;
private String musicStyle;
public ArtistInfo(String artist, String musicStyle) {
this.artist = artist;
this.musicStyle = musicStyle;
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public String getMusicStyle() {
return musicStyle;
}
public void setMusicStyle(String musicStyle) {
this.musicStyle = musicStyle;
}
}
CSU33012 Goetz Botterweck 69
Image: https://unsplash.com/photos/Km5J-KCP1Mw
70. Spring Initializr
CSU33012 Goetz Botterweck 70
• http://start.spring.io
• Project = Maven
• Language = Java
• Spring Boot = default (3.x)
• Group = ie.tcd.scss
• Artifact = musicstyles
• Packaging = Jar
• Java = 17
• Dependencies
• Spring Web
• Generate
• Download and unpack
• Open project in your IDE
82. class MusicstylesController (1 of 2)
CSU33012 Goetz Botterweck 82
package ie.tcd.scss.musicstyles;
import ...
@RestController
@RequestMapping("/api")
public class MusicstylesController {
private final Map<String, String> artistStyleMap = new ConcurrentHashMap<>();
@PostMapping("/artists")
public ResponseEntity<String> addArtist(@RequestBody ArtistInfo artistInfo) {
artistStyleMap.put(artistInfo.getArtist(), artistInfo.getMusicStyle());
return ResponseEntity.ok("Artist added successfully!");
}
@GetMapping("/artists/{artistName}")
public ResponseEntity<String> getMusicStyle(@PathVariable String artistName) {
String musicStyle = artistStyleMap.get(artistName);
if (musicStyle == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Artist not found!");
}
return ResponseEntity.ok(musicStyle);
}
...
}
83. class MusicstylesController (1 of 2)
CSU33012 Goetz Botterweck 83
package ie.tcd.scss.musicstyles;
import ...
@RestController
@RequestMapping("/api")
public class MusicstylesController {
private final Map<String, String> artistStyleMap = new ConcurrentHashMap<>();
@PostMapping("/artists")
public ResponseEntity<String> addArtist(@RequestBody ArtistInfo artistInfo) {
artistStyleMap.put(artistInfo.getArtist(), artistInfo.getMusicStyle());
return ResponseEntity.ok("Artist added successfully!");
}
@GetMapping("/artists/{artistName}")
public ResponseEntity<String> getMusicStyle(@PathVariable String artistName) {
String musicStyle = artistStyleMap.get(artistName);
if (musicStyle == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Artist not found!");
}
return ResponseEntity.ok(musicStyle);
}
...
}
URL path pattern with path
variable “artistName”
84. class MusicstylesController (2 of 2)
CSU33012 Goetz Botterweck 84
package ie.tcd.scss.musicstyles;
import ...
@RestController
@RequestMapping("/api")
public class MusicstylesController {
...
@GetMapping("/musicstyles/{musicStyleName}/artists")
public ResponseEntity<List<String>> getArtists(@PathVariable String musicStyleName) {
List<String> artists = artistStyleMap.entrySet().stream()
.filter(entry -> entry.getValue().equalsIgnoreCase(musicStyleName))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
if (artists.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Collections.emptyList());
}
return ResponseEntity.ok(artists);
}
}
85. class MusicstylesController (2 of 2)
CSU33012 Goetz Botterweck 85
package ie.tcd.scss.musicstyles;
import ...
@RestController
@RequestMapping("/api")
public class MusicstylesController {
...
@GetMapping("/musicstyles/{musicStyleName}/artists")
public ResponseEntity<List<String>> getArtists(@PathVariable String musicStyleName) {
List<String> artists = artistStyleMap.entrySet().stream()
.filter(entry -> entry.getValue().equalsIgnoreCase(musicStyleName))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
if (artists.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Collections.emptyList());
}
return ResponseEntity.ok(artists);
}
}
Converts artistStyleMap into
stream of its entries
Filter for entries where the
value is <musicStyleName>
ignoring case
Transform each remaining
entry into its key (the artist
name in this case)
Collect the results into a
List<Strings>
Lambda
expression
URL path pattern with path
variable “musicStyleName”
86. class MusicstylesController (full source)
CSU33012 Goetz Botterweck 86
package ie.tcd.scss.musicstyles;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api")
public class MusicstylesController {
private final Map<String, String> artistStyleMap = new ConcurrentHashMap<>();
@PostMapping("/artists")
public ResponseEntity<String> addArtist(@RequestBody ArtistInfo artistInfo) {
artistStyleMap.put(artistInfo.getArtist(), artistInfo.getMusicStyle());
return ResponseEntity.ok("Artist added successfully!");
}
@GetMapping("/artists/{artistName}")
public ResponseEntity<String> getMusicStyle(@PathVariable String artistName) {
String musicStyle = artistStyleMap.get(artistName);
if (musicStyle == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Artist not found!");
}
return ResponseEntity.ok(musicStyle);
}
@GetMapping("/musicstyles/{musicStyleName}/artists")
public ResponseEntity<List<String>> getArtists(@PathVariable String musicStyleName) {
List<String> artists = artistStyleMap.entrySet().stream()
.filter(entry -> entry.getValue().equalsIgnoreCase(musicStyleName))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
if (artists.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Collections.emptyList());
}
return ResponseEntity.ok(artists);
}
}
99. 5. Library
(More Complex Example using Spring MVC Web
Controller, JPA, Repositories, H2)
CSU33012 Goetz Botterweck 99
100. 5.1 Setting up the Initial
Project
CSU33012 Goetz Botterweck 100
101. • Based on „Explore California“ in (Bowman 2019)
• Simplified and added explanations
• Updated to Spring Boot 3
• Changed namespaces
javax.somepackage → jakarta.somepackage
CSU33012 Goetz Botterweck 101
103. Spring Initializr
CSU33012 Goetz Botterweck 103
• http://start.spring.io
• Project = Maven
• Language = Java
• Spring Boot = default (3.x)
• Group = ie.tcd.scss
• Artifact = library
• Packaging = Jar
• Java = 17
• Dependencies
• Spring Web
• Rest Repositories
• Spring Data JPA
• H2 Database
• Generate
• Download and unpack
• Open project in your IDE
106. Creating Remote Repository (GitHub or GitLab)
CSU33012 Goetz Botterweck 106
If you want to
push an existing
repository later
do NOT create a
README
126. Simplified Domain Model
• Book
• Authors represented as simple
string
• Identifier “id” of type Integer
• Library Section
• Identifier “code” of type String
CSU33012 Goetz Botterweck 126
127. Jakarta Persistence API (JPA)
• Formerly known as Java Persistence API
• Application Programming Interface (set of interfaces) to
• handle data in Java programs
• manage the mapping of Java classes to tables in databases
• Query Language JPQL
• Implementations
• Eclipse Link (reference implementation)
• Hibernate
• Spring Data JPA uses JPA, expects a JPA implementation
CSU33012 Goetz Botterweck 127
129. H2 Database
• Minimal, fast in-memory database
• Good for early experiments, proof-of-concept implementations
• https://www.h2database.com/
• No need to install!
• https://www.baeldung.com/spring-boot-h2-database
CSU33012 Goetz Botterweck 129
133. class LibrarySection
CSU33012 Goetz Botterweck 133
package ie.tcd.scss.library.domain;
import jakarta.persistence.*;
@Entity
public class LibrarySection {
@Id
private String code;
@Column
private String name;
@OneToMany
private Set<Book> books;
}
134. Generating Missing Elements
for classes, not needed for enumerations
• Constructors
• One without any arguments – make private, so it does not get used
• One with all arguments (except @GeneratedValue id)
• Getters
• Setters
• except for @GeneratedValue id
• toString()
• equals()
• hashCode()
(Can be avoided with lombok, but for now we use the standard approach.)
CSU33012 Goetz Botterweck 134
140. Creating Spring Data Repositories
• We declare an interface that extends
public interface CrudRepository<T, I>
(CRUD = Create, Read, Update, Delete)
• T = Data type that the repository manages, e.g., Book
• I = Data type of the entity ID, e.g., Integer
• Spring Data automatically provides all necessary methods to operate
on that Repository
CSU33012 Goetz Botterweck 140
141. Creating Spring Data Repositories
For instance after declaring
public interface BookRepository extends
CrudRepository<Book, Integer>
We automatically get
Book save(Book b)
Iterable<Book> saveAll(Iterable<Book> iterable)
CSU33012 Goetz Botterweck 141
146. interface BookRepository
CSU33012 Goetz Botterweck 146
package ie.tcd.scss.library.repo;
import ie.tcd.scss.library.domain.Book;
import org.springframework.data.repository.CrudRepository;
public interface BookRepository extends CrudRepository<Book, Long> {
}
Basic default functionality
is available automatically.
If we need additional
methods, we need to
declare them as part of
the interface.
155. Details of Service
• Service class tagged @Service
• Private variable(s) holding reference(s) to all needed repositories
• Final
• Constructor
• expects references to repositories
• is „autowired“ by tag @Autowired
• Methods
• createBook()
• Create a new book
• lookup() – return (an Iterable with) all books
• total()– return total number of books
CSU33012 Goetz Botterweck 155
156. class BookService (Overview)
CSU33012 Goetz Botterweck 156
package ie.tcd.scss.library.service;
import ...
@Service
public class BookService {
private final BookRepository bookRepository;
private final LibrarySectionRepository librarySectionRepository;
@Autowired
public BookService(BookRepository bookRepository, LibrarySectionRepository librarySectionRepository) {
this.bookRepository = bookRepository;
this.librarySectionRepository = librarySectionRepository;
}
public Book createBook(String title, String authors, Integer publicationYear, Integer price, String isbn,
String keywords, String description, String librarySectionCode) {
... find librarySection
return bookRepository.save(new Book(...));
}
public Iterable<Book> lookup() {...}
public long total() {...}
}
157. How Does Autowiring and Depdendency Injection Work?
• Defining Beans
• Classes eligible for autowiring must be marked with tags like
@Component, @Service, @Repository, @Controller
• @Autowired annotation
• Instructs Spring to “inject” the dependency automatically
• Field Injection
• Constructor Injection – recommend, what we used above
• Setter Injection
CSU33012 Goetz Botterweck 157
158. How Does Autowiring and Depdendency Injection Work?
• Component Scanning
• Dependency Injection
• When a bean (e.g., BookService) is created, Spring checks (e.g. by looking
at constructor) if there are any dependencies that need to be injected.
• In the case of our BookService, it needs an instance of BookRepository
and an instance of LibrarySectionRepository
• Spring will search among the beans in its context for one that matches the
required type (e.g., BookRepository). If it finds a match, it will inject that
bean into the dependent one (BookService). Otherwise exception.
CSU33012 Goetz Botterweck 158
159. How Does Autowiring and Depdendency Injection Work?
Much more details at https://www.baeldung.com/spring-dependency-
injection
CSU33012 Goetz Botterweck 159
160. class BookService (Full Source)
CSU33012 Goetz Botterweck 160
package ie.tcd.scss.library.service;
import ie.tcd.scss.library.domain.Book;
import ie.tcd.scss.library.domain.LibrarySection;
import ie.tcd.scss.library.repo.BookRepository;
import ie.tcd.scss.library.repo.LibrarySectionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class BookService {
private final BookRepository bookRepository;
private final LibrarySectionRepository librarySectionRepository;
@Autowired
public BookService(BookRepository bookRepository, LibrarySectionRepository librarySectionRepository) {
this.bookRepository = bookRepository;
this.librarySectionRepository = librarySectionRepository;
}
public Book createBook(String title, String authors, Integer publicationYear, Integer price, String isbn,
String keywords, String description, String librarySectionCode) {
LibrarySection librarySection = librarySectionRepository
.findById(librarySectionCode)
.orElseThrow(() -> new RuntimeException("Library Section does not exist: " + librarySectionCode));
return bookRepository.save(new Book(title, authors, publicationYear, price, isbn, keywords, description,
librarySection));
}
public Iterable<Book> lookup() {
return bookRepository.findAll();
}
public long total() {
return bookRepository.count();
}
}
161. class LibrarySectionService
CSU33012 Goetz Botterweck 161
package ie.tcd.scss.library.service;
import ie.tcd.scss.library.domain.LibrarySection;
import ie.tcd.scss.library.repo.LibrarySectionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LibrarySectionService {
private final LibrarySectionRepository librarySectionRepository;
@Autowired LibrarySectionService(LibrarySectionRepository librarySectionRepository) {
this.librarySectionRepository = librarySectionRepository;
}
public LibrarySection createLibrarySection(String code, String name) {
return librarySectionRepository
.findById(code)
.orElse(librarySectionRepository.save(new LibrarySection(code, name)));
}
public Iterable<LibrarySection> lookup() {
return librarySectionRepository.findAll();
}
public long total() {
return librarySectionRepository.count();
}
}
168. • Why Create BookController?
• To provide REST access to business logic, e.g.,
create Books and add them into a LibrarySection in one go
• We need to get extra data (code of LibrarySection)
• Two approaches
• Get extra data in request parameters
• Get extra data in Data Transfer Object (DTO)
• i.e., one class/object that can hold all data needed during the request
CSU33012 Goetz Botterweck 168
209. 5.9 RESTful APIs with
Spring Data REST
CSU33012 Goetz Botterweck 209
210. RESTful APIs
• Hypermedia-driven APIs
• Roy Fielding (2000)
• Hypermedia as the Engine of the Application State (HATEOAS)
• Expose application state via API
• Expose the API‘s documentation
• Navigation between resources
• Spring Data REST provides that out of the box!
CSU33012 Goetz Botterweck 210
211. RESTful APIs
• Hypermedia-driven APIs
• Roy Fielding (2000)
• Hypermedia as the Engine of the Application State (HATEOAS)
• Expose application state via API
• Expose the API‘s documentation
• Navigation between resources
• Spring Data REST provides that out of the box!
CSU33012 Goetz Botterweck 211