Skip to main content

Columns

In order to define which columns should be returned to the client, we need to use ColDefs objects. Each column that we want to include in the AG Grid response must be explicitly defined in the ColDefs.

Defining Columns​

Each column is defined using a ColDef object.

PropertyTypeDefaultDescription
field (required)SingularAttribute / FieldPath / ComputedField—Where the column reads its value, passed to ColDef.builder(...): a JPA metamodel attribute (e.g. Trade_.price), a FieldPath for nested paths, or a ComputedField for a database expression.
sortablebooleantrueEnables or disables sorting.
filterIFilter—Defines the filter type (no default — a column has no filter unless one is set).
Supports:
✅ Custom IFilter implementations
✅ Built-in filters (e.g., AgNumberColumnFilter)
⚠️ Omit .filter(...) to leave the column without a filter
enableValuebooleanfalseSet to true if you want to be able to aggregate by this column.
enableRowGroup(boolean, Function<String, T>)falseEnable row grouping on this column. Because AG Grid sends group keys as strings, you must also pass a converter from the string group key to the column type T, e.g. .enableRowGroup(true, BigDecimal::new).
enablePivotbooleanfalseSet to true if you want to be able to pivot by this column.
allowedAggFuncsSet<AggregationFunction>All availableDefines allowed aggregation functions.

Example Usage​

var priceColumn = ColDef.builder(Entity_.price)
.sortable(true)
.filter(AgSetColumnFilter.forNumber())
.allowedAggFuncs(AggregationFunction.avg, AggregationFunction.count)
.build();

// no .filter(...) -> this column has no filter
var nameColumn = ColDef.builder(Entity_.name)
.sortable(false)
.build();

QueryBuilder<Entity, Long, Void> queryBuilder = QueryBuilder.builder(Entity.class, Entity_.id, entityManager)
.colDefs(priceColumn, nameColumn)
.build();

Inherited columns​

Attributes declared on a @MappedSuperclass are typed against that superclass in the generated metamodel, and they are accepted everywhere the adapter takes a column or a metamodel attribute — including the primary field.

@MappedSuperclass
public abstract class AuditedRecord {
@Id
private Long recordId;
private String owner;
}

@Entity
public class Invoice extends AuditedRecord {
private String invoiceNumber;
}
// AuditedRecord_.recordId is a SingularAttribute<AuditedRecord, Long>, the grid queries Invoice
QueryBuilder<Invoice, Long, Void> queryBuilder = QueryBuilder.builder(Invoice.class, AuditedRecord_.recordId, entityManager)
.colDefs(
ColDef.builder(AuditedRecord_.recordId).build(),
ColDef.builder(AuditedRecord_.owner).filter(AgSetColumnFilter.forString()).build(),
ColDef.builder(Invoice_.invoiceNumber).build()
)
.build();

The same holds for FieldPath hops, quickFilterSearchInFields, the tree-data and master-detail attributes, and for a ComputedField<AuditedRecord, T> used in a grid over Invoice.

Computed columns​

A column does not have to map to an entity attribute. ComputedField builds the column from a JPA Expression, so the value is produced by the database.

PropertyTypeDescription
nameStringThe column name AG Grid sends and the field name in the response.
javaTypeClass<T>The type the expression evaluates to.
expressionFunctionBiFunction<CriteriaBuilder, Root<? extends E>, Expression<T>>Builds the expression for a given query root.

The expression is resolved wherever the column is used, so a computed column behaves like a mapped one: selecting, filtering, sorting, row grouping, aggregation, pivoting, quick filter, advanced filter and supplySetFilterValues all work on it.

ComputedField<Trade, String> valueBand = ComputedField.<Trade, String>builder()
.name("valueBand")
.javaType(String.class)
.expressionFunction((cb, root) -> cb.<String>selectCase()
.when(cb.greaterThan(root.get(Trade_.currentValue), BigDecimal.ZERO), "POSITIVE")
.otherwise("NON_POSITIVE"))
.build();

QueryBuilder<Trade, Long, Void> queryBuilder = QueryBuilder.builder(Trade.class, Trade_.tradeId, entityManager)
.colDefs(
ColDef.builder(Trade_.tradeId).build(),
ColDef.builder(valueBand)
.filter(AgSetColumnFilter.forString())
.enableRowGroup(true, key -> key)
.build()
)
.build();

Value Change is currentValue - previousValue, Value Band is a CASE over those same two columns and Portfolio / Book concatenates two text columns. None of the three exists on the entity.

  • try filtering or sorting on a computed column, or drag Value Band into the row group panel
  • the Value Band set filter values come from supplySetFilterValues, read straight from the expression
  • Source code for this grid available here
  • Backend source code available here
Loading grid...

Dot notation​

The adapter fully supports columns on nested/related entities. The path is built with FieldPath, chaining .to(...) through the mapped relationships. The resulting column name in the AG Grid response still uses dot notation (e.g. submitter.id).

// submitter.id
ColDef.builder(FieldPath.of(Trade_.submitter).to(Submitter_.id)).build()

// parentTrade.parentTrade.tradeId
ColDef.builder(FieldPath.of(Trade_.parentTrade).to(Trade_.parentTrade).to(Trade_.tradeId)).build()
JPA Requirement

Each hop must correspond to a mapped relationship (e.g., @ManyToOne, @OneToOne) within your JPA Entity. It uses LEFT JOINS to join the table.

  • Trade Id is from main table
  • Submitter Id uses FieldPath.of(Trade_.submitter).to(Submitter_.id), referencing the submitter entity
  • Submitter Deal Id uses FieldPath.of(Trade_.submitterDeal).to(SubmitterDeal_.id), referencing the submitterDeal entity
  • Parent Trade Id uses FieldPath.of(Trade_.parentTrade).to(Trade_.tradeId), referencing the parentTrade entity (same table)
  • Parent Trade -> Parent Trade Id uses FieldPath.of(Trade_.parentTrade).to(Trade_.parentTrade).to(Trade_.tradeId), referencing parent's parent trade
  • Source code for this grid available here
  • Backend source code available here
Loading grid...

Suppress Field Dot Notation - Flat data​

By default, dot notation fields are returned as nested JSON objects to align with AG Grid's default behavior (check response body in previous grid).

To return flat keys (e.g., "category.name": "Value"), you must enable suppressFieldDotNotation in the QueryBuilder.

QueryBuilder<Entity, Long, Void> queryBuilder = QueryBuilder.builder(Entity.class, Entity_.id, entityManager)
.colDefs(
// ...col defs
)
.suppressFieldDotNotation(true)
.build();
Client-Side Configuration

If you enable suppressFieldDotNotation(true) in the backend, you must also set suppressFieldDotNotation to true in your AG Grid options on the frontend to ensure the grid treats dots as literal characters.

  • Source code for this grid available here
  • Backend source code available here
Loading grid...