I have following table (snippet):
create table users
(
user_id uniqueidentifier default newid() not null primary key,
name varchar(192),
...
)
and the entity:
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
@Table(value = "users")
public class User {
@Id
@Column("user_id")
private UUID userId;
}
Running a save against an h2-database with sql-server configurations works fine, however if run a save against a real sql-server db, I get
java.lang.IllegalArgumentException: After saving the identifier must not be null!
at org.springframework.util.Assert.notNull(Assert.java:201)
at org.springframework.data.jdbc.core.JdbcAggregateTemplate.store(JdbcAggregateTemplate.java:364)
at org.springframework.data.jdbc.core.JdbcAggregateTemplate.save(JdbcAggregateTemplate.java:161)
I saw various other posts, dealing with similar issues, however most use approaches via hibernate/JPA, which i want to avoid, if possible.
I tried various strategy
settings of @GeneratedValue
annotation, but none of them worked.
Is there a way to solve this issue solely using spring-data-jdbc ?
How would I have to annotate my id property correctly, so that sql-server
creates the uuid and pass it along to spring-data-jdbc ?
Im using SQL server 2019 with default settings.
edit:
To save an entity, I use save
of UserRepository extends CrudRepository<User, UUID>
edit-2:
With this workaround, I was able to get it working:
@Bean
BeforeConvertCallback<AlertUser> beforeConvertCallback() {
return (user) -> {
if (user.getId() == null) {
user.setId(UUID.randomUUID());
}
return user;
};
}
But it is still not the desired solution, since it relies on client side uuid generation.
I digged a bit further and kinda located the issue is happening, when spring tries to fetch the generated ids. Afaics it expects a ResultSet
containing a row with property GENERATED_KEYS
, but on sql-server this property is never or differently set by the dbms as it seems.
@GeneratedValue
is a JPA annotation. It is completely unrelated to Spring Data JDBC and therefor will have no effect on your Spring Data JDBC setup.
As far as I can tell Microsoft SqlServer (or it's JDBC driver) does not return ids generated in the way you setup your table. Therefore Spring Data JDBC can't pick them up.
The correct way around this is to use the construct you already discovered:
@Bean
BeforeConvertCallback<AlertUser> beforeConvertCallback() {
return (user) -> {
if (user.getId() == null) {
user.setId(UUID.randomUUID());
}
return user;
};
}
You probably want to drop the default newid()
clause on the database side so to not confuse people where the UUID is generated.
This is the "right" approach because
Of course, there is always the scenario when someone higher up in the food chain told you to generate the UUID on the database. In that case there are a couple options:
NamedParameterJdbcTemplate
or JdbcTemplate
to insert your entity end use the approach referenced above to obtain the generated id. You can put that code into a custom method implementation in your Spring Data JDBC repository.