fieldenms / tg

Trident Genesis
MIT License
14 stars 7 forks source link

Meta-model support for aliases #1855

Closed homedirectory closed 1 year ago

homedirectory commented 2 years ago

Description

Database queries are often constructed with the usage of aliases, which requries prepending the alias to every column name that is referenced in a query ("alias.column"). Entity meta-models, however, provide no ability to add an alias to the constructed dot-notated path. This forces software engineers to rely on String concatenation, which should ultimately be disallowed (or discouraged, at least). For example, currently the following approach is used to combine meta-models and aliases:

select(Timesheet.class).as("t").where().prop("t." + Timesheet_.person())

Expected outcome

Consider the following example:

var timesheet = TimeSheet_("t");
select(Timesheet.class).as(timesheet.alias).where().prop(timesheet.person())...

Accessing a meta-model without an alias should remain as it were, through a static field.

Alternative approaches are discussed in the comments below.

homedirectory commented 2 years ago
  1. Initial approach that makes use of instantiation of generic types through reflection

    Generate a static method in the MetaModels class that would have the following signature:

    public static <T extends EntityMetaModel> T withAlias(String alias, T metaModel)

    Then by using reflection it would be possible to instantiate T with the given alias. This approach, although the implementation is not the simplest, would be very convenient. Consider that it may be used like this:

    String alias = "t";
    var timesheet = MetaModels.withAlias(alias, MetaModels.TimeSheet_);
    select(Timesheet.class).as(timesheet.alias).where().prop(timesheet.person())
  2. Generation of a static method for each meta-model Generate a method withAlias(String) as a static method for each meta-model. Every meta-model would provide its own implementation to ensure that the correct type is returned. The method must be static, in my opinion, in order to avoid causing a potential conflict that would arise if the underlying entity had a property withAlias. In general, it is preferred to limit all instance methods of a meta-model only to those that model the properties of the underlying entity.

    String alias = "t";
    TimeSheetMetaModel timesheet = TimeSheetMetaModel.withAlias(alias);
    select(Timesheet.class).as(timesheet.alias).where().prop(timesheet.person())

    While keeping this in mind, I think that it would still be best to extend the MetaModels class for it to remain the sole interface to meta-models, preserving the simplicity.

homedirectory commented 2 years ago

An interesting approach was proposed by @01es to change the way meta-model instances are accessed in the MetaModels class. The idea is to use method calls instead of static fields. So instead of having this:

public static final PersonMetaModel Person_  = new PersonMetaModel();

The following would be generated:

public static PersonMetaModel Person_() {
    return new PersonMetaModel();
}
public static PersonMetaModel Person_(String alias) {
    return new PersonMetaModel(alias);
}

The non-aliased meta-model should be computed lazily to avoid creating multiple instances that are identical.

01es commented 2 years ago

Yes, so the invocation would look like this:

var alias = "t";
var timesheet = TimeSheet_(alias);
select(Timesheet.class).as(alias).where().prop(timesheet.person())...

or

var alias = "t";
select(Timesheet.class).as(alias).where().prop(TimeSheet_(alias).person())...