Interface DerivedValue<T>

Type Parameters:
T - the value type
All Superinterfaces:
Serializable
Functional Interface:
This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

@FunctionalInterface public interface DerivedValue<T> extends Serializable
Responsible for providing values derived from other attribute values.

Derived attributes compute their values from other attributes within the same entity or from related entities. They provide calculated fields, formatting, concatenation, and other derived values without being stored in the database.

Derived attributes are defined using value providers that receive source values and compute the derived result:

 public class Store extends DomainModel {

     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");

         // Derived attributes
         Attribute<String> FULL_NAME = TYPE.stringAttribute("full_name");
         Attribute<String> DISPLAY_NAME = TYPE.stringAttribute("display_name");
         Attribute<Integer> AGE = TYPE.integerAttribute("age");
         Attribute<String> INITIALS = TYPE.stringAttribute("initials");
     }

     void defineCustomer() {
         Customer.TYPE.as()
             .attributes(
                 Customer.FIRST_NAME.as()
                     .column(),
                 Customer.LAST_NAME.as()
                     .column(),
                 Customer.EMAIL.as()
                     .column(),
                 Customer.BIRTH_DATE.as()
                     .column(),

                 // Simple concatenation
                 Customer.FULL_NAME.as()
                     .derived()
 				             .from(Customer.FIRST_NAME, Customer.LAST_NAME)
                     .with(values -> {
                         String first = values.get(Customer.FIRST_NAME);
                         String last = values.get(Customer.LAST_NAME);
                         return (first != null ? first : "") + " " + (last != null ? last : "");
                     }),

                 // Complex formatting with multiple sources
                 Customer.DISPLAY_NAME.as()
                     .derived()
 				             .from(Customer.FULL_NAME, Customer.EMAIL)
                     .with(values -> {
                         String fullName = values.get(Customer.FULL_NAME);
                         String email = values.get(Customer.EMAIL);
                         return fullName + " (" + email + ")";
                     }),

                 // Age calculation
                 Customer.AGE.as()
                     .derived()
 				             .from(Customer.BIRTH_DATE)
                     .with(values -> {
                         LocalDate birthDate = values.get(Customer.BIRTH_DATE);
                         return birthDate != null ?
                             Period.between(birthDate, LocalDate.now()).getYears() : null;
                     }),

                 // Initials from names
                 Customer.INITIALS.as()
                     .derived()
 				             .from(Customer.FIRST_NAME, Customer.LAST_NAME)
                     .with(values -> {
                         String first = values.get(Customer.FIRST_NAME);
                         String last = values.get(Customer.LAST_NAME);
                         String firstInitial = first != null && !first.isEmpty() ?
                             first.substring(0, 1).toUpperCase() : "";
                         String lastInitial = last != null && !last.isEmpty() ?
                             last.substring(0, 1).toUpperCase() : "";
                         return firstInitial + lastInitial;
                     }))
             .build();
     }
 }

 // Usage
 Entity customer = entities.entity(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"
 String displayName = customer.get(Customer.DISPLAY_NAME); // "John Doe (john.doe@example.com)"
 Integer age = customer.get(Customer.AGE);                 // Calculated age
 String initials = customer.get(Customer.INITIALS);       // "JD"
See Also:
  • Method Details

    • from

      Must be total, that is, return a value or null for every combination of source values, including all null, which a new entity presents. A cached derived value is computed for every attribute of an entity being made immutable, not only for the ones a caller happens to read, so a provider throwing on some combination throws from Entity.immutable() rather than from the read it would once have failed.

      Parameters:
      values - the source values, mapped to their respective attributes
      Returns:
      the derived value
    • sourceValues

      static DerivedValue.SourceValues sourceValues(Attribute<?> derivedAttribute, Map<Attribute<?>,Object> values)
      Parameters:
      derivedAttribute - the derived attribute
      values - the values
      Returns:
      a new DerivedValue.SourceValues instance