Ya estoy inscrito ¿Todavía no tienes acceso? Nuestros Planes
Ya estoy inscrito ¿Todavía no tienes acceso? Nuestros Planes
2
respuestas

[CORTADO] Reto final: Spring Data JPA

Este proyecto es una aplicación Java con Spring Boot y Maven para la gestión de facturación electrónica. Permite:

  • Registrar clientes con identificación única y datos legales.
  • Gestionar productos (ítems), incluyendo impuestos, descuentos y retenciones.
  • Crear facturas asociando clientes e ítems, con soporte para códigos de referencia y métodos de pago.
  • Utilizar Spring Data JPA para el acceso a datos y relaciones entre entidades (@ManyToMany y @ManyToOne).
  • Manejar problemas comunes de Hibernate como la carga perezosa (LazyInitializationException).
  • Interfaz de consola.

El proyecto aplica buenas prácticas de Java, Spring Boot y Hibernate en escenarios reales de facturación.

Principal

package com.diego.billing.principal;

public class Principal {
    Scanner scan = new Scanner(System.in);

    private BillingRepository billingRepository;
    private CustomerRepository customerRepository;
    private ItemRepository itemRepository; 

    public void muestraElMenu() {
        var opcion = -1;
        while (opcion != 0) {
            var menu = """
                    2 - Ver clientes
                    4 - Ver items
                    5 - Crear factura
                    6 - Ver facturas
                    7 - Buscar factura por código de referencia
                    
                    0 - Salir
                    """;
            System.out.println(menu);
            opcion = scan.nextInt();
            
            switch (opcion) {               
                case 5:
                    crearFactura();
                    break;
                case 6:
                    verFacturas();
                    break;
                case 7:
                    verFacturaPorNombre();
                    break;
                case 0:
                    System.out.println("Cerrando la aplicación...");
                    break;
                default:
                    System.out.println("Opción inválida");
            }
        }
    }
    
    public void crearFactura() {
        Billing billing = new Billing();
        billing.setItems(new ArrayList<>());

        System.out.print("Document: ");
        billing.setDocument(scan.nextLine());
        System.out.print("Numbering Range ID: ");
        billing.setNumberingRangeId(scan.nextInt());

        scan.nextLine(); // Limpiar buffer
        System.out.print("Reference Code: ");
        String referenceCode = scan.nextLine();
        billing.setReferenceCode(referenceCode);
        System.out.print("Observation: ");
        billing.setObservation(scan.nextLine());
        System.out.print("Payment Method Code: ");
        billing.setPaymentMethodCode(scan.nextInt());
        scan.nextLine(); // Clear buffer

        // Select or create customer
        System.out.print("Enter customer ID (or 0 to create new): ");
        Long customerId = scan.nextLong();
        scan.nextLine();
        Customer customer;
        if (customerId == 0) {
            customer = crearCliente();
        } else {
            customer = customerRepository.findById(customerId).orElseGet(this::crearCliente);
        }
        billing.setCustomer(customer);

        String addMore;
        do {
            Item item;
            System.out.print("Enter item ID (or 0 to create new): ");
            Long itemId = scan.nextLong();
            scan.nextLine();
            if (itemId == 0) {
                item = crearItem();
            } else {
                item = itemRepository.findById(itemId).orElseThrow();
            }
            billing.getItems().add(item);
            System.out.print("Add another item? (y/n): ");
            addMore = scan.nextLine();
        } while (addMore.equalsIgnoreCase("y"));


        // Save billing (cascade saves items)
        billingRepository.save(billing);
        System.out.println("✅ Invoice created successfully:\n" + billing);
    }
    private void verFacturas(){
        List<Billing> facturas = billingRepository.findAll();
        if (facturas.isEmpty()) {
            System.out.println("No hay facturas registradas.");
        } else {
            facturas.forEach(System.out::println);
        }
    }
    
}

Services:

package com.diego.billing.service;

@Service
public class BillingService {

    private final BillingRepository repository;

    public BillingService(BillingRepository repository) {
        this.repository = repository;
    }

    public Billing save(Billing billing) {
        return repository.save(billing);
    }

    public List<Billing> findAll() {
        return repository.findAll();
    }

    public List<Billing> findByReferenceCode(String referenceCode) {
        return repository.findAll()
                .stream()
                .filter(b -> b.getReferenceCode().equalsIgnoreCase(referenceCode))
                .toList();
    }
}
2 respuestas

Hola Diego, espero que estés bien

¡Qué proyecto tan interesante estás desarrollando! Parece que estás haciendo un gran trabajo integrando conceptos de Spring Boot, Spring Data JPA y Hibernate. Aquí te dejo algunas sugerencias para abordar el desafío que estás enfrentando:

  1. Carga Perezosa (LazyInitializationException): Si estás enfrentando problemas con la carga perezosa, una solución común es utilizar la anotación @Transactional en los métodos del servicio que acceden a las entidades. Esto asegura que la sesión de Hibernate esté abierta mientras se acceden las colecciones perezosas.

    @Service
    public class BillingService {
    
        private final BillingRepository repository;
    
        public BillingService(BillingRepository repository) {
            this.repository = repository;
        }
    
        @Transactional
        public Billing save(Billing billing) {
            return repository.save(billing);
        }
    
        @Transactional(readOnly = true)
        public List<Billing> findAll() {
            return repository.findAll();
        }
    
        @Transactional(readOnly = true)
        public List<Billing> findByReferenceCode(String referenceCode) {
            return repository.findAll()
                    .stream()
                    .filter(b -> b.getReferenceCode().equalsIgnoreCase(referenceCode))
                    .toList();
        }
    }
    
  2. Integración de Base de Datos: Asegúrate de que tu archivo application.properties esté correctamente configurado para conectar con tu base de datos. Aquí tienes un ejemplo básico:

    spring.datasource.url=jdbc:mysql://localhost:3306/tu_base_de_datos
    spring.datasource.username=tu_usuario
    spring.datasource.password=tu_contraseña
    spring.jpa.hibernate.ddl-auto=update
    spring.jpa.show-sql=true
    
  3. Modelado de Entidades: Verifica que las relaciones entre tus entidades estén correctamente mapeadas. Por ejemplo, si tienes una relación @ManyToOne o @OneToMany, asegúrate de que las anotaciones estén correctamente aplicadas y que las claves foráneas estén bien definidas.

  4. Consultas Personalizadas: Si necesitas realizar consultas específicas, puedes definir métodos en tus repositorios que utilicen JPQL o consultas derivadas. Por ejemplo:

    public interface BillingRepository extends JpaRepository<Billing, Long> {
        List<Billing> findByCustomerName(String name);
    }
    

Espero que estas sugerencias te sean útiles para avanzar en tu proyecto. ¡Sigue adelante con tu aplicación de gestión de facturación electrónica!

Espero haber ayudado y buenos estudios!

¡Hola Brenda! Muchas gracias por tomarte el tiempo para revisar mi proyecto y compartir tus observaciones.

Me alegra mucho que hayas encontrado interesante el proyecto. Estoy aplicando lo aprendido en el curso para construir una solución robusta de facturación, con una pronta integración a una API POST, para generar factura electrónica con registro en la DIAN (Dirección de Impuestos y Aduanas Nacionales) en Colombia, y tus recomendaciones me vienen como anillo al dedo.

Voy a implementar el uso de @Transactional en los métodos del servicio para evitar los errores de LazyInitializationException, y revisaré que las relaciones entre entidades estén correctamente anotadas.

Por otro lado, La integración con la base de datos funciona perfectamente, he tenido que abstraer los objetos y funcionalidades que estos tendrán finalmente, comprender la documentación para la creación de relaciones muchos a muchos, como por ejemplo los item y las facturas, es un tema que aun debo abordar bastante en su implementación completamente.

Gracias nuevamente por tu acompañamiento. ¡Seguiré puliendo el proyecto y aprendiendo con cada paso!