Interface DerivedAttributeDefinition<T>

Type Parameters:
T - the underlying type
All Superinterfaces:
AttributeDefinition<T>

public interface DerivedAttributeDefinition<T> extends AttributeDefinition<T>
A definition for attributes which value is derived from the values of one or more attributes.

DerivedAttributeDefinition configures attributes that compute their values from other attributes within the same entity or from related entities. These attributes provide calculated fields, formatting, aggregation, and other computed values.

Derived attributes can be cached for performance or computed on-demand:

 public class Store extends DefaultDomain {

     interface Customer {
         EntityType TYPE = DOMAIN.entityType("store.customer");

         Column<String> FIRST_NAME = TYPE.stringColumn("first_name");
         Column<String> LAST_NAME = TYPE.stringColumn("last_name");
         Column<String> EMAIL = TYPE.stringColumn("email");
         Column<LocalDate> BIRTH_DATE = TYPE.localDateColumn("birth_date");
         Column<String> PHONE = TYPE.stringColumn("phone");

         // Derived attributes
         Attribute<String> FULL_NAME = TYPE.stringAttribute("full_name");
         Attribute<String> CONTACT_INFO = TYPE.stringAttribute("contact_info");
         Attribute<Integer> AGE = TYPE.integerAttribute("age");
         Attribute<String> NAME_UPPER = TYPE.stringAttribute("name_upper");
     }

     void defineCustomer() {
         Customer.TYPE.define(
                 Customer.FIRST_NAME.define()
                     .column(),
                 Customer.LAST_NAME.define()
                     .column(),
                 Customer.EMAIL.define()
                     .column(),
                 Customer.BIRTH_DATE.define()
                     .column(),
                 Customer.PHONE.define()
                     .column(),

                 // Simple derived attribute (cached by default)
                 Customer.FULL_NAME.define()
                     .derived(Customer.FIRST_NAME, Customer.LAST_NAME)
                     .provider(values -> {
                         String first = values.get(Customer.FIRST_NAME);
                         String last = values.get(Customer.LAST_NAME);
                         if (first == null && last == null) {
                             return null;
                         }
                         return ((first != null ? first : "") + " " +
                                 (last != null ? last : "")).trim();
                     })
                     .caption("Full Name"),

                 // Multi-source derived attribute with caching disabled
                 Customer.CONTACT_INFO.define()
                     .derived(Customer.FULL_NAME, Customer.EMAIL, Customer.PHONE)
                     .provider(values -> {
                         String name = values.get(Customer.FULL_NAME);
                         String email = values.get(Customer.EMAIL);
                         String phone = values.get(Customer.PHONE);

                         StringBuilder contact = new StringBuilder();
                         if (name != null) contact.append(name);
                         if (email != null) {
                             if (contact.length() > 0) contact.append(" - ");
                             contact.append(email);
                         }
                         if (phone != null) {
                             if (contact.length() > 0) contact.append(" - ");
                             contact.append(phone);
                         }
                         return contact.toString();
                     })
                     .cached(false) // Compute on each access
                     .caption("Contact Information"),

                 // Time-dependent derived attribute (not cached)
                 Customer.AGE.define()
                     .derived(Customer.BIRTH_DATE)
                     .provider(values -> {
                         LocalDate birthDate = values.get(Customer.BIRTH_DATE);
                         return birthDate != null ?
                             Period.between(birthDate, LocalDate.now()).getYears() : null;
                     })
                     .cached(false) // Age changes over time
                     .caption("Age"),

                 // Formatting derived attribute
                 Customer.NAME_UPPER.define()
                     .derived(Customer.FULL_NAME)
                     .provider(values -> {
                         String fullName = values.get(Customer.FULL_NAME);
                         return fullName != null ? fullName.toUpperCase() : null;
                     })
                     .caption("Name (Uppercase)"))
             .build();
     }
 }

 // Usage examples
 Entity customer = entities.builder(Customer.TYPE)
     .with(Customer.FIRST_NAME, "John")
     .with(Customer.LAST_NAME, "Doe")
     .with(Customer.EMAIL, "john.doe@example.com")
     .with(Customer.BIRTH_DATE, LocalDate.of(1990, 5, 15))
     .build();

 // Derived values are computed automatically
 String fullName = customer.get(Customer.FULL_NAME);         // "John Doe" (cached)
 String contactInfo = customer.get(Customer.CONTACT_INFO);   // Computed each time
 Integer age = customer.get(Customer.AGE);                   // Current age
 String nameUpper = customer.get(Customer.NAME_UPPER);       // "JOHN DOE"

 // Modifying source attributes updates derived values
 customer.set(Customer.FIRST_NAME, "Jane");
 String newFullName = customer.get(Customer.FULL_NAME);      // "Jane Doe"
See Also:
  • Method Details

    • sources

      List<Attribute<?>> sources()
      Returns:
      the source attributes this attribute derives from.
    • provider

      Returns:
      the value provider, providing the derived value
    • cached

      boolean cached()
      Note that cached attributes are included when an entity is serialized.
      Returns:
      true if the value of this derived attribute is cached, false if computed on each access