Skip to content

Gestió d'events de Post/Pre de creacio, actualització i esborrat. #184

@anadal-fundaciobit

Description

@anadal-fundaciobit

Resposta de deepseek de dia 30/09/2025

Sistema Simple de Eventos de Persistencia (Modificado)

  1. Clase Abstracta PersistenceEvent
    java

public abstract class PersistenceEvent {

public void executePrePersist(E entity) {
    // Implementación por defecto vacía
}

public void executePostPersist(E entity) {
    // Implementación por defecto vacía
}

public void executePreUpdate(E entity) {
    // Implementación por defecto vacía
}

public void executePostUpdate(E entity) {
    // Implementación por defecto vacía
}

public void executePreRemove(E entity) {
    // Implementación por defecto vacía
}

public void executePostRemove(E entity) {
    // Implementación por defecto vacía
}

public void executePostLoad(E entity) {
    // Implementación por defecto vacía
}

// Método abstracto para identificar si este listener aplica a una entidad específica
public abstract boolean supports(Class<?> entityClass);

}

  1. Manager Centralizado (Simplificado)
    java

public class PersistenceEventManager {

private final Map<Class<?>, List<PersistenceEvent<?>>> entityListeners = new ConcurrentHashMap<>();

// Instancia singleton
private static PersistenceEventManager instance;

private PersistenceEventManager() {
    // Constructor privado para singleton
}

// ===== SINGLETON PATTERN =====

public static synchronized PersistenceEventManager getInstance() {
    if (instance == null) {
        instance = new PersistenceEventManager();
    }
    return instance;
}

public static synchronized void destroyInstance() {
    if (instance != null) {
        instance.clearAllListeners();
        instance = null;
        System.gc(); // Sugerencia al garbage collector
    }
}

public static boolean isInstanceCreated() {
    return instance != null;
}

// ===== REGISTRO DE LISTENERS =====

public <E> void registerListener(Class<E> entityClass, PersistenceEvent<E> listener) {
    entityListeners
        .computeIfAbsent(entityClass, k -> new CopyOnWriteArrayList<>())
        .add(listener);
}

public <E> void unregisterListener(Class<E> entityClass, PersistenceEvent<E> listener) {
    List<PersistenceEvent<?>> listeners = entityListeners.get(entityClass);
    if (listeners != null) {
        listeners.remove(listener);
        
        // Si no quedan listeners para esta entidad, eliminar la entrada
        if (listeners.isEmpty()) {
            entityListeners.remove(entityClass);
        }
    }
}

public void unregisterAllListenersForEntity(Class<?> entityClass) {
    entityListeners.remove(entityClass);
}

public void clearAllListeners() {
    entityListeners.clear();
}

// ===== MÉTODOS DE DISPARO =====

@SuppressWarnings("unchecked")
public <E> void firePrePersist(E entity) {
    if (entity == null) return;
    fireEvent(entity, "executePrePersist");
}

@SuppressWarnings("unchecked")
public <E> void firePostPersist(E entity) {
    if (entity == null) return;
    fireEvent(entity, "executePostPersist");
}

@SuppressWarnings("unchecked")
public <E> void firePreUpdate(E entity) {
    if (entity == null) return;
    fireEvent(entity, "executePreUpdate");
}

@SuppressWarnings("unchecked")
public <E> void firePostUpdate(E entity) {
    if (entity == null) return;
    fireEvent(entity, "executePostUpdate");
}

@SuppressWarnings("unchecked")
public <E> void firePreRemove(E entity) {
    if (entity == null) return;
    fireEvent(entity, "executePreRemove");
}

@SuppressWarnings("unchecked")
public <E> void firePostRemove(E entity) {
    if (entity == null) return;
    fireEvent(entity, "executePostRemove");
}

@SuppressWarnings("unchecked")
public <E> void firePostLoad(E entity) {
    if (entity == null) return;
    fireEvent(entity, "executePostLoad");
}

// ===== MÉTODO PRIVADO PARA DISPARAR EVENTOS =====

@SuppressWarnings("unchecked")
private <E> void fireEvent(E entity, String methodName) {
    Class<?> entityClass = entity.getClass();
    
    // Buscar listeners específicos para esta entidad
    List<PersistenceEvent<?>> specificListeners = entityListeners.get(entityClass);
    if (specificListeners != null) {
        for (PersistenceEvent<?> listener : specificListeners) {
            if (listener.supports(entityClass)) {
                invokeMethod(listener, entity, methodName);
            }
        }
    }
    
    // Buscar listeners para Object.class (aplican a todas las entidades)
    List<PersistenceEvent<?>> globalListeners = entityListeners.get(Object.class);
    if (globalListeners != null) {
        for (PersistenceEvent<?> listener : globalListeners) {
            if (listener.supports(entityClass)) {
                invokeMethod(listener, entity, methodName);
            }
        }
    }
}

@SuppressWarnings("unchecked")
private <E> void invokeMethod(PersistenceEvent<?> listener, E entity, String methodName) {
    try {
        Method method = listener.getClass().getMethod(methodName, Object.class);
        method.invoke(listener, entity);
    } catch (Exception e) {
        System.err.println("Error invoking method " + methodName + " on listener: " + e.getMessage());
    }
}

// ===== UTILIDADES =====

public <E> boolean hasListeners(Class<E> entityClass) {
    List<PersistenceEvent<?>> listeners = entityListeners.get(entityClass);
    return listeners != null && !listeners.isEmpty();
}

public <E> int getListenerCount(Class<E> entityClass) {
    List<PersistenceEvent<?>> listeners = entityListeners.get(entityClass);
    return listeners != null ? listeners.size() : 0;
}

public int getTotalListenerCount() {
    return entityListeners.values().stream()
            .mapToInt(List::size)
            .sum();
}

public Set<Class<?>> getRegisteredEntityClasses() {
    return new HashSet<>(entityListeners.keySet());
}

}

  1. Configuración Estática
    java

public class PersistenceEventConfig {

// ===== REGISTRO DE EVENTOS =====

public static <E> void registerEvent(Class<E> entityClass, PersistenceEvent<E> listener) {
    PersistenceEventManager manager = PersistenceEventManager.getInstance();
    manager.registerListener(entityClass, listener);
    System.out.println("Listener registrado para entidad: " + entityClass.getSimpleName());
}

public static <E> void unregisterEvent(Class<E> entityClass, PersistenceEvent<E> listener) {
    if (PersistenceEventManager.isInstanceCreated()) {
        PersistenceEventManager manager = PersistenceEventManager.getInstance();
        manager.unregisterListener(entityClass, listener);
        System.out.println("Listener desregistrado para entidad: " + entityClass.getSimpleName());
        
        // Verificar si el manager puede ser destruido
        checkAndDestroyManager();
    }
}

public static void unregisterAllEventsForEntity(Class<?> entityClass) {
    if (PersistenceEventManager.isInstanceCreated()) {
        PersistenceEventManager manager = PersistenceEventManager.getInstance();
        manager.unregisterAllListenersForEntity(entityClass);
        System.out.println("Todos los listeners desregistrados para: " + entityClass.getSimpleName());
        
        // Verificar si el manager puede ser destruido
        checkAndDestroyManager();
    }
}

public static void unregisterAllEvents() {
    if (PersistenceEventManager.isInstanceCreated()) {
        PersistenceEventManager manager = PersistenceEventManager.getInstance();
        manager.clearAllListeners();
        System.out.println("Todos los listeners desregistrados");
        
        // Forzar destrucción del manager
        PersistenceEventManager.destroyInstance();
    }
}

// ===== VERIFICACIÓN Y LIMPIEZA =====

private static void checkAndDestroyManager() {
    if (PersistenceEventManager.isInstanceCreated()) {
        PersistenceEventManager manager = PersistenceEventManager.getInstance();
        
        // Si no hay listeners registrados, destruir el manager
        if (manager.getTotalListenerCount() == 0) {
            System.out.println("No hay listeners registrados. Destruyendo PersistenceEventManager...");
            PersistenceEventManager.destroyInstance();
        }
    }
}

// ===== INFORMACIÓN DEL ESTADO =====

public static boolean isManagerActive() {
    return PersistenceEventManager.isInstanceCreated();
}

public static int getTotalRegisteredListeners() {
    if (PersistenceEventManager.isInstanceCreated()) {
        return PersistenceEventManager.getInstance().getTotalListenerCount();
    }
    return 0;
}

public static Set<Class<?>> getRegisteredEntities() {
    if (PersistenceEventManager.isInstanceCreated()) {
        return PersistenceEventManager.getInstance().getRegisteredEntityClasses();
    }
    return Collections.emptySet();
}

}

  1. EntityListener de JPA (Actualizado)
    java

@component
public class GlobalJpaEventListener {

@PrePersist
public void prePersist(Object entity) {
    if (PersistenceEventManager.isInstanceCreated()) {
        PersistenceEventManager.getInstance().firePrePersist(entity);
    }
}

@PostPersist
public void postPersist(Object entity) {
    if (PersistenceEventManager.isInstanceCreated()) {
        PersistenceEventManager.getInstance().firePostPersist(entity);
    }
}

@PreUpdate
public void preUpdate(Object entity) {
    if (PersistenceEventManager.isInstanceCreated()) {
        PersistenceEventManager.getInstance().firePreUpdate(entity);
    }
}

@PostUpdate
public void postUpdate(Object entity) {
    if (PersistenceEventManager.isInstanceCreated()) {
        PersistenceEventManager.getInstance().firePostUpdate(entity);
    }
}

@PreRemove
public void preRemove(Object entity) {
    if (PersistenceEventManager.isInstanceCreated()) {
        PersistenceEventManager.getInstance().firePreRemove(entity);
    }
}

@PostRemove
public void postRemove(Object entity) {
    if (PersistenceEventManager.isInstanceCreated()) {
        PersistenceEventManager.getInstance().firePostRemove(entity);
    }
}

@PostLoad
public void postLoad(Object entity) {
    if (PersistenceEventManager.isInstanceCreated()) {
        PersistenceEventManager.getInstance().firePostLoad(entity);
    }
}

}

  1. Ejemplos de Implementaciones Concretas
    java

// Listener de Auditoría
public class AuditPersistenceEvent extends PersistenceEvent {

@Override
public void executePrePersist(Object entity) {
    if (entity instanceof Auditable) {
        Auditable auditable = (Auditable) entity;
        auditable.setCreatedAt(LocalDateTime.now());
        auditable.setCreatedBy(getCurrentUser());
    }
}

@Override
public void executePreUpdate(Object entity) {
    if (entity instanceof Auditable) {
        Auditable auditable = (Auditable) entity;
        auditable.setUpdatedAt(LocalDateTime.now());
        auditable.setUpdatedBy(getCurrentUser());
    }
}

@Override
public boolean supports(Class<?> entityClass) {
    return Auditable.class.isAssignableFrom(entityClass);
}

private String getCurrentUser() {
    return "system";
}

}

// Listener Específico para Usuario
public class UsuarioPersistenceEvent extends PersistenceEvent {

@Override
public void executePrePersist(Usuario usuario) {
    if (usuario.getEmail() != null && !usuario.getEmail().contains("@")) {
        throw new IllegalArgumentException("Email inválido para usuario: " + usuario.getEmail());
    }
    usuario.setActivo(true);
}

@Override
public void executePostLoad(Usuario usuario) {
    if (usuario.getNombre() != null && usuario.getApellido() != null) {
        usuario.setNombreCompleto(usuario.getNombre() + " " + usuario.getApellido());
    }
}

@Override
public boolean supports(Class<?> entityClass) {
    return Usuario.class.equals(entityClass);
}

}

  1. Uso del Sistema
    java

@service
public class UsuarioService {

@Autowired
private UsuarioRepository usuarioRepository;

@PostConstruct
public void init() {
    // Registrar eventos estáticamente
    PersistenceEventConfig.registerEvent(Usuario.class, new UsuarioPersistenceEvent());
    PersistenceEventConfig.registerEvent(Object.class, new AuditPersistenceEvent());
}

@PreDestroy
public void cleanup() {
    // Limpiar todos los eventos al destruir el servicio
    PersistenceEventConfig.unregisterAllEventsForEntity(Usuario.class);
}

@Transactional
public Usuario crearUsuario(Usuario usuario) {
    return usuarioRepository.save(usuario);
}

}

// Uso en cualquier parte de la aplicación
@component
public class OtroComponente {

public void configurarEventos() {
    // Registrar nuevo listener dinámicamente
    PersistenceEventConfig.registerEvent(Producto.class, new ProductoPersistenceEvent());
    
    // Verificar estado
    System.out.println("Manager activo: " + PersistenceEventConfig.isManagerActive());
    System.out.println("Listeners registrados: " + PersistenceEventConfig.getTotalRegisteredListeners());
}

public void limpiarEventos() {
    // Desregistrar eventos específicos
    PersistenceEventConfig.unregisterAllEventsForEntity(Producto.class);
    
    // O limpiar todo
    PersistenceEventConfig.unregisterAllEvents();
}

}

  1. Controlador para Gestión
    java

@RestController
@RequestMapping("/api/persistence-events")
public class PersistenceEventController {

@PostMapping("/register/{entityName}")
public String registerEvent(@PathVariable String entityName) {
    try {
        Class<?> entityClass = Class.forName("com.tudominio.entities." + entityName);
        
        // Aquí podrías tener lógica para crear el listener apropiado
        if (entityName.equals("Usuario")) {
            PersistenceEventConfig.registerEvent(entityClass, new UsuarioPersistenceEvent());
        } else if (entityName.equals("Producto")) {
            PersistenceEventConfig.registerEvent(entityClass, new ProductoPersistenceEvent());
        }
        
        return "Evento registrado para: " + entityName;
    } catch (ClassNotFoundException e) {
        return "Entidad no encontrada: " + entityName;
    }
}

@PostMapping("/unregister/{entityName}")
public String unregisterEvent(@PathVariable String entityName) {
    try {
        Class<?> entityClass = Class.forName("com.tudominio.entities." + entityName);
        PersistenceEventConfig.unregisterAllEventsForEntity(entityClass);
        return "Eventos desregistrados para: " + entityName;
    } catch (ClassNotFoundException e) {
        return "Entidad no encontrada: " + entityName;
    }
}

@GetMapping("/status")
public Map<String, Object> getStatus() {
    return Map.of(
        "managerActive", PersistenceEventConfig.isManagerActive(),
        "totalListeners", PersistenceEventConfig.getTotalRegisteredListeners(),
        "registeredEntities", PersistenceEventConfig.getRegisteredEntities()
                    .stream()
                    .map(Class::getSimpleName)
                    .collect(Collectors.toList()),
        "timestamp", LocalDateTime.now()
    );
}

}

🚀 Características Principales del Nuevo Diseño

✅ Clase abstracta en lugar de interface

✅ Singleton pattern para el manager

✅ Gestión automática de memoria (se destruye cuando no hay listeners)

✅ API estática simple para registro/desregistro

✅ Zero config - no necesita @Autowired ni @Component

✅ Lazy initialization - solo se crea cuando se necesita

¿Te gusta este enfoque más simplificado?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    To do

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions