[hibernate-dev] HHH-11791

Christian Bongiorno christian.bongiorno at gmail.com
Fri Jun 23 00:12:58 EDT 2017


On Mon, Jun 12, 2017 at 11:02 PM Christian Beikov <
christian.beikov at gmail.com> wrote:

> Hi,
>
> this might work for a limited set of use cases, but what exactly are you
> expecting from that approach? Performance might be a bit better, but
> that's probably negligible. Apart from that, I only see "encapsulation"
> or "ease of use with other languages" being your argument. Is that
> correct, or are there other reasons why you think constructor injection
> is a good fit here?
>
>
Encapsulation is one of the OO principles (PIE) so maintaining it is a good
thing as it prevents
unintended state change. Having a class with only 'getters' means that, for
example, if an instance of that class is used in a HashSet, there is no
`setter` that could subsequently alter the hashCode. This is a common
problem with Java and big reason project lombok exists.
Using constructor injection honors this.

My reasoning for this feature is definitely to support integration with
Kotlin which is very strict on encapsulation. When you declare something
`val foo:String` in Kotlin it is 100% immutable. Attempting to simply set
field access to `true` on foo won't help. It does, however, generate a
constructor for all declared parameters in data classes (AKA: Pojos) so
this is the obvious way to respect (true) immutability and still make use
of an ORM like hibernate.


> As soon as you have circular references between entities, you need a
> hybrid approach with setters for some attributes. How would you do lazy
> initialization of entities when some fields don't have a setter?
>
>
Circular references present a thorny issue for sure that can't simply be
resolved with lazy hydration; this is the eager approach. In my opinion,
and exception is in order (and, a nieve implementation of this feature
would naturally generate a stack overflow).


> I could imagine that a constructor is annotated with a marker annotation
> to make it selectable for this kind of hydration and allow the
> parameters of that constructor to be annotated to bind them to entity
> attributes. By default, the parameter names could be made use of to
> avoid cluttering code. With that information, you can select the
> constructor at boot time for entity types. After all, we know what kind
> of SQL is generated, it's based on the field metadata, so no need for
> the runtime overhead.
>
> I don't know what others think, but when thinking about a possible
> implementation, this seems like a real deep cut that touches many places.
>

You certainly would know better than me on the difficulty of implementation.


>
>
> Mit freundlichen Grüßen,
> ------------------------------------------------------------------------
> *Christian Beikov*
> Am 12.06.2017 um 23:47 schrieb Christian Bongiorno:
> > To work, yes. This would be an optional means of injecting. I was
> thinking
> > some code like this (very very rough):
> >
> > public void prep(DataSource source)
> >      throws SQLException, NoSuchMethodException,
> > IllegalAccessException, InvocationTargetException,
> > InstantiationException {
> >
> >    ResultSet resultSet = source.getConnection().createStatement()
> >        .executeQuery("select * from User where false");
> >    ResultSetMetaData metaData = resultSet.getMetaData();
> >    Set<String> columnNames = new HashSet<>();
> >
> >    for(int i = 1; i < metaData.getColumnCount(); i++) {
> >      columnNames.add(metaData.getColumnName(i));
> >
> >    }
> >
> >    Constructor<?> toUse =
> Arrays.stream(User.class.getDeclaredConstructors())
> >        .filter(c -> c.getParameterCount() == columnNames.size())
> >        .filter(ctr ->
> Arrays.stream(ctr.getParameters()).map(Parameter::getName)
> >
> .allMatch(columnNames::contains)).findFirst().orElse(User.class.getConstructor());
> >
> >    Object entity = null;
> >    if(toUse != User.class.getConstructor()) {// means we found a
> > matching constructor
> >      Object[] input = new Object[toUse.getParameterCount()];
> >      int i = 0;
> >      for (Parameter parameter : toUse.getParameters()) {
> >        input[i++] = resultSet.getObject(parameter.getName());
> >      }
> >      entity = toUse.newInstance(input);
> >    }
> >    else {
> >      entity = toUse.newInstance();
> >    }
> > }
> >
> >
> > One question that comes up is which data set is the master of the match?
> >
> > Do we "Select the constructor that matches the ResultSet?" or "Do we
> > select the result set that matches the constructor?"
> >
> > Another thought is to use the existing field selection logic (find
> > properties on the Class) and then look for the matching constructor;
> which
> > is a very common paradigm.
> >
> > Finally: An annotation specifically indicating this might also be in
> order.
> >
> > Just getting the discussion started
> >
> >
> > On Mon, Jun 12, 2017 at 1:41 PM Vlad Mihalcea <mihalcea.vlad at gmail.com>
> > wrote:
> >
> >> Hi,
> >>
> >> Wouldn't that require all entities be compiled with?
> >>
> >> javac -parameters
> >>
> >> Vlad
> >>
> >> On Mon, Jun 12, 2017 at 10:19 PM, Christian Bongiorno <
> >> christian.bongiorno at gmail.com> wrote:
> >>
> >>> Now that java 8 supports named parameters it becomes possible
> (potentially
> >>> preferrable) to use constructor injection instead of circumventing
> >>> encapsulation to set values on private fields.
> >>>
> >>> This shows itself as a potential win when integrating with Kotlin with
> >>> disallows the circumvention quite forcefully. Meaning: without
> constructor
> >>> injection the object needs setters. And, if it has setters then it's
> >>> mutable which is against best practices.
> >>>
> >>> I propose optionally using constructor injection when marshalling an
> >>> object
> >>> from data sources in a DB. I am willing to make the changes if I know
> they
> >>> can/will be incorporated.
> >>>
> >>> Thoughts? Here is the ticket
> >>> https://hibernate.atlassian.net/browse/HHH-11791
> >>>
> >> _______________________________________________
> >>> hibernate-dev mailing list
> >>> hibernate-dev at lists.jboss.org
> >>> https://lists.jboss.org/mailman/listinfo/hibernate-dev
> >>>
> >>
> > _______________________________________________
> > hibernate-dev mailing list
> > hibernate-dev at lists.jboss.org
> > https://lists.jboss.org/mailman/listinfo/hibernate-dev
>
> _______________________________________________
> hibernate-dev mailing list
> hibernate-dev at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/hibernate-dev


More information about the hibernate-dev mailing list