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.