我目前正在为spring-boot应用程序编写 data.sql SQL脚本。我希望它填写各种表的默认值(例如,在初始启动时)。
我已在 application.properties 中设置spring.jpa.hibernate.ddl-auto=update
以保留数据库的内容,但每次启动服务时都会执行data.sql。
现在我正在寻找一种插入行的方法,如果它们不存在,如果它们确实存在则不更新。
我正在使用 H2Dialect (以及H2数据库)。
最初我想使用某种“IF NOT EXISTS
”语句和SELECT COUNT(*) FROM table
,但看起来H2并不直接支持这种说法。
所以下一个最好的事情要做的就是使用MERGE INTO
,但这似乎没有按预期工作,我不明白为什么会这样。
这就是我的脚本的样子,但遗憾的是它没有按预期工作:
MERGE INTO Languages AS T USING (SELECT * FROM Languages) AS S ON (T.ID = S.ID)
WHEN NOT MATCHED THEN
INSERT (LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE) VALUES
('en', 'English', 'USA', TRUE, TRUE),
('de', 'German', 'Deutschland', FALSE, FALSE);
我想添加这些值如果它们不存在(这可能更容易)或者如果语言表中没有值(其中是我更喜欢的。)
这是我启动spring-boot应用程序时收到的(缩短的)异常:
Caused by: org.h2.jdbc.JdbcSQLException: Syntax Fehler in SQL Befehl "MERGE INTO LANGUAGES AS[*] T USING (SELECT * FROM LANGUAGES) AS S ON (T.ID = S.ID) WHEN NOT MATCHED THEN INSERT (LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE) VALUES ('en', 'English', 'USA', TRUE, TRUE), ('de', 'German', 'Deutschland', FALSE, FALSE) "; erwartet "., (, KEY, VALUES, (, WITH, SELECT, FROM"
Syntax error in SQL statement "MERGE INTO LANGUAGES AS[*] T USING (SELECT * FROM LANGUAGES) AS S ON (T.ID = S.ID) WHEN NOT MATCHED THEN INSERT (LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE) VALUES ('en', 'English', 'USA', TRUE, TRUE), ('de', 'German', 'Deutschland', FALSE, FALSE) "; expected "., (, KEY, VALUES, (, WITH, SELECT, FROM"; SQL statement:
MERGE INTO Languages AS T USING (SELECT * FROM Languages) AS S ON (T.ID = S.ID) WHEN NOT MATCHED THEN INSERT (LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE) VALUES ('en', 'English', 'USA', TRUE, TRUE), ('de', 'German', 'Deutschland', FALSE, FALSE) [42001-196]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345) ~[h2-1.4.196.jar:1.4.196]
at org.h2.message.DbException.getSyntaxError(DbException.java:205) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.Parser.getSyntaxError(Parser.java:541) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.Parser.parseSelectSimple(Parser.java:2073) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.Parser.parseSelectSub(Parser.java:1940) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.Parser.parseSelectUnion(Parser.java:1755) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.Parser.parseSelect(Parser.java:1743) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.Parser.parseMerge(Parser.java:1053) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.Parser.parsePrepared(Parser.java:423) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.Parser.parse(Parser.java:321) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.Parser.parse(Parser.java:297) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.Parser.prepareCommand(Parser.java:258) ~[h2-1.4.196.jar:1.4.196]
at org.h2.engine.Session.prepareLocal(Session.java:578) ~[h2-1.4.196.jar:1.4.196]
at org.h2.engine.Session.prepareCommand(Session.java:519) ~[h2-1.4.196.jar:1.4.196]
at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1204) ~[h2-1.4.196.jar:1.4.196]
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:176) ~[h2-1.4.196.jar:1.4.196]
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:164) ~[h2-1.4.196.jar:1.4.196]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_121]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_121]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:114) ~[tomcat-jdbc-8.5.27.jar:na]
at com.sun.proxy.$Proxy97.execute(Unknown Source) ~[na:na]
at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:470) ~[spring-jdbc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
... 78 common frames omitted
异常堆栈跟踪是巨大的,但是“由”引起的“部分”基本上说,由于最深的异常,我在上面发布的“由...引起的”声明,无法创建bean。如果有人需要更多细节,请告诉我。
编辑:或者是否有初始化数据的最佳做法(例如默认用户或默认用户)?如果执行数据库重置,最好可以重用一个。
编辑(25.04.2018):我认为我的语言类可能出错了,因为用于架构创建的SQL看起来有点奇怪。这是:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.particles.authservice.jwtservice.JSONHelper;
import com.particles.authservice.languageservice.converters.LanguageDeserializer;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* This class describes the entity for a language.
*/
@JsonDeserialize(using = LanguageDeserializer.class)
@Data
@NoArgsConstructor
@Entity(name = "Languages")
public class Language {
private static Language defaultLanguage;
@Id
@Column(nullable = false, unique = true)
private String languageCode;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String i18nName;
@Column(nullable = false)
private boolean localizeUi = false;
@Column(nullable = false)
private boolean canChoose = false;
/**
* This method sets the class attribute {@link Language#defaultLanguage}.
*
* @param defaultLanguage
* ({@link Language}) default language
*/
public static void setDefaultLanguage(final Language defaultLanguage) {
Language.defaultLanguage = defaultLanguage;
}
/**
* @return ({@link Language}) default language
*/
public static Language getDefaultLanguage() {
return Language.defaultLanguage;
}
}
答案 0 :(得分:2)
现在我正在寻找一种方法来插入行,如果它们不存在而不更新它们,如果它们存在的话。
您可以使用INSERT INTO ... SELECT ...
:
INSERT INTO Languages(LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE)
SELECT LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE
FROM (SELECT 'en' AS Language_code, 'English' AS name,
'USA' AS I18N_NAME, TRUE AS LOCALIZE_UI, TRUE AS CAN_CHOOSE
UNION ALL
SELECT 'de', 'German', 'Deutschland', FALSE, FALSE
) sub
WHERE NOT EXISTS (SELECT 1
FROM Languages l
WHERE sub.Language_code = l.Language_code);
假设LANGUAGE_CODE
是UNIQUE,否则您需要明确指定ID
列。
或者使用MINUS/EXCEPT
:
INSERT INTO Languages(LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE)
SELECT LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE
FROM (SELECT 'en' AS Language_code, 'English' AS name,
'USA' AS I18N_NAME, TRUE AS LOCALIZE_UI, TRUE AS CAN_CHOOSE
UNION ALL
SELECT 'de', 'German', 'Deutschland', FALSE, FALSE
) sub
EXCEPT
SELECT LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE
FROM Languages;
修改强>
有没有办法避免使用UNION?
我不确定H2是否支持VALUES
子句:
SELECT LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE
FROM (SELECT 'en' AS Language_code, 'English' AS name,
'USA' AS I18N_NAME, TRUE AS LOCALIZE_UI, TRUE AS CAN_CHOOSE
UNION ALL
SELECT 'de', 'German', 'Deutschland', FALSE, FALSE
) sub
<=>
SELECT *
FROM (VALUES('en', 'English', 'USA', TRUE, TRUE),
('de', 'German', 'Deutschland', FALSE, FALSE)
) sub(LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE)
<强> SQLFiddle Demo 强>
答案 1 :(得分:0)
根据您的要求,您应该能够在不使用WHEN NOT MATCHED
子句的情况下使用MERGE语句。
MERGE :
更新现有行,并插入不存在的行。如果没有钥匙 指定了列,主键列用于查找行。 如果每个新行有多个行受到影响,则抛出异常。
MERGE INTO Languages
KEY(LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE) VALUES
('en', 'English', 'USA', TRUE, TRUE),
('de', 'German', 'Deutschland', FALSE, FALSE);
如果这仍然不适合你,请告诉我。