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

ventajaexplicación
control totalescribes el SQL
mejor rendimientooptimización directa
consultas complejasJOIN, GROUP BY, etc
funciones específicasfunciones del motor

7.12 Desventajas

problemaexplicación
menos portabledepende de la base de datos
más mantenimientohay que escribir SQL
más acoplamientodependes 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...