Curso de Spring Boot
Aunque Spring Data JPA genera muchas consultas automáticamente, en proyectos reales muchas veces necesitamos usar SQL directamente.
Esto se llama:
consultas SQL nativas
Se utilizan cuando:
- necesitamos consultas complejas
- queremos optimizar rendimiento
- queremos usar funciones específicas de la base de datos
- queremos controlar completamente el SQL
7.1 Cuándo usar SQL nativo
JPA es muy cómodo, pero no siempre es suficiente.
Ejemplo donde JPA funciona bien:
1findByEmail(String email)
Pero consultas complejas como:
1SELECT COUNT(*) 2FROM orders 3GROUP BY user_id
son más fáciles con SQL nativo.
7.2 Usar @Query
Spring permite escribir consultas personalizadas usando la anotación:
1@Query
Ejemplo
1@Repository 2public interface UserRepository extends JpaRepository<User, Long> { 3 4 @Query("SELECT u FROM User u WHERE u.name = :name") 5 List<User> findUsersByName(String name); 6 7}
Esto es JPQL (una versión SQL para entidades).
7.3 SQL nativo
Si queremos usar SQL real usamos:
1nativeQuery = true
Ejemplo
1@Query(value = "SELECT * FROM users WHERE name = :name", 2 nativeQuery = true) 3List<User> findUsersByName(String name);
Esto ejecuta SQL directamente en la base de datos.
7.4 Consulta con parámetros
Podemos usar parámetros en las consultas.
Ejemplo
1@Query(value = "SELECT * FROM users WHERE email = :email", 2 nativeQuery = true) 3User findByEmail(String email);
Uso:
1repository.findByEmail("ana@email.com");
7.5 Insert SQL nativo
Para ejecutar INSERT usamos:
1@Modifying
Ejemplo
1@Modifying 2@Query(value = "INSERT INTO users(name,email) VALUES(:name,:email)", 3 nativeQuery = true) 4void insertUser(String name, String email);
También debemos usar:
1@Transactional
porque modifica datos.
Ejemplo completo
1@Transactional 2@Modifying 3@Query(value = "INSERT INTO users(name,email) VALUES(:name,:email)", 4 nativeQuery = true) 5void insertUser(String name, String email);
7.6 Update SQL nativo
Ejemplo actualizar usuario.
1@Transactional 2@Modifying 3@Query(value = "UPDATE users SET name = :name WHERE id = :id", 4 nativeQuery = true) 5void updateUser(Long id, String name);
7.7 Delete SQL nativo
1@Transactional 2@Modifying 3@Query(value = "DELETE FROM users WHERE id = :id", 4 nativeQuery = true) 5void deleteUser(Long id);
7.8 Consultas con JOIN
Una gran ventaja del SQL nativo es usar JOINs complejos.
Ejemplo
Tablas:
1users 2orders
Consulta:
1@Query(value = """ 2 SELECT u.name, COUNT(o.id) 3 FROM users u 4 JOIN orders o ON u.id = o.user_id 5 GROUP BY u.name 6""", nativeQuery = true) 7List<Object[]> countOrdersByUser();
Resultado:
1[ 2 ["Ana",5], 3 ["Carlos",3] 4]
7.9 Proyecciones (interfaces)
Para evitar usar Object[], podemos usar proyecciones.
Crear interfaz
1public interface UserOrderStats { 2 3 String getName(); 4 Integer getTotalOrders(); 5 6}
Consulta
1@Query(value = """ 2 SELECT u.name as name, 3 COUNT(o.id) as totalOrders 4 FROM users u 5 JOIN orders o ON u.id = o.user_id 6 GROUP BY u.name 7""", nativeQuery = true) 8List<UserOrderStats> getUserStats();
Ahora Spring convierte automáticamente el resultado.
7.10 Usar EntityManager
Otra forma de ejecutar SQL es usando:
1EntityManager
Esto permite ejecutar consultas dinámicas.
Ejemplo
1@PersistenceContext 2private EntityManager entityManager;
Consulta:
1public List<User> getUsers() { 2 3 String sql = "SELECT * FROM users"; 4 5 return entityManager 6 .createNativeQuery(sql, User.class) 7 .getResultList(); 8 9}
7.11 Ventajas del SQL nativo
| ventaja | explicación |
|---|---|
| control total | escribes el SQL |
| mejor rendimiento | optimización directa |
| consultas complejas | JOIN, GROUP BY, etc |
| funciones específicas | funciones del motor |
7.12 Desventajas
| problema | explicación |
|---|---|
| menos portable | depende de la base de datos |
| más mantenimiento | hay que escribir SQL |
| más acoplamiento | dependes de la estructura DB |
7.13 Cuándo usar JPA vs SQL
Regla práctica:
Usar JPA cuando
- CRUD simple
- consultas sencillas
- rapidez de desarrollo
Usar SQL nativo cuando
- consultas complejas
- optimización
- agregaciones
- informes
7.14 Ejemplo real completo
Repository:
1@Repository 2public interface UserRepository extends JpaRepository<User, Long> { 3 4 @Query(value = "SELECT * FROM users WHERE email = :email", 5 nativeQuery = true) 6 User findByEmail(String email); 7 8 @Transactional 9 @Modifying 10 @Query(value = "UPDATE users SET name = :name WHERE id = :id", 11 nativeQuery = true) 12 void updateName(Long id, String name); 13 14}
Service:
1@Service 2public class UserService { 3 4 private final UserRepository repository; 5 6 public UserService(UserRepository repository) { 7 this.repository = repository; 8 } 9 10 public User getUserByEmail(String email) { 11 return repository.findByEmail(email); 12 } 13 14}
- Loading...