A Spring Boot server usually keeps no state in memory between requests; it lives in a database. This chapter shows how to work with data through Spring Data JPA, keep it consistent with transactions, validate input and manage the schema with Flyway.
Dependencies and connection settings
For PostgreSQL, add these dependencies (in Initializr: data-jpa, postgresql, flyway and validation):
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-flyway")
implementation("org.flywaydb:flyway-database-postgresql")
runtimeOnly("org.postgresql:postgresql")
}
Connection details go under spring.datasource.*. Let Flyway own the schema and have Hibernate only check that entities match the tables:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/demo
username: demo
password: ${DB_PASSWORD}
jpa:
hibernate:
ddl-auto: validate
open-in-view: false
Entities and repositories
An entity maps to a table row. JPA needs a no-argument constructor, so add a protected one.
@Entity
public class Todo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 200)
private String title;
private boolean done;
protected Todo() {}
public Todo(String title) { this.title = title; }
public void complete() { this.done = true; }
public Long getId() { return id; }
public String getTitle() { return title; }
public boolean isDone() { return done; }
}
For a repository you declare only an interface; Spring Data generates the implementation. JpaRepository provides save, findById, findAll, deleteById and more, and method names can be turned into queries:
public interface TodoRepository extends JpaRepository<Todo, Long> {
List<Todo> findByDoneFalseOrderByIdAsc();
@Query("select t from Todo t where lower(t.title) like lower(concat('%', :keyword, '%'))")
List<Todo> search(@Param("keyword") String keyword);
}
Transactions
The service layer is where operations are grouped into units of work. A @Transactional method opens a transaction on entry, commits on normal return and rolls back on a runtime exception. Changes to an entity loaded inside the transaction are flushed at commit without calling save (dirty checking).
@Service
@Transactional(readOnly = true)
public class TodoService {
private final TodoRepository repository;
public TodoService(TodoRepository repository) {
this.repository = repository;
}
public List<Todo> open() {
return repository.findByDoneFalseOrderByIdAsc();
}
@Transactional
public void complete(Long id) {
Todo todo = repository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
todo.complete();
}
}
コメント 0件
ログイン · ログインするとコメントできます。