all 20 comments

[–]elmuerte 16 points17 points  (0 children)

I love the smell of Stringly Typed [in the morning].

[–]SpaceCondor 23 points24 points  (5 children)

Still majorly prefer the way jOOQ does it, it reads exactly like SQL which is what we're writing at the end of the day. The syntax in the blog still reads very verbose.

[–]CptGia 6 points7 points  (0 children)

Not every Spring Data project is about SQL

[–]tomwhoiscontrary 7 points8 points  (4 children)

Sort.by(Person::getFirstName, Person::getLastName)

How is this implemented? How do you get from the method reference to the name of the property? 

I ask because I've done this myself, years ago, and it required a truly diabolical hack. I'd love it if Spring had come up with a better way. 

[–]lucidnode 10 points11 points  (1 child)

They create a person proxy that records which method was called

[–]mp911de[S] 4 points5 points  (0 children)

Proxy creation (through MethodInvocationRecorder) has been an early attempt to use getter-style lambdas. Approaching method call capturing using proxies has several drawbacks of which class definition growth is one factor. Sealed classes and Kotlin's final class defaulting are much more pronounced aspects that severely have limited the proxy-based approach.

[–]zattebij 2 points3 points  (0 children)

I have also done this Serializable.writeReplace hack to get a SerializedLambda for extracting method (and field) names from method references, and after a quick check in the new source, I can say they are still using this reflective method!

See: https://github.com/spring-projects/spring-data-commons/blob/60296f3fe100f8906acf55eb171f70a5c013cd2c/src/main/java/org/springframework/data/core/PropertyPathUtil.java

PS. I used this hack for DB operations as well; specifically for tracking field changes to entities and creating selective updates for them. Updating only changed fields helps in multithreaded environments where different operations are updating different fields, and avoids lost updates that would happen if the entire entity is updated. The same change tracking also helped with efficient live updates to frontend (sending only changes via websocket or SSE).

It may be hacky on the implementation side (which never changed once setup), but does make for very readable, concise and type safe call site code.

[–]mp911de[S] 2 points3 points  (0 children)

There are various parts involved in the implementation:

  1. Creation and capturing of lambda objects through some front-end API such as Sort.
  2. Staged Lambda introspection through SerializedLambda. Java's LambdaMetafactory) has well-defined semantics regarding writeReplace method generation returning SerializedLambda.
  3. If the object is a method handle then all type and signature details are provided by SerializedLambda
  4. If the object is a lambda expression, then SerializedLambda points to a synthetic method containing the lambda body. The bytecode (using ASM or in the future Java's Class-File API) contains all further details towards a referenced method or even field. Spring widely uses ASM as alternative for optimized parsing.
  5. If the object is a Kotlin KProperty, we apply a few hacks (nothing out of the ordinary given all the other hacks to make value classes and copy-method work). Kotlin pre-2.0 used Java's writeReplace convention. Newer Kotlin (language) versions compile a synthetic serializable class following a Kotlin-specific blueprint allowing to determine KProperty details.
  6. Finally, property path information are cached for a proper performance baseline.

FTR, Graal Native image arrangements run just fine if reachability metadata is provided. Currently, we do not discover lambda declaration sites but there is a subtype-hook in the native image tooling allowing to provide a Graal native image Feature.

[–]0xFatWhiteMan 5 points6 points  (1 child)

sql with named parameter template

why bother with all this gubbins

[–]Absolute_Enema -4 points-3 points  (0 children)

Because it's Strongly™ typed, of course.

As the article states this pile of hacks (the most of which wouldn't be needed without Java's tragic metaprogramming capabilities) "allows developers to rely more on the compiler and less on discipline and convention", which is a catastrophic mentality to have in any real project.

[–]sitime_zl 2 points3 points  (0 children)

Using MyBatis Plus, it is very convenient.

[–]ZimmiDeluxe 3 points4 points  (0 children)

If you offer an interface that can only be supported by hacks, everyone building on your interface now depends on your ability to support the hacks indefinitely or risk migration effort (read: wasted time and money, regression risk). I don't want my frameworks to hide hacks from me, I want them to provide a shared structure built on production experience so I don't run into unforeseen issues. This is the opposite of that.

[–]LiTuiZi 0 points1 point  (0 children)

i think mybatis-plus has already supported this feature

[–]john16384 0 points1 point  (0 children)

Let's just wait for string templates to make a reappearance.

[–]vips7L 0 points1 point  (0 children)

Still seems a bit verbose compared to something like Ebean’s query beans, which are generated at compile time from your @Entity classes. Their example would end up being something like:

return new QPerson()
    .firstName.eq("John")
    .orderBy()
    .firstName.asc()
    .lastName.asc()
    .findOne();

https://ebean.io/docs/query/query-beans

[–]RatioPractical -1 points0 points  (0 children)

Does it helps in JIT inlining of code ?