On Mon, Jun 12, 2017 at 11:02 PM Christian Beikov <
christian.beikov(a)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(a)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(a)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(a)lists.jboss.org
>>>
https://lists.jboss.org/mailman/listinfo/hibernate-dev
>>>
>>
> _______________________________________________
> hibernate-dev mailing list
> hibernate-dev(a)lists.jboss.org
>
https://lists.jboss.org/mailman/listinfo/hibernate-dev
_______________________________________________
hibernate-dev mailing list
hibernate-dev(a)lists.jboss.org
https://lists.jboss.org/mailman/listinfo/hibernate-dev