Skip to content

AutoCrud<T>

AutoCrud<T> uses a single type parameter T for filters, rows, view, and edit forms. It is the fastest way to get a working CRUD on screen. Capability annotations let you strip write operations one by one — turning the same class into a listing, a read-only catalogue, or any combination in between.


public abstract class AutoCrud<T extends Identifiable>
extends FilteredAutoCrud<T, T>
MethodPurpose
repository()Return the CrudRepository<T> used for all operations

@Service
@UI("/products")
public class ProductCrud extends AutoCrud<Product> {
private final ProductRepository repository;
public ProductCrud(ProductRepository repository) {
this.repository = repository;
}
@Override
public CrudRepository<Product> repository() {
return repository;
}
}
public record Product(
@EditableOnlyWhenCreating @NotEmpty String id,
@NotEmpty String name,
BigDecimal price,
ProductStatus status
) implements Identifiable {}

This gives you:

RouteScreen
/productsListing with New and Delete buttons
/products/:idRead-only detail view with Edit button
/products/:id/editEdit form
/products/newCreate form

Add any combination of these class-level annotations to restrict what users can do:

AnnotationEffect
@ReadOnlyShorthand for @NotCreatable @NotEditable @NotDeletable
@NotCreatableHides the New button in the list
@NotEditableHides the Edit button in the detail view
@NotDeletableHides the Delete button in the list
@NotNavigableHides the View button column — rows are not clickable

These combine freely. A few common patterns:

IntentAnnotations
Full CRUD(nothing)
Read-only with detail view@ReadOnly
Simple read-only list (no detail)@ReadOnly @NotNavigable
List you can add to, but not click into@NotNavigable
List you can edit but not create@NotCreatable
List you can edit but not delete@NotDeletable
@Service
@UI("/audit-log")
@ReadOnly
@NotNavigable
public class AuditLog extends AutoCrud<AuditEntry> {
private final AuditRepository repository;
public AuditLog(AuditRepository repository) {
this.repository = repository;
}
@Override
public CrudRepository<AuditEntry> repository() {
return repository;
}
}

The write operations are simply never invoked for a read-only AutoCrud.


OperationBehaviour
searchDelegates to repository().find(searchText, filters, pageable)Page<T> (default: filters by Searchable.searchableText()/toString(), sorts by pageable.sort(), paginates in memory)
getViewLoads entity by id and wraps in AutoNamedView
getEditorSame as view — entity fields become editable inputs
getCreationFormInstantiates a new T and wraps in AutoNamedView
deleteAllByIdDelegates to repository().deleteAllById()

For most use cases repository() is the only method you need to implement. To push search, filtering, sorting and pagination to the database, override CrudRepository.find(...)AutoCrud calls it automatically. To customise other operations (pre-populated creation forms, etc.) override the protected hooks fetchRows(), buildNamedView(), or buildCreationForm() directly in your subclass — see Customising AutoCrud behaviour.


AutoCrud<T> uses T for every screen, which means:

  • The filter bar shows the same fields as the grid rows.
  • The edit form shows the same fields as the detail view.
  • The create form is identical to the edit form.

When this is too restrictive, move to: