1. Datenbankverbindung in Spring Boot
📄 application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/demo
username: demo
password: secret
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: update # create, update, validate, none
show-sql: true # SQL in der Konsole anzeigen
properties:
hibernate.format_sql: true2. JPA + Hibernate
Spring Data JPA = eine Abstraktionsschicht über Hibernate. Ermöglicht die Arbeit mit der Datenbank wie mit Objekten.
Entity (Tabelle)
import jakarta.persistence.*;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
private String email;
// Getter/Setter
}Repository (DAO-Schicht)
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
// automatische Methoden anhand des Methodennamens
List<User> findByName(String name);
List<User> findByEmailContaining(String part);
}Spring generiert die Implementierung basierend auf dem Methodennamen.
Service-Schicht
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository repo;
public User createUser(User u) {
return repo.save(u);
}
public List<User> allUsers() {
return repo.findAll();
}
}Controller
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService service;
@GetMapping
public List<User> all() {
return service.allUsers();
}
@PostMapping
public User create(@RequestBody User user) {
return service.createUser(user);
}
}3. JPQL & Native Queries
📌 Wenn der findBy... Methode nicht ausreicht…
public interface UserRepository extends JpaRepository<User, Long> {
// JPQL (arbeitet mit Entities, nicht mit Tabellen)
@Query("SELECT u FROM User u WHERE u.name = :name")
List<User> findByNameJPQL(@Param("name") String name);
// Native SQL
@Query(value = "SELECT * FROM users WHERE email LIKE %:email%", nativeQuery = true)
List<User> findByEmailNative(@Param("email") String email);
}4. DTO (Data Transfer Object)
Um die Entity nicht direkt zurückzugeben (unsicher für APIs).
public record UserDto(Long id, String name) {}
@Service
@RequiredArgsConstructor
class UserService {
private final UserRepository repo;
public List<UserDto> allUsers() {
return repo.findAll().stream()
.map(u -> new UserDto(u.getId(), u.getName()))
.toList();
}
}5. Transaction Management
Spring Data JPA umschließt Methoden save/delete automatisch in Transaktionen, aber man kann manuell verwalten.
@Service
public class BankService {
@Transactional
public void transfer(Account a1, Account a2, double sum) {
a1.withdraw(sum);
a2.deposit(sum);
// falls hier eine Ausnahme auftritt → wird der gesamte Methode zurückgerollt
}
}📌 Standardmäßig: Rollback für RuntimeException.
6. Beziehungen (Entity relationships)
OneToOne
@Entity
class Profile {
@Id @GeneratedValue Long id;
String bio;
@OneToOne
User user;
}OneToMany / ManyToOne
@Entity
class User {
@Id @GeneratedValue Long id;
String name;
@OneToMany(mappedBy = "user")
List<Post> posts = new ArrayList<>();
}
@Entity
class Post {
@Id @GeneratedValue Long id;
String text;
@ManyToOne
User user;
}ManyToMany
@Entity
class Student {
@Id @GeneratedValue Long id;
String name;
@ManyToMany
@JoinTable(name = "student_course")
Set<Course> courses;
}
@Entity
class Course {
@Id @GeneratedValue Long id;
String title;
@ManyToMany(mappedBy = "courses")
Set<Student> students;
}7. Datenbankmigrationen (Flyway, Liquibase)
Es ist besser, sich nicht auf ddl-auto: update zu verlassen, sondern Migrationen zu verwenden.
Flyway 📄 application.properties
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration📄 src/main/resources/db/migration/V1__init.sql
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100)
);Beim Start von Spring Boot werden die Migrationen automatisch angewendet.
8. Paging & Sorting
Spring Data JPA kann automatisch paginieren:
@GetMapping
public Page<User> all(@RequestParam int page, @RequestParam int size) {
return repo.findAll(PageRequest.of(page, size, Sort.by("name")));
}📌 Die Antwort ist JSON mit Daten und Metadaten (Seitennummer, Gesamtzahl der Elemente usw.).
9. Best Practices
- ✅ Geben Sie keine Entity im API zurück → Verwenden Sie DTO.
- ✅ Fügen Sie immer Migrationen (Flyway/Liquibase) hinzu.
- ✅ Für die Produktion ist ddl-auto=validate besser.
- ✅ Transaktionen – nur in der Service-Schicht (@Transactional).
- ✅ Verwenden Sie Pageable für das API → belasten Sie die Datenbank nicht.
- ✅ Teilen Sie Repository → Service → Controller (saubere Architektur).