Property Field Name as Index Field Name in PropertyBridge

Hello,

I wanted to tap a bit into the deeper stuff like PropertyBridges. I think I understand the concepts but am stuck at some point with the Binder Java-API.

How to use the field name in the entity that uses the Property with the Annotation as index field name? Like the default behavior in ValueBridge. I’m confused: Why cant i type context.bridgedElement().name() ?

My use case would be Enums with an additional (e.g.) priority Value, e.g.:

enum AnimalType implements Prioritized {
   DOG(prio: 1),
   CAT(prio: 2),
   HORSE(prio: 3),
   ...
}
@Entity
public class Pet {
  @PropertyBinding(...)
  AnimalType type;
}

Now i want an index like:

pet.type = "DOG"
pet.type.sort = 1

pet.type = "CAT"
pet.type.sort = 2

I know i can use the good and great getter+@Indexingdependcy+@GenericField) in my entity. But what i have many dozens of differrent enums like this and i want a more general solution without having to write getters all over the place. Seems to me like PropertyBinder is the answer.

public class EnumTypeBinder implements PropertyBinder {

    public static final String SORT_APPENDIX = "sort";

    @Override
    public void bind(TypeBindingContext context) {
        context.dependencies().useRootOnly();
        context.bridgedElement().property("???").name();
        context.indexSchemaElement()
                .objectField("<property name>")
                //...
                .toReference();
        //
    }

I’m confused: Why cant i type context.bridgedElement().name()?

First, be aware that you cannot do this:

pet.type would be both an object field (composed of other fields) and a value field (with a string value, e.g. CAT), and that’s not allowed.

At best, you can do this:

pet.type.value = "DOG"
pet.type.sort = 1

pet.type.value = "CAT"
pet.type.sort = 2

Regarding your question:

Your code doesn’t compile. You want to implement a PropertyBinder, but you obviously started from a TypeBinder and forgot to change the signature of the bind method.

This:

Should look like this:

public void bind(PropertyBindingContext context) {

And then you’ll be able to call context.bridgedElement().name().

As to the different between TypeBinder and PropertyBinder, well… it’s simply that a TypeBinder is applied to a type:

// Will declare field "value" and field "sort"
@TypeBinding(binder = @TypeBinderRef(type = MyTypeBinder.class))
enum AnimalType implements Prioritized {
   DOG(prio: 1),
   CAT(prio: 2),
   HORSE(prio: 3),
   ...
}
@Entity
public class Pet {
  // Will embed fields declared by the type binder
  // and thus will add fields "type.value" and "type.sort"
  @IndexedEmbedded
  AnimalType type;
}

While a PropertyBinder is applied to a property:

@Entity
public class Pet {
  // Will declare fields "type.value" and "type.sort" directly
  @PropertyBinding(binder = @PropertyBinderRef(type = MyPropertyBinder.class))
  AnimalType type;
}

The PropertyBinder has access to a bit more information (the property name, among others), but it can only use data from a single property (the one it’s applied to). The type binder can use data from all properties of the type it’s applied to.

Hopefully the reference documentation explains this a bit better:

If you think something is missing in the documentation, please tell me, so that I can improve it.

Ah, thanks! So I messed up at the TypeBindingContext vs PropertyBindingContext part because of copy & past. How stupid :nerd_face: :sweat_smile:

I think the docs are great regarding that part. I would consider adding an example at that place of field building when to choose an object and/or a single field with an example how the index will look like and that mixed is not possible.

Also, if you want to make things look nice in your mapping, be aware that you can define your own mapping annotations that will apply your bridge. You can find an example here: the example defines attributes on the annotation to pass them to the binder, but you can totally define an annotation without any attribute.

For example you could define an annotation named @PrioritizedEnumField (with the proper meta-annotations) and then do this in your mapping:

@Entity
public class Pet {
  // Will declare fields "type.value" and "type.sort" directly
  @PrioritizedEnumField
  AnimalType type;
}
1 Like

Side Question: Why is no Type Check in the Examples/Docs? I’d assume it’s a default best practice for many cases.

I’ve found PojoModelElement.isAssignableTo(Class<?> clazz);.

Mostly because the examples are quite complex as they are, so I was struggling to make them as simple as possible… but yes, you should perform type checks where possible.

Ok. For me, personally it would have reduced the complexity, as it would have been clear what type is operated on in what part of the code.