Я пытаюсь подключить службу Spring Boot к базе данных SQL Server 2022, но у меня возникают проблемы с последовательностью:
public class SomeEntity {
@Id
@SequenceGenerator(name = "some_seq", schema = "sch_some", sequenceName = "some_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "some_seq")
@Column(name = "id", columnDefinition = "decimal")
private Long id;
...
}
Это ошибка, которую я получаю, когда пытаюсь сохранить что-то в таблице:
{"date":"2024-07-07T20:50:08,250Z","env":"test","app":"some-service","version":"1.32.0","host": "_","level":"ERROR","logger":"org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet]","msg":"Servlet .service() для сервлета [dispatcherServlet] в контексте с путем [] выдал исключение [Ошибка обработки запроса; вложенное исключение — org.springframework.dao.InvalidDataAccessResourceUsageException: ошибка при выполнении изолированной работы; SQL [н/д]; hibernate.Exception.SQLGrammarException: ошибка при выполнении изолированной работы] с основной причиной","thread":"http-nio-8080-exec-1","context":[],"eventId":"com.microsoft.sqlserver. jdbc.SQLServerException","stack":"com.microsoft.sqlserver.jdbc.SQLServerException: неверное имя объекта 'some_db.sch_some.some_seq'.\n\tat com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java) :262)\n\tat com.microsoft.sqlserver.jdbc.SQLServerStatement.getNextResult(SQLServerStatement.java:1624)\n\tat com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.doExecutePreparedStatement(SQLServerPreparedStatement.java:594)\n\ tat com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement$PrepStmtExecCmd.doExecute(SQLServerPreparedStatement.java:524)\n\tat com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7194)\n\tat com.microsoft .sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:2979)\n\tat com.microsoft.sqlserver.jdbc.SQLServerStatement.executeCommand(SQLServerStatement.java:248)\n\tat com.microsoft.sqlserver.jdbc.SQLServerStatement .executeStatement(SQLServerStatement.java:223)\n\tat com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.executeQuery(SQLServerPreparedStatement.java:446)\n\tat com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java :52)\n\tat com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)\n\tat
Вот как я создал последовательность
create sequence SCH_SOME.SOME_SEQ start with 1 increment by 1;
Поскольку у меня есть еще одна таблица в этой схеме, в которой нет проблем со вставкой, я думаю, что это может быть проблема с доступом. Я также пытался запустить оператор выбора гранта, как в Oracle, но в итоге получил вот это
Ошибка SQL [4606] [S0001]: Предоставленная или отозванная привилегия SELECT несовместима с объектом.
Итак, есть ли в SQL Server другой набор разрешений, для которого мне нужно предоставить грант? Я также пробовал UPDATE и ALTER, но это не имело значения. Если бы кто-нибудь мог пролить свет на эти вопросы, мы были бы очень признательны. Спасибо.
Обновление. Я провел некоторую отладку и обнаружил, что именно это генерирует Hibernate:
Hibernate:
select
next_val as id_val
from
some_db.sch_some.some_seq with (updlock,
rowlock)
Я не думаю, что это правильный синтаксис для SQL Server.
Какие версии Spring, Hibernate и JPA вы используете? Используемый им синтаксис совершенно неверен для SQL Server, он должен быть ближе к select next value for SCH_SOME.SOME_SEQ as [id_val];
. Ссылка: db<>рабочий пример
Да, это правильно, в итоге я изменил диалект спящего режима с org.hibernate.dialect.SQLServerDialect на org.hibernate.dialect.SQLServer2012Dialect, и это устранило проблему.
Сообщение об ошибке, которое вы видите, связано с тем, что Hibernate пытается использовать последовательность способом, который не поддерживается SQL Server. В SQL Server доступ к последовательностям осуществляется не как к таблицам, а через вызов функции. Правильный синтаксис для получения следующего значения из последовательности в SQL Server — NEXT VALUE FOR sequence_name
. Однако Hibernate генерирует оператор SQL, как если бы последовательность была таблицей, что неверно для SQL Server. Чтобы решить эту проблему, вы можете использовать аннотацию @TableGenerator
вместо @SequenceGenerator
. Аннотация @TableGenerator
— это портативный способ генерации идентификаторов, который хорошо работает с базами данных, которые не поддерживают последовательности, например SQL Server. Вот пример того, как вы можете изменить свой код:
@Id
@TableGenerator(name = "some_gen", table = "id_gen", pkColumnName = "gen_name", valueColumnName = "gen_val", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "some_gen")
@Column(name = "id", columnDefinition = "decimal")
private Long id;
В приведенном выше коде id_gen
— это таблица, которую Hibernate будет использовать для генерации идентификаторов. В этой таблице должно быть два столбца: gen_name
и gen_val
. В столбце gen_name
хранится имя генератора, а в столбце gen_val
— текущее значение идентификатора. Обратите внимание, что вам нужно будет создать таблицу id_gen
вручную в вашей базе данных. Вот пример того, как вы можете это сделать:
CREATE TABLE id_gen (
gen_name VARCHAR(255) NOT NULL,
gen_val BIGINT NOT NULL,
PRIMARY KEY (gen_name)
);
После создания таблицы вам необходимо вставить строку для вашего генератора:
INSERT INTO id_gen (gen_name, gen_val) VALUES ('some_gen', 0);
Это должно решить проблему, с которой вы столкнулись.
Спасибо за ответ. В итоге я изменил диалект спящего режима с org.hibernate.dialect.SQLServerDialect на org.hibernate.dialect.SQLServer2012Dialect, и это устранило проблему.
Этот вопрос задавался много раз, например. stackoverflow.com/questions/49037803/… и вам нужно проверить одну вещь: у вас установлены последние версии драйверов и вы используете правильный диалект SqlServer.