08 - Spring Boot
Spring Boot (visto en Programación) elimina el cableado a mano: el contenedor de IoC crea los objetos y los conecta. En la parte de datos, lo que este temario ha construido a mano se reduce a configuración y anotaciones. Sigue siendo JDBC por debajo: lo que cambia es quién crea y conecta las piezas.
1. Conexión y pool
Desaparece la clase DbConnection. Las credenciales viajan en el fichero de propiedades estándar y los componentes necesarios llegan como dependencias:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.mariadb.jdbc</groupId> <artifactId>mariadb-java-client</artifactId> <scope>runtime</scope> </dependency>
spring.datasource.url=jdbc:mariadb://localhost:3306/library spring.datasource.username=library_user spring.datasource.password=secret
Con esas propiedades, Spring Boot auto-configura un bean DataSource con HikariCP — el pool del tema 06 — e inyecta donde haga falta. Los ajustes del pool, cuando toca afinarlos, van en el mismo fichero:
spring.datasource.hikari.maximum-pool-size=10
2. JdbcTemplate
El DAO inyecta JdbcTemplate, que ejecuta SQL y mapea resultados sin gestionar conexiones ni PreparedStatement a mano (los abre y cierra contra el DataSource del pool):
@Repository
public class JdbcBookDao implements BookDao {
private final JdbcTemplate template;
private final SimpleJdbcInsert insertBook;
public JdbcBookDao(DataSource dataSource) {
this.template = new JdbcTemplate(dataSource);
this.insertBook = new SimpleJdbcInsert(dataSource)
.withTableName("book")
.usingGeneratedKeyColumns("id");
}
@Override
public Optional<Book> findById(long id) {
List<Book> books = template.query(
"SELECT id, title, price, publisher_id FROM book WHERE id = ?",
this::map, id);
return books.stream().findFirst();
}
@Override
public Book save(Book book) {
long id = insertBook.executeAndReturnKey(Map.of(
"title", book.title(),
"price", book.price(),
"publisher_id", book.publisherId())).longValue();
return new Book(id, book.title(), book.price(), book.publisherId());
}
private Book map(ResultSet resultSet, int rowNum) throws SQLException {
// el mismo mapeo del tema 05
}
}
| Elemento | Función |
|---|---|
query / queryForObject | SELECT con un mapeador por fila |
update | INSERT/UPDATE/DELETE; devuelve filas afectadas |
SimpleJdbcInsert | INSERT declarativo que devuelve la clave autogenerada |
@Repository | Estereotipo de persistencia: el IoC registra el DAO como bean |
La interfaz BookDao no cambia: es el mismo patrón del tema 05 reescrito a la API de Spring.
3. Transacciones
@Transactional sustituye el bloque manual de setAutoCommit, commit y rollback:
@Transactional
public void publish(Book book, Author author) {
bookDao.save(book);
authorDao.save(book.id(), author);
}
El método completo se ejecuta dentro de una transacción: al terminar sin excepción se confirma (commit); si cualquier excepción sale del método, se deshace (rollback) automáticamente — por defecto ante excepciones unchecked.
4. Flyway en el arranque
Con flyway-core en el classpath y los scripts en db/migration, Spring Boot ejecuta migrate automáticamente al arrancar, antes de servir la primera consulta: el flujo del tema 07 sin escribir nada en el main. La ruta y el comportamiento se ajustan con las propiedades spring.flyway.*.
5. En las pruebas
Las pruebas de integración con Testcontainers (explicadas en Entornos) se simplifican igual: la anotación @ServiceConnection conecta la aplicación de prueba con el contenedor sin configurar la DataSource a mano:
@Testcontainers
@SpringBootTest
class ApplicationIntegrationTest {
@Container
@ServiceConnection
static MariaDBContainer<?> mariadb = new MariaDBContainer<>("mariadb:12.3");
}
DataSource es HikariCP con su pool, JdbcTemplate ejecuta sentencias parametrizadas, @Transactional usa las transacciones del propio JDBC y Flyway aplica scripts SQL puros. JPA y sus repositorios automáticos quedan para 2.º curso.