Repositorio 24.00
El patrón Repositorio en webforJ proporciona una forma estandarizada de gestionar y consultar colecciones de entidades. Actúa como una capa de abstracción entre tus componentes de UI y los datos, facilitando el trabajo con diferentes fuentes de datos mientras se mantiene un comportamiento consistente.
Por qué usar repositorio
Repositorio elimina las actualizaciones manuales mientras mantiene tus datos originales intactos:
// Sin Repositorio - actualizaciones manuales
List<Customer> customers = loadCustomers();
Table<Customer> table = new Table<>();
table.setItems(customers);
// Agregar requiere recargar todo
customers.add(newCustomer);
table.setItems(customers); // Debe recargar todo
// Con Repositorio - sincronización automática
List<Customer> customers = loadCustomers();
CollectionRepository<Customer> repository = new CollectionRepository<>(customers);
Table<Customer> table = new Table<>();
table.setRepository(repository);
// Agregar se sincroniza automáticamente
customers.add(newCustomer);
repository.commit(newCustomer); // Solo actualiza lo que cambió
Repositorio de colección
El CollectionRepository es la implementación más común y envuelve cualquier Colección de Java:
// Desde ArrayList
List<Customer> customers = new ArrayList<>();
CollectionRepository<Customer> customerRepo = new CollectionRepository<>(customers);
// Desde HashSet
Set<String> tags = new HashSet<>();
CollectionRepository<String> tagRepo = new CollectionRepository<>(tags);
// Desde cualquier Colección
Collection<Employee> employees = getEmployeesFromHR();
CollectionRepository<Employee> employeeRepo = new CollectionRepository<>(employees);
Sincronización de datos
El Repositorio actúa como un puente entre tus datos y los componentes de UI. Cuando los datos cambian, notificas al repositorio a través del método commit():
List<Product> products = new ArrayList<>();
CollectionRepository<Product> repository = new CollectionRepository<>(products);
// Agregar nuevo producto
Product newProduct = new Product("P4", "Gizmo", 79.99, 15);
products.add(newProduct);
repository.commit(); // Todos los componentes conectados se actualizan
// Actualizar producto existente
products.get(0).setPrice(89.99);
repository.commit(products.get(0)); // Solo actualiza esta fila específica
// Eliminar producto
products.remove(2);
repository.commit(); // Refresca la vista
El método commit tiene dos firmas:
commit()- Indica al repositorio que refresque todo. Dispara unRepositoryCommitEventcon todos los datos actualescommit(entity)- Apunta a una entidad específica. El repositorio encuentra esta entidad por su clave y actualiza solo los elementos de UI afectados
Esta distinción importa para el rendimiento. Cuando actualizas un campo en una tabla de 1000 filas, commit(entity) actualiza solo esa celda, mientras que commit() refrescaría todas las filas.
Filtrando datos
El filtro del repositorio controla qué datos fluyen hacia los componentes conectados. Tu colección subyacente permanece sin cambios porque el filtro actúa como una lente:
// Filtrar por disponibilidad de stock
repository.setBaseFilter(product -> product.getStock() > 0);
// Filtrar por categoría
repository.setBaseFilter(product -> "Electrónica".equals(product.getCategory()));
// Combinar múltiples condiciones
repository.setBaseFilter(product ->
product.getCategory().equals("Electrónica") &&
product.getStock() > 0 &&
product.getPrice() < 100.0
);
// Limpiar filtro
repository.setBaseFilter(null);
Cuando estableces un filtro, el Repositorio:
- Aplica el predicado a cada elemento de tu colección
- Crea un flujo filtrado de elementos coincidentes
- Notifica a los componentes conectados de que actualicen su visualización
El filtro persiste hasta que lo cambies. Los nuevos elementos añadidos a la colección se prueban automáticamente contra el filtro actual.
Trabajando con claves de entidad
El repositorio necesita identificar entidades de manera única para soportar operaciones como find() y commit(entity). Hay dos formas de definir cómo se identifican las entidades:
Usando la interfaz HasEntityKey
Implementa HasEntityKey en tu clase de entidad:
public class Customer implements HasEntityKey {
private String customerId;
private String name;
private String email;
@Override
public Object getEntityKey() {
return customerId;
}
// Constructor y getters/setters...
}
// Buscar por clave
Optional<Customer> customer = repository.find("C001");
// Actualizar cliente específico
customer.ifPresent(c -> {
c.setEmail("newemail@example.com");
repository.commit(c); // Solo se actualiza la fila de este cliente
});
Usando proveedor de clave personalizado 25.10
Para entidades donde no puedes o no quieres implementar HasEntityKey (como las entidades JPA), utiliza setKeyProvider():
@Entity
public class Product {
@Id
private Long id;
private String name;
private double price;
// Entidad gestionada por JPA
}
// Configurar repositorio para usar el método getId()
CollectionRepository<Product> repository = new CollectionRepository<>(products);
repository.setKeyProvider(Product::getId);
// Ahora find funciona con el ID
Optional<Product> product = repository.find(123L);
Elegir un enfoque
Ambos enfoques funcionan, pero setKeyProvider() es preferido cuando:
- Se trabaja con entidades JPA que tienen campos
@Id - No puedes modificar la clase de entidad
- Necesitas diferentes estrategias de clave para diferentes repositorios
Usa HasEntityKey cuando:
- Controlas la clase de entidad
- La lógica de extracción de clave es compleja
- Quieres que la entidad defina su propia identidad
Integración de UI
Repositorio se integra con componentes conscientes de los datos:
// Crear repositorio y tabla
List<Customer> customers = new ArrayList<>();
CollectionRepository<Customer> repository = new CollectionRepository<>(customers);
Table<Customer> table = new Table<>();
table.setRepository(repository);
table.addColumn("ID", Customer::getId);
table.addColumn("Nombre", Customer::getName);
table.addColumn("Correo", Customer::getEmail);
// Añadir datos - la tabla se actualiza automáticamente
customers.add(new Customer("C001", "Alice Johnson", "alice@example.com"));
repository.commit();
Próximos pasos
📄️ Querying data
La interfaz QueryableRepository extiende Repository con consultas avanzadas a través de RepositoryCriteria. A diferencia de los repositorios básicos que solo admiten filtrado simple, los repositorios consultables proporcionan consultas estructuradas con tipos de filtro personalizados, ordenamiento y paginación.
📄️ Custom data sources
Cuando tus datos están fuera de tu aplicación - en una API REST, base de datos o servicio externo - necesitas crear una implementación de repositorio personalizada. La clase DelegatingRepository hace esto sencillo al permitirte proporcionar funciones en lugar de implementar una clase completa.
📄️ Events and updates
Los eventos de Repository te permiten reaccionar a cambios de datos. Más allá de las actualizaciones automáticas de la UI, puedes escuchar cambios para disparar lógica personalizada.