Implementando Hibernate con Spring Boot y PostgreSQL

    Introducción

    A medida que el uso de software se vuelve más común y se construyen más y más sistemas para manejar diversas tareas, los datos juegan un papel más importante en la escena tecnológica actual y futura. La información es cada vez más valiosa a medida que avanza la tecnología y abre más oportunidades para su uso.

    Es por esta razón, y muchas más, que el almacenamiento seguro y la manipulación de datos se han convertido en un aspecto importante de cualquier sistema o aplicación que se construya.

    ¿Qué es el mapeo relacional de objetos?

    En muchos sistemas, los objetos de la vida real se modelan como objetos en sistemas para facilitar la representación y manipulación de sus atributos. Por ejemplo, un teléfono se puede modelar como un objeto con atributos como su nombre, sistema operativo, fabricante y mucho más como sus atributos y esto se puede manipular y almacenar fácilmente en una base de datos.

    El mapeo relacional de objetos (ORM) es una técnica de mapeo de dichos objetos y sus atributos en la base de datos a través de mapeadores relacionales de objetos. Esta técnica también nos ayuda a convertir datos entre sistemas incompatibles utilizando aplicaciones de programación orientadas a objetos.

    Un ORM es una biblioteca que nos ayuda a interactuar con múltiples bases de datos o sistemas fácilmente utilizando nuestro idioma de elección. Nuestro código ahora está asignado al lenguaje de consulta específico de las bases de datos.

    Normalmente, necesitamos usar un lenguaje específico de base de datos para interactuar con una base de datos. Por ejemplo, para interactuar con un MySQL base de datos, necesitamos utilizar el lenguaje de consulta estructurado (SQL), pero estos lenguajes pueden diferir de una plataforma a otra.

    Por ejemplo, si bien siguen siendo similares, la sintaxis en un Postgres La base de datos es diferente del lenguaje de consulta utilizado en un Microsoft SQL base de datos. Un ORM ayuda a salvar esa diferencia y conecta nuestro software a diferentes sistemas de bases de datos con facilidad.

    Otros beneficios de usar un ORM incluyen acelerar el proceso de desarrollo, ya que los desarrolladores no tienen que escribir el código de acceso a la base de datos y repetirlo cada vez que quieran acceder a una base de datos. Una vez que se diseña un modelo y se escribe el código de manipulación, no es necesario volver a hacerlo, lo que hace que el código sea fácil de actualizar, mantener y reutilizar.

    Sin embargo, existen algunos inconvenientes asociados con los ORM e incluyen:

    • Los ORM tienden a ser lentos en algunas situaciones en cuanto al rendimiento.
    • Para consultas complejas como uniones, los ORM a veces no pueden sustituir las consultas SQL sin procesar
    • Debido a las abstracciones introducidas por un ORM, el desarrollador puede perder la comprensión de SQL y cómo se logra la administración de la base de datos entre bastidores.

    Hibernar

    Hibernar es un marco que permite a los desarrolladores conservar fácilmente los datos de la aplicación en bases de datos relacionales utilizando JDBC. Es una implementación de la API de persistencia de Java (JPA), lo que significa que se puede utilizar en cualquier sistema que admita JPA, como la edición estándar (Java SE) y la edición empresarial (Java EE).

    Hibernate es una herramienta ligera y de código abierto que simplifica la creación, manipulación y acceso de datos desde una base de datos en aplicaciones basadas en Java. Funciona mapeando un objeto creado a partir de una clase Java y sus atributos a los datos almacenados en la base de datos.

    Algunas ventajas de usar Hibernate incluyen:

    • Es de código abierto y liviano, lo que significa que es de uso gratuito y tiene una comunidad de colaboradores que lo mejoran constantemente.
    • Hibernate utiliza un caché internamente que mejora su rendimiento
    • Es independiente de la base de datos, lo que significa que se puede utilizar para acceder y manipular datos en varias bases de datos diferentes.
    • Proporciona la funcionalidad para simplificar las combinaciones cuando se obtienen datos de varias tablas.
    • Al crear tablas automáticamente, el desarrollador puede concentrarse en hacer otra lógica
    • Es un marco estable que existe desde hace 18 años.

    Alternativas

    Hibernate no es el único marco ORM que podemos usar en nuestras aplicaciones Java, otros incluyen:

    • JOOQ (Consulta orientada a objetos de Java) es una biblioteca ligera de software de mapeo de bases de datos
    • JDBI proporciona acceso a datos relacionales en Java de una manera conveniente
    • MyBatis es un marco mapeador SQL para integrar bases de datos relacionales
    • Ebean que se puede utilizar para aplicaciones basadas en Java y Kotlin
    • ORMLite que es un marco ligero para persistir objetos Java en bases de datos SQL

    Estas son solo algunas de las alternativas para Hibernate, definitivamente hay incluso más bibliotecas y marcos que se adaptan a muchos escenarios y bases de datos diferentes.

    Implementando Hibernate con Spring Boot

    Configuración del proyecto

    Para este proyecto de demostración, usaremos una base de datos PostgreSQL y se pueden encontrar instrucciones de instalación. Aquí para plataformas Mac OS, Linux y Windows.

    Una vez configurado, podemos crear nuestra base de datos de demostración, phonesdemo. PgAdmin proporciona una interfaz de usuario para interactuar con una base de datos PostgreSQL, pero también se puede utilizar una terminal.

    Veamos ahora a Hibernate en acción usándolo en una API Spring Boot de muestra, que arrancaremos usando el Spring Initializr herramienta:

    Usaremos Java 8 y Maven para nuestra gestión de dependencias con algunas dependencias:

    • Spring Web Starter para ayudarnos a crear una aplicación basada en web
    • Spring Data JPA para proporcionar la API de acceso a datos que utilizará Hibernate
    • H2 Database para incorporar la funcionalidad de Hibernate a nuestro proyecto
    • PostgreSQL para permitirnos conectarnos a una base de datos PostgreSQL

    Una vez que hagamos clic en “Generar”, recibiremos un archivo zip que contiene el proyecto y podemos comenzar a implementar la aplicación web que requiere una base de datos. Una vez que descomprimimos este archivo zip en nuestra carpeta de trabajo, podemos probar que está listo para trabajar ejecutando el comando:

    $ mvn spring-boot:run
    

    Implementación

    Modifiquemos nuestro application.properties para incluir los detalles de nuestra base de datos:

    # Database Properties
    spring.datasource.url=jdbc:postgresql://localhost:5432/phonesdemo
    spring.datasource.username=postgres
    spring.datasource.password=
    
    # Hibernate Properties
    # The SQL dialect makes Hibernate generate better SQL for the chosen database
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL92Dialect
    
    # Hibernate ddl auto (create, create-drop, validate, update)
    spring.jpa.hibernate.ddl-auto=update
    

    Hibernate proporciona una consola H2 que podemos usar para verificar el estado de la base de datos e incluso realizar la entrada de datos a través de una interfaz de usuario. Lo habilitamos agregando la siguiente línea a nuestro application.properties:

    spring.h2.console.enabled=true
    

    Luego iniciamos nuestra aplicación y navegamos a http://localhost:8080/h2-console para probar si todo está funcionando. Obtenemos una página donde podemos probar si la conexión a nuestra base de datos funciona:

    Para el menú desplegable de configuraciones guardadas, elegiremos Generic PostgreSQL y también actualizaremos la URL de JDBC para que coincida con el nombre de nuestra base de datos de prueba. Luego, completamos el nombre de usuario y la contraseña de nuestra base de datos y hacemos clic en “Probar conexión” solo para asegurarnos de que nuestra aplicación Spring pueda conectarse a nuestra base de datos. Si todo está bien configurado, recibimos un mensaje de éxito.

    Si hacemos clic en “Conectar”, obtenemos esta página:

    Aquí podemos navegar por nuestra base de datos e incluso realizar consultas SQL.

    La API que crearemos se utilizará para almacenar y manipular teléfonos y sus atributos, como el nombre y el sistema operativo. Con nuestra base de datos instalada y conectada, creemos una clase de entidad (Phone.java) que mapeará los atributos de nuestro objeto a la base de datos y nos permitirá realizar operaciones CRUD en la base de datos:

    // Imports truncated for brevity, refer to GitHub link below for the full code
    
    @Entity
    @Table(name = "phones")
    @EntityListeners(AuditingEntityListener.class)
    public class Phone {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private long id; // Each phone will be given an auto-generated unique identifier when stored
    
        @Column(name = "phone_name", nullable = false)
        private String phoneName; // Save the name of the phone
    
        @Column(name = "os", nullable = false)
        private String os; // Save the operating system running in the phone
        
        // Standard getters and setters
    }
    

    los @Entity La anotación le dice a Hibernate que esta clase representa una entidad que debe persistir.

    los @Table La anotación se utiliza para nombrar la tabla. Si se omite esta anotación, la tabla simplemente usará el nombre de la clase / entidad.

    Similarmente el @Column Las anotaciones también se pueden omitir, pero las columnas de la base de datos usarán los nombres de campo tal como están, y algunas veces este no es el comportamiento preferido ya que los nombres de sus columnas pueden ser mayúsculas y minúsculas y los nombres de campos son mayúsculas y minúsculas, por ejemplo.

    Cuando guardamos este archivo y reiniciamos nuestro servidor y verificamos nuestra base de datos, habrá una nueva tabla llamada phones y las columnas de id, phone_namey os estará presente.

    No habrá datos, pero esto es Hibernate en funcionamiento, si la tabla especificada en la clase de entidad no existe, Hibernate la creará por nosotros.

    Implementemos ahora el controlador para ayudarnos a realizar operaciones en nuestra base de datos a través de una API:

    @RestController
    @RequestMapping("/api/v1")
    public class PhoneController {
        @Autowired
        private PhoneRepository phoneRepository;
    
        // GET method to fetch all phones
        @GetMapping("/phones")
        public List<Phone> getAllPhones() {
            return phoneRepository.findAll();
        }
    
        // GET method to fetch phone by Id
        @GetMapping("/phones/{id}")
        public ResponseEntity<Phone> getPhoneById(@PathVariable(value = "id") Long phoneId)
            throws Exception {
            Phone phone = phoneRepository.findById(phoneId)
                   .orElseThrow(() -> new Exception("Phone " + phoneId + " not found"));
            return ResponseEntity.ok().body(phone);
        }
      
        // POST method to create a phone
        @PostMapping("/phones")
        public Phone createPhone(@Valid @RequestBody Phone phone) {
            return phoneRepository.save(phone);
        }
      
        // PUT method to update a phone's details
        @PutMapping("/phones/{id}")
        public ResponseEntity<Phone> updatePhone(
            @PathVariable(value="id") Long phoneId, @Valid @RequestBody Phone phoneDetails
        ) throws Exception {
            Phone phone = phoneRepository.findById(phoneId)
                .orElseThrow(() -> new Exception("Phone " + phoneId + " not found"));
    
            phone.setPhoneName(phoneDetails.getPhoneName());
            phone.setOs(phoneDetails.getOs());
    
            final Phone updatedPhone = phoneRepository.save(phone);
            return ResponseEntity.ok(updatedPhone);
        }
      
        // DELETE method to delete a phone
        @DeleteMapping("/phone/{id}")
        public Map<String, Boolean> deletePhone(@PathVariable(value="id") Long phoneId) throws Exception {
            Phone phone = phoneRepository.findById(phoneId)
                .orElseThrow(() -> new Exception("Phone " + phoneId + " not found"));
    
            phoneRepository.delete(phone);
            Map<String, Boolean> response = new HashMap<>();
            response.put("deleted", Boolean.TRUE);
            return response;
        }
    }
    

    En nuestra clase de controlador, anotamos nuestra clase por @RestController para indicar que esta es la clase de controlador de solicitudes que manejará la funcionalidad REST para nuestra API. Luego definimos métodos para manejar cada una de las cuatro operaciones RESTful: GET, POST, PUTy DELETE. Estos métodos nos proporcionarán una interfaz para interactuar con nuestra API y administrar datos.

    Nuestra API, a su vez, utilizará Hibernate para reflejar nuestras operaciones en dichos datos en nuestra base de datos.

    Comencemos creando un solo teléfono a través de nuestra API:

    Obtenemos la respuesta de nuestra API, pero verifiquemos la base de datos usando la Consola H2 para confirmar:

    Como puede ver, en la captura de pantalla anterior, para obtener los datos en nuestra base de datos, usamos el comando SQL SELECT * FROM phones. Para lograr lo mismo en nuestro código a través del ORM, es tan simple como usar la línea:

    phoneRepository.findAll();
    

    Esto es más amigable y familiar para nosotros ya que se logra en el mismo idioma que estamos usando mientras implementamos el resto de nuestro proyecto.

    Conclusión

    Hemos creado con éxito un objeto de teléfono y hemos guardado sus atributos en nuestra base de datos PostgreSQL usando Hibernate en nuestra API Spring Boot. Podemos agregar más teléfonos, eliminar teléfonos y actualizar los datos del teléfono interactuando con la API e Hibernate reflejará los cambios en nuestra base de datos.

    Hibernate nos ha facilitado interactuar con una base de datos desde nuestra aplicación Spring Boot y administrar nuestros datos. El tiempo de desarrollo también se ha reducido significativamente ya que no tenemos que escribir los comandos SQL para administrar los datos nosotros mismos.

    El código fuente de este proyecto está disponible aquí en GitHub.

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *