Skip to content

GH-2020 Added SqlTypeResolver abstraction #2024

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

mipo256
Copy link
Contributor

@mipo256 mipo256 commented Apr 5, 2025

Fixes #2020

I've introduced the SqlTypeResolver interface, and a default implementation of it - DefaultSqlTypeResolver.

Backward compatibility: I've removed the @Deprecated constructors. Apart from that, it seems that we're backward compatible, if I have not overlooked anything.

Note: I had to wrap the SqlTypeResolver with Lazy here. The reason is described in the ad-hoc comment and in this issue.

CC: @mp911de @schauder

@mp911de mp911de force-pushed the main branch 2 times, most recently from 571fd96 to 1f2e694 Compare April 9, 2025 13:29
@mp911de mp911de self-assigned this Apr 14, 2025
@mp911de mp911de added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Apr 14, 2025
import org.springframework.data.relational.core.dialect.DefaultSqlTypeResolver;

/**
* Serves as a hint to the {@link DefaultSqlTypeResolver}, that signals the {@link java.sql.SQLType} to be used.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the strong reference to SQLType, we should move the entire annotation and infrastructure into the JDBC module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, agree

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a problem here, I have explained it in a separate ticket: #2031

Please, take a look.

* {@link SQLType} of the given parameter
*/
@Nullable
SQLType resolveSqlType(RelationalParameter relationalParameter);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we should return @Nullable. If a type is defined, then it must be used.

Also, the parameter should be a value object consisting of @Nullable name and vendorTypeNumber. RelationalParameter originates from the repository.query package and we don't want to introduce any cycles.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we should return @nullable. If a type is defined, then it must be used.

Okay... what is the expected behavior in this case? The DefaultSqlTypeResolver may fallback to JdbcUtil.targetSqlTypeFor resolution, as it happens now, but this interface may be implemented by the end users, and I'm not sure what SQLType they need to return in case they do not know/care about the target SQLType in this exact case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in case they do not know/care about the target SQLType

Then there's no sense in implementing the interface in the first place. When an annotated element is annotated with @SqlType, then this interface must be used. Otherwise, the resolveSqlType method would not be called.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then there's no sense in implementing the interface in the first place. When an annotated element is annotated with @SqlType, then this interface must be used. Otherwise, the resolveSqlType method would not be called.

I think this idea is a good one, but we can, possibly, do even better. In this case, the resolution logic is split up between the DefaultSqlTypeResolver and "somewhere else" (the JdbcParameter in our case with JdbcUtil call), if I got you right.

What are your thoughts on the solution, where we would move the java.sql.SQLType resolution logic in its entirety to the SqlTypeResolver, so that the SqlTypeResolver, as you've pointed out, never returns null, but if the user would want to implement his own SqlTypeResolver, then he/she could just decorate the DefaultSqlTypeResolver and provide some additional logic on top of it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are your thoughts on the solution, where we would move the java.sql.SQLType resolution logic in its entirety to the SqlTypeResolver, so that the SqlTypeResolver, as you've pointed out, never returns null, but if the user would want to implement his own SqlTypeResolver, then he/she could just decorate the DefaultSqlTypeResolver and provide some additional logic on top of it.

Where would resolution happen? Somewhere inside JdbcDialect? JdbcUtil is widely used in parts that aren't associated with a Dialect. I like generally the idea to move type resolution closer to the Dialect level. JdbcArrayColumns already has some code for SQLType resolution.

Following that line of thought, we could also add SQLType getSqlType(Class<?> componentType) to JdbcDialect while we keep the array part independently as array type resolution uses already code specific to array component types and array type codes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where would resolution happen? Somewhere inside JdbcDialect?

Well, I do not know to be honest. Please, read below.

JdbcUtil is widely used in parts that aren't associated with a Dialect. I like generally the idea to move type resolution closer to the Dialect level. JdbcArrayColumns already has some code for SQLType resolution.

I know about it. Let me go thought it step by step, I'm not as smart as you @mp911de 😄

The fact is that the JdbcUtil.targetSqlTypeFor(Class<?> type) accepts a Class. And, in general, it just takes advantage of a static mapping between Class --> SQLType. What we have in our case is an annotation @SqlType that is placed on a org.springframework.core.MethodParameter and that overrides that static mapping in very particular cases.

So, SqlTypeResolver cannot just accept Class. It has to accept something around MethodParameter, or JdbcParamter, because just Class does not carry any annotation info itself, at least for now.

Moving on, @SqlType can only be placed on method parameters, and not POJO fields. Therefore, sometimes, when we have JdbcDggregateTemplte.find() or insert() the resolution of java.sql.SQLType would still happen based on Class only.

So, the reason I'm talking all that through is to explain to myself and outline it here for others that SqlTypeResolver can only be used in JdbcParameters as a SQLType resolution for them and them only, at least it seems. The MappingJdbcConverter is used when we read and write our POJOs, but again, this is not our case.

So, to conclude, I think we can encapsulate all the logic for SQLType resolution of the method parameters in the SqlTypeResolver, but for now that is it.

Following that line of thought, we could also add SQLType getSqlType(Class<?> componentType) to JdbcDialect while we keep the array part independently as array type resolution uses already code specific to array component types and array type codes.

Yes, that probably makes sense. I need to play around with it. It is not yet 100% clear to me that moving getSqlType from JdbcArrayColumns into JdbcDialect is going to be the right move... Maybe we can consider this in a separate PR? I can play around with it, and we will see what we have here.

@mipo256
Copy link
Contributor Author

mipo256 commented Apr 16, 2025

Everything is fixed, except for the remaining two conversation threads @mp911de

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement @SQLType annotation to support custom mapping of @Param parameters to java.sql.SQLType
3 participants