News for hackers. Stuff that matters.
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by gabereiser on Wednesday August 16, 2023 @12:37PM (#37149661)
No, you won't. People who gripe about spring didn't bother to do things the spring way. If you wrote your own DAL, you did it wrong. JpaRepositories and the like are the way. If you wrote your own JSON serializer/deserializer you did it wrong, Spring includes Jackson and will do it for you (YMMV). If you rolled your own authentication, you did it wrong. Spring Security can magically lock down your app with as little as a single configuration method, providing jwt/oauth/openid-connect enterprise security out of the box.

The issue with spring boot is not it's feature-set or it's robustness. It's with it's heavy handedness to force you down the "spring way". Annotation driven. Extending provided interfaces. Making it "sticky" and impossible to remove. Want to introduce gRPC? yeah, you're going to have to find a way to weave that into Netty or use a different port. It's robust enough just up unto a point where you hit a roadblock and have to dig deep deep into Spring land to understand whats going on.

It's still the best framework for standard crud web api's and such with Java. Though micronauts is quickly approaching. You can't go wrong choosing spring boot in 2023 if your intention is to ship in 2024. Whether or not that codebase is solid in 2027 is another matter.

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by yAak on Wednesday August 16, 2023 @01:27PM (#37150458)
Sorta random, but I really appreciate this kind of post on HN. People have tons of domain expertise and hard-earned lessons that get shared like this, and it broadens my awareness without requiring any time investment on my part.

Thank you, thank you (and others like you) for taking the time to type this up!

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by gabereiser on Wednesday August 16, 2023 @06:43PM (#37154873)
I’m shocked actually that you don’t get this kind of mentorship where you work. What’s your Director doing? Not that we’ll be building spring apps but knowing where we, as an industry, have came from and tried things, we’ll avoid them in the future. My heart sinks when a good idea on paper, but bad IRL, get reimplemented 10 years later because someone didn’t bother to read history. That said, sometimes time is all you need for tech to catch up. Rarely that’s the case though.

So yeah, you’re welcome, thanks for coming to my grey beard tech talk. Ask your managers for more brown bag/lunch & learns from the older folks at your company - they have stories.

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by guilherme-puida on Wednesday August 16, 2023 @07:45PM (#37155372)
> I’m shocked actually that you don’t get this kind of mentorship where you work. What’s your Director doing?

Bold of you to assume that every company has a technically competent director (or that higher-ups even care about mentorship).

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by gabereiser on Wednesday August 16, 2023 @07:59PM (#37155475)
I’m sorry. Really. You should be valued enough to warrant skilling up. Future road maps depend on it.
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by sorokod on Wednesday August 16, 2023 @01:51PM (#37150884)
Annotations are great until something breaks.

Fundamentally they are an abstraction over a rats nest of code (of a specific version), library dependencies and configuration. Gods help you when something breaks, the abstraction leaks and you are presented with a glimpse of the underlying complexity that is now yours to tinker with.

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by gabereiser on Wednesday August 16, 2023 @06:35PM (#37154805)
Oh don’t get me started when competing data source beans are defined. Which one will @Repository pick up? Which one will it use at runtime? Why is my stack trace 245 deep?
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by whartung on Wednesday August 16, 2023 @01:57PM (#37150974)
I can't speak to Spring, and I've never worked with it directly.

I'm from the Java/Jakarta EE parallel universe. Where everything is the same, but different. Both universes have impacted each other is various ways.

JEE has a lot of baggage and notoriety, but, to be fair, so does Spring as they both came up in the same domain at the same time. The XML Hell that JEE was notorious for afflicted Spring as well back in the day. Spring has been more nimble and brought many things to light before they hit the JEE standard, but JEE marches to a different drummer.

Modern JEE is pretty darn good. JAX-RS for web services is really nice, and there's a lot of different deployment options. A lot of the things you can love in Spring you can find in JEE, a lot of the things you can hate in JEE you can find in Spring.

Again, not able to speak to Spring, but JEE has been really stable over time. In that old code continues to work even in new containers. There's a good chance that could even deploy an old, creaky "J2EE" application from 20 years ago in a modern container.

The most disruptive thing to happen to JEE was the Jakartatization of it when it moved from Java EE to Jakarata EE, and all of the namespace changes that happened between Java EE 8 and Jakarta EE 9 with the move from Oracle to Eclipse. But even then, you can still deploy old code in new containers. This was far less disruptive than it could have been, frankly.

I'm certainly not going to suggest to not use Spring, but at the same time, I wouldn't discard JEE out of hand either. It's also an exciting space (for assorted values of "exciting" and "enterprise") for back end work.

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by TedDoesntTalk on Wednesday August 16, 2023 @05:09PM (#37153879)
> There's a good chance that could even deploy an old, creaky "J2EE" application from 20 years ago in a modern container.

Just no. No. All the different types of EJBs… won’t work. Neither will SOAP endpoints. And a shit ton of other stuff that JEE dropped. What are you talking about? Even XML parsing moved packages.

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by gabereiser on Wednesday August 16, 2023 @06:34PM (#37154791)
He must still be stuck on JBoss at work…
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by mrbonner on Wednesday August 16, 2023 @09:24PM (#37156005)
Spring was the reason J2EE is better today. It is the reason for existence of Interface21 if you can remember. I think your bad thoughts are tied to the J2EE monster and dumpster fire that goes with it.
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by zapnuk on Wednesday August 16, 2023 @02:05PM (#37151125)
Sometimes the spring way is also just ugly, or confusing if you just want to do thing X. This code here is used for making HTTP requests using OAuth2. There are probably other ways to do it but I've seen this code in more than one place. I speculate that most people didn't knew they had to use a "AuthorizedClientServiceOAuth2AuthorizedClientManager", but rather found an implementation online and copied it.

But as you pointed out, since there are established spring ways to do something, it doen't matter if it's ugly since it actually "just works".

```

@Bean

public AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientServiceAndManager (

        ClientRegistrationRepository,
        clientRegistrationRepository,
        OAuth2AuthorizedClientService authorizedClientService) {

    OAuth2AuthorizedClientProvider authorizedClientProvider =
            OAuth2AuthorizedClientProviderBuilder.builder()
                    .clientCredentials()
                    .build();

    AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
            new AuthorizedClientServiceOAuth2AuthorizedClientManager(
                    clientRegistrationRepository, authorizedClientService);
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

    return authorizedClientManager;
}

```

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by vbezhenar on Wednesday August 16, 2023 @02:46PM (#37151756)
Now make it with user-configurable credentials (like extracted from database) with proper token caching.

With similar code I almost made a vulnerability in my app because Spring cached tokens which belong to different users.

Spring security is terrible piece of code. Spring - I can stand it. Spring Security - I can not. It was different project back in the day (Acegi Security if I remember correctly). It should have stayed that way.

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by zapnuk on Wednesday August 16, 2023 @03:16PM (#37152191)
This already uses user-configurable credentials. It uses an autowired "ClientRegistrationRepository" that provides the credentials.

We load the credentials once from AWS SecretsManager but I'm sure you could also load them from some DB.

But yes, I'd probably prefern a modern rewrite of Spring Security.

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by sverhagen on Wednesday August 16, 2023 @06:52PM (#37154935)
And yet, it is probably still much more secure than what 90% of us can whip together. I mean, maybe there's something better out there. I'm just pushing back on any suggestions that you should rather roll your own. Because, unless you're the other 10%, in an organization where continuity of that skill-set is taken care of, you definitely should not.
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by FlyingSnake on Wednesday August 16, 2023 @03:14PM (#37152158)
Tangential but I really wish HN supported monospaced code blocks using 3 backticks. Reading code blocks on a phone is terrible here on HN
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by gabereiser on Wednesday August 16, 2023 @06:38PM (#37154826)
You can do it with 3 spaces in front. Not the same as backticks but works just as good…

   public class See {
      public See() {
      }
   }
You just have to use a scratch
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by switch007 on Wednesday August 16, 2023 @04:12PM (#37153039)
I usually begin to rant about the verbosity and unintuitive ways of overriding things in Spring when I have to do it, but then stop and realise I'm grateful that it's actually possible and I don't have to throw out the framework or do some extremely ugly hacks.
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by seadan83 on Wednesday August 16, 2023 @02:44PM (#37151739)
Spring DAL is really debatable. My view of it is that it is one of those 'fast early development' at the expense of maintenance. It's easy to write code, modifying it later is difficult. Thus I feel this is an optimization to make writing easy code even easier. Therefore the pain of the Spring DAL is unexpectedly slow queries that are doing N+1 selects, having to translate SQL to JPA queries and back [+], dealing with the ORM issue that Spring models database rows and not database results, selects that retrieve far more data than they need, and the unnecessary situation of having to build a full object model to represent database (which is often unnecessary! If my only query aggregates 4 tables and returns two string values, there should be no need to model those 4 tables as full blown Java objects - I'm clearly a fan of things like MyBatis or JDBI for replacing the Spring DAL access. I've been embarking on that very effort at my job, so far replacing Spring JPA to JDBI has reduced a ton of code, was able to recently remove net 40 classes by replacing JPA with JDBI).

For example, some queries are naturally best done as analytic queries (or window queries). I have no idea how to do that in JQL (JPA's query language). Moving those queries to the database side can come to the rescure, but it's still an issue.. Which makes me think that JPA makes the easy queries even easier, but the difficult queries nearly impossible (if not outright impossible).

> Though micronauts is quickly approaching.

I've been looking at micronaut quite a bit as well. Another big downside to springboot is that by default it wants around 100MB of RAM per instance. With 6~7 applications on one host, the expenditure for the memory is sizable. Micronaut has a significant leg up here.

[+] DB Queries are often developed first by figuring out what the right query is. After that is done, it needs to be translated to JPA. If JPA eventually starts doing the wrong thing, or something slow - it then needs to be converted to SQL so you can do an explain plan. The latter is not that hard as you can enable a debug output to see what it is, but overall there are quite a few more steps here compared to: "develop your SQL query, next plop that into a string variable in your java code", vs "develop your SQL query, model the table relationships as java objects, build a JPA repository to run some queries against those tables" (in the happy case the latter is all that you need and is not bad, but when a query starts going slow, or starts fetching MB of data which you won't notice on local development but in production causes a p0...)

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by eweise on Wednesday August 16, 2023 @03:25PM (#37152322)
Spring DAL is geared towards operational data stores where typically you are not joining four different tables. Its been a while but pretty sure you can just write the native SQL and map the results to a collection, bypassing mapping to objects. The best strategy I've found is to make the domain objects one-to-one with the tables so their is no joining or inheritance. Do any combining within the repository or application layers. This makes the DAL super simple.
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by gabereiser on Wednesday August 16, 2023 @06:47PM (#37154905)
If you really really really have to, you can use @Query to define your own query for the repository interface method. This allows you to map, join, select, whatever you wish. The gripe is that if you don’t follow a specific wording convention, you must use @Query. JPA follows a specific convention - findByUsername or findBy (id) or findByPostAuthor(String name). There’s a whole section on how it derives sql statements from these methods.
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by seadan83 on Wednesday August 16, 2023 @07:29PM (#37155251)
The magic naming in JPA CrudRepository can be pretty nice.

Though, even resorting to @Query is not a panacea. Notably it breaks down for things like dynamic where clauses, dynamic joins - and you wind up using entity manager directly.

This really good article [1] on how to use native queries has some good examples where the native query is very clean. Though, the article ends with this:

```

Query q = em.createNamedQuery("selectAuthorEntities");

SynchronizeableQuery hq = q.unwrap(SynchronizeableQuery.class);

hq.addSynchronizedEntityClass(Author.class);

List<Author> authors = (List<Author>) q.getResultList();

for (Author a : authors) {

    System.out.println("Author "

            + a.getFirstName()

            + " "

            + a.getLastName());
}

```

For a simple "select first, last from author", the above seems kinda insane. Further there are issues with the requirement that Author now be mutable.

It gets even worse when it comes to the primary key and how that gets mapped. At work we have an ancient database where you cannot invoke a sequence within an insert statement. The ID generator for JPA throws a fit around this; it really wants to know the sequence and to use it. Hacking around JPA to not throw an error when we set the ID value directly is painful. JPA assumes that if an ID value is set, then your invocation of "entityManager.persist(entity)" will be an update. For us we have to set the ID value, and it very well is not an update operation that we wish to perform!

Thus a simple task of inserting a new row and fetching it goes through different techniques, is required to have an object mapping, and breaks down suddenly if used in a different environment where the DB version changes (and of course, it is only production that uses this ancient database). For the use-case of the original developers this was fine, when looking at it now, with 4x the data and seeing that we do a N+1 query - the whole thing is a mess to work with as we have some join data that sometimes needs to be lazy fetched, and other times it does need to be eagerly fetched. Thus having duplicate DTOs to represent this situation because we need different annotations is a headache. So I've been facing with existing simple JPA entities that conditionally need to lazy fetch vs eager, and that's really painful.

These are all tractable problems, but overall it would have been better/simpler for it all to have been pretty bare-bones SQL without any of the ORM layer. My snarky consolation is that at least those original developers got to save an hour or two when jamming this original product together in the first place. This is why I think SpringBoot + JPA are particularly good for toy demo projects, but something that is going to live for a decade and need serious changes and deal with serious changes in data scales - it's those difficult things that come later that are now requiring tons of time to implement. Some of those difficult things would be trivial to do had it all stayed as just SQL with mapping of results to objects.

[1] https://thorben-janssen.com/jpa-native-queries/

--------------

Caveat, I am just saying it is debatable whether to use SpringBoot's DAL, or whether to avoid it and use something like JDBI instead (I strongly prefer the latter, and of course at work, that is what we're doing. The stuff that has been converted to JDBI performs really well because it fixes the lazy vs eager join problems, eliminates N+1 queries and always selects just exactly the right amount of data, and we can use advanced query structures that lets the database do the work (eg: case statements, analytic queries, the aggregation functions of DB, etc) - and it's been easy to test and has eliminated tons of needless JPA class mapping code that was inflexible for the growth of these applications).

I've a lot of respect for the JPA repository stuff, but still just saying, it's debatable if that is a win. Usually the counter argument says "JPA is great stuff, and we can default to @Query or entity manager for native query - so how about we just give it a try and change it if it does not work?" At that point you become locked in and the painful parts are when you are 2 years down the road and have modify that system that of course uses JPA and mapped entities to the gills.

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by gabereiser on Wednesday August 16, 2023 @08:15PM (#37155592)
Jpa ID’s can be any serializable with a matching column type. If you’re going down to the JDBI level or doing jooq to get at your data, there’s a problem. I’ve found most of my issues with Jpa and Hibernate specifically are due to relationships and my table design. Sometimes just having the foreignkey and not the full relationship is the answer, sometimes writing a custom @Query is, but never has going down to the jdbc level been pleasurable. Even looking at your example above, it’s something I would flag in a code review as being not novel and not trivial and therefore complicates the code base unnecessarily. SynchronizedQuery can be simplified with the synchronized keyword in modern Java on the interface method of your repository.

For a really large spring project with hundreds of tables, we abandoned entity relationships outside of FK’s and instead did our joins in @Query’s, named queries (views essentially), services (to join repositories) and some components to share cached results in-memory, across nodes. What kind of ID were you trying to use and did you create a generator for it? IdentifierGenerator interface

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by seadan83 on Wednesday August 16, 2023 @11:56PM (#37156997)
> Even looking at your example above, it’s something I would flag in a code review as being not novel and not trivial and therefore complicates the code base unnecessarily. SynchronizedQuery can be simplified with the synchronized keyword in modern Java on the interface method of your repository.

Apparently that lengthy bit of code is to specify the query space. That call to "hq.addSynchronizedEntityClass(Author.class);" is seemingly a critical part.

I learned a bit though to consider whether that could be simply sychronized (I appreciate your response!)

From that same blog post:

>> The query space describes which entity classes your query references. Hibernate uses it to optimize the dirty check and flush operation that it has to perform before executing the query.. You can do that by unwrapping Hibernate’s SynchronizeableQuery from JPA’s Query interface and calling the addSynchronizedEntityClass method with a reference to your entity class.

I agree that is not trivial and complicates the code base unnecessarily.

For comparison (just putting this out there), the equivalent JDBI (with no concerns over dirty checks, flush operations, or query spaces) in nearly its entirety is :

```

List<Author> author = jdbi

     .withResultMapper(BeanFactoryMapper.of(Author.class))

     .withHandle(handle -> handle.createQuery("select first, last from author")

     .mapTo(Author.class)

     .list());
@Data

class Author {

  String first;

  String last;
}

```

I particularly like how this moves so much knowledge into the query string and away from the java classes. The above could be a far more complex query with joins, aggregation functions, etc.. and no new java classes would be needed, just a longer query string. In JPA, there is no query string of course. In JPA, the complexity of a more complicated query is pushed to additional classes with data fields and annotations.

This is just a different characteristic of how one scales with complexity vs the other. Whether ORM vs result-set-mapper is better depends on context.

It's all about picking the right tool for the right job. There are difference in how they scale relative to needed queries, complexity of data model, it's very situational.

As an analogy, I think ORM is like PHP - great for getting started and building some pretty big stuff very quickly. For a larger system I would not recommend PHP. I find JPA to be very hard to maintain once scaled to larger code bases with non-trivial relational data.

> What kind of ID were you trying to use and did you create a generator for it? IdentifierGenerator interface

It was a vanilla Long. The IdentifierGenerator suggestion is a good one, I think that might have solved potentially all of the problems we had with the PK values (there were others). I think the crux of that issue was the existing annotation worked, but then we also needed it to work for a very ancient version of Oracle where it no longer. The required annotations for the two different versions of Oracle were not compatible with one another.. just messy.

The issue of 'sometimes eager fetch' vs 'sometimes lazy fetch' was a large problem at the same time. I think that was perhaps a difficult problem because an '@Eager' was added to a number of fields already so teasing these different scenarios apart for all of the existing queries involving a number of entities was going to be a very non-trivial process.

-------------

I'm just pointing out there are trade-offs when going with an ORM vs a result-set mapper. Both are better than doing some direct JDBC work. Spring+JPA pushes you into an ORM philosophy, which still for sure has its place.

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by gabereiser on Thursday August 17, 2023 @12:50AM (#37157376)
I agree with you. ORM is for business domain object that you know of, are certain of, have clear definition of, and are the core data structures for your app. If you have to explore data in unintended ways, result-set mapper is useful. Another option is to provide an analysis tool that weaves into your app allowing you to run result-set mapper queries against your data source without having to make it part of your data access strategy in your application. Just a thought. Spring+JPA is designed for business, boring relational business, 99% of the time.
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by haarolean on Thursday August 17, 2023 @03:29AM (#37158363)
>Another big downside to springboot is that by default it wants around 100MB of RAM per instance While it might be not applicable to every app, nowadays we can do native images with Graal and some cheesy app could be launched with under 128MB of RAM usage.
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by no_wizard on Wednesday August 16, 2023 @04:51PM (#37153641)
What about something like ktor[0]? This looks promising to me, at least.

[0]: https://ktor.io/docs/welcome.html

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by Alupis on Wednesday August 16, 2023 @05:00PM (#37153763)
ktor is best fit for Kotlin applications. But, much like Play Framework - you can use it with just about any (or any?) JVM language.

With that said, Spring Boot will be vastly more mature, have a lot more resources available, bigger community, etc. There's also tons of 3rd party libraries that have support for spring and/or spring boot out of the box - such as resiliance4j, openapi-generators, etc.

With that said, Spring/Boot's Kotlin support is really good. The team has clearly put a lot of effort into making Kotlin a first-class language within the Spring ecosystem.

For backend work, Kotlin + Spring Boot is amazing.

Re:Spring 6.1 now compatible with virtual threads and JDK 21... by heisenbit on Wednesday August 16, 2023 @05:21PM (#37154022)
I would agree with most except Spring Security is not so simple due to its power and a number of changes over the releases. Trivial cases are simple to do but anything beyond that requires understanding the beast and that takes real effort. More complex cases are then again relatively easy.
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by gabereiser on Wednesday August 16, 2023 @06:51PM (#37154928)
We experts have to pay our bills somehow… I joke but you’re right. Spring Security works up until a point and when you want to make it do exactly what you want it to, you have to crack it open-override or extend-implement your own class (cough UserDetailService cough). Still, if you’re doing microservices with spring, spring security is enough to ensure your jwt in the request is valid. I like to pair it with more robust auth like Keycloak though if I’m going “platform” instead of monolith.
Re:Spring 6.1 now compatible with virtual threads and JDK 21... by Tainnor on Monday August 21, 2023 @07:10AM (#37207912)
> People who gripe about spring didn't bother to do things the spring way.

Of course, everyone who complains about a technology just hasn't learned it properly.

I think I understand the Spring model fairly well at this point. But one of the problems with it is that it's confusing and that the runtime-reflection-heavy approach causes issues with debuggability, performance and also errors creeping into production that should by all accounts be preventable at compile-time.

It's rather easy to make silly mistakes with Spring, such as confusing @Configuration and @ConfigurationProperties or writing tests that share the application context even though they require different configuration and suddenly stop working if they're executed in the wrong order, and even if you yourself learn it perfectly, your coworkers might not.

I agree that it offers a ton of benefits - but that doesn't mean that it isn't valid to criticise it.