防止@Entity 重新创建数据库table - Spring 启动
Prevent @Entity from re creating a database table - Spring Boot
我对 spring 引导数据 jpa 有点陌生,据我所知,@Entity 用于表示应用程序中的数据库 table,对于这个项目,我使用 spring-boot 2.2.5.RELEASE
和 H2
在内存数据库中。
到目前为止我已经知道了。
在 resources/data.sql
里面
CREATE TABLE CURRENCY (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(250) NOT NULL,
code VARCHAR(250) NOT NULL
);
CREATE TABLE EXCHANGE_CURRENCY (
id INT AUTO_INCREMENT PRIMARY KEY,
IdFx1 INT NOT NULL,
IdFx2 INT NOT NULL,
equivalent DECIMAL NOT NULL,
FOREIGN KEY (IdFx1) REFERENCES CURRENCY(id),
FOREIGN KEY (IdFx2) REFERENCES CURRENCY(id)
);
我的实体class
import javax.persistence.*;
@Entity
@Table(name = "CURRENCY")
public class Currency {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String code;
}
存储库
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CurrencyRepository extends CrudRepository<Currency, Long> {
@Query("SELECT c FROM CURRENCY WHERE c.code LIKE %:code%")
List<Currency> findCurrencyByCode(@Param("code") String code);
}
和服务
import com.currency.canonical.models.Currency;
import com.currency.canonical.request.ExchangeValueRequest;
import com.currency.canonical.response.ExchangeValueResponse;
import com.currency.dao.CurrencyService;
import com.currency.dao.repository.CurrencyRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class CurrencyConversionServiceImpl implements CurrencyConversionService {
Logger logger = LoggerFactory.getLogger(CurrencyConversionServiceImpl.class);
@Autowired
private CurrencyRepository currencyRepository;
@Override
public ExchangeValueResponse performCurrencyConversion(ExchangeValueRequest request) {
final long initialTime = System.currentTimeMillis();
ExchangeValueResponse objExchangeValueResponse = new ExchangeValueResponse();
try {
List<Currency> currencyList = currencyRepository.findCurrencyByCode(request.getMonedaOrigen());
currencyList.forEach(System.out::println);
} catch (Exception e) {
}
return objExchangeValueResponse;
}
}
执行应用程序时出现此错误
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #2 of URL [file:/C:/Users/Usuario/Documents/IdeaProjects/currency-converter/currency-converter-resource/target/classes/data.sql]: CREATE TABLE CURRENCY ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(250) NOT NULL, code VARCHAR(250) NOT NULL ); nested exception is org.h2.jdbc.JdbcSQLSyntaxErrorException: Tabla "CURRENCY" ya existe
Table "CURRENCY" already exists; SQL statement:
CREATE TABLE CURRENCY ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(250) NOT NULL, code VARCHAR(250) NOT NULL ) [42101-200]
为什么 @Entity 试图重新创建一个本应仅表示的 table,有没有办法禁用它?
发生错误是因为当您 运行 您的应用程序试图重新创建 table 时,解决方法如下:
- 在创建 table 之前在 data.sql 中添加一个
drop table if exists [tablename]
。
- 将语句从
CREATE TABLE
更改为 CREATE TABLE IF NOT EXISTS
JPA 具有 DDL 生成功能,这些功能可以在针对数据库启动时设置为 运行。这是通过两个外部属性控制的:
spring.jpa.generate-ddl
(布尔值)打开和关闭该功能
并且独立于供应商。
spring.jpa.hibernate.ddl-auto
(枚举)是一个 Hibernate 特性
以更细粒度的方式控制行为。
spring.jpa.hibernate.ddl-auto
属性 是如何工作的?您可以根据环境参考下面的配置。
开发 - 创建、更新
生产 - none
这里的问题是文件 data.sql 的名称,正如 spring 所建议的,有两个重要的文件可以帮助您控制数据库创造,这些就是
schema.sql
- 这个文件顾名思义是创建数据库模式所需的 DDL 语句。
data.sql
- 此文件将包含正确填充初始数据库所需的所有 DML 语句。
您的应用程序的问题是 DDL 是在 data.sql
文件中指定的,这会混淆 spring 并且它会尝试将 DDL 视为 DML。
您的问题的解决方案是将 data.sql
重命名为 schema.sql
,spring 将处理其余部分。
我还发现存储库还有一个问题,因为您使用的是自定义查询 @Query("SELECT code FROM CURRENCY WHERE code LIKE %:code%")
可能会在启动存储库时导致错误,因为 java 实体名称区分大小写。您可以通过以下方式解决此问题 -
一个。因为它是一个类似的查询 spring 存储库已经支持它,你可以重写像 -
这样的方法
List<Currency> findByCodeLike(@Param("code") String code);
乙。使用 JPQL 查询,除了更改 table 名称外,与您在代码中所做的相同,因为 JPA 实体名称区分大小写
@Query("SELECT code FROM Currency WHERE code LIKE %:code%")
List<Currency> findCurrencyByCode(@Param("code") String code);
C。如果您仍想使用 table 数据库模式“CURRENCY”中的名称保留当前查询,那么您可以在 @Query
中使用 nativeQuery
标志让 spring 知道您是使用本机查询而不是 JPQL -
@Query(value = "SELECT code FROM CURRENCY WHERE code LIKE %:code%", nativeQuery = true)
List<Currency> findCurrencyByCode(@Param("code") String code);
希望对您有所帮助!
我对 spring 引导数据 jpa 有点陌生,据我所知,@Entity 用于表示应用程序中的数据库 table,对于这个项目,我使用 spring-boot 2.2.5.RELEASE
和 H2
在内存数据库中。
到目前为止我已经知道了。
在 resources/data.sql
里面CREATE TABLE CURRENCY (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(250) NOT NULL,
code VARCHAR(250) NOT NULL
);
CREATE TABLE EXCHANGE_CURRENCY (
id INT AUTO_INCREMENT PRIMARY KEY,
IdFx1 INT NOT NULL,
IdFx2 INT NOT NULL,
equivalent DECIMAL NOT NULL,
FOREIGN KEY (IdFx1) REFERENCES CURRENCY(id),
FOREIGN KEY (IdFx2) REFERENCES CURRENCY(id)
);
我的实体class
import javax.persistence.*;
@Entity
@Table(name = "CURRENCY")
public class Currency {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String code;
}
存储库
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CurrencyRepository extends CrudRepository<Currency, Long> {
@Query("SELECT c FROM CURRENCY WHERE c.code LIKE %:code%")
List<Currency> findCurrencyByCode(@Param("code") String code);
}
和服务
import com.currency.canonical.models.Currency;
import com.currency.canonical.request.ExchangeValueRequest;
import com.currency.canonical.response.ExchangeValueResponse;
import com.currency.dao.CurrencyService;
import com.currency.dao.repository.CurrencyRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class CurrencyConversionServiceImpl implements CurrencyConversionService {
Logger logger = LoggerFactory.getLogger(CurrencyConversionServiceImpl.class);
@Autowired
private CurrencyRepository currencyRepository;
@Override
public ExchangeValueResponse performCurrencyConversion(ExchangeValueRequest request) {
final long initialTime = System.currentTimeMillis();
ExchangeValueResponse objExchangeValueResponse = new ExchangeValueResponse();
try {
List<Currency> currencyList = currencyRepository.findCurrencyByCode(request.getMonedaOrigen());
currencyList.forEach(System.out::println);
} catch (Exception e) {
}
return objExchangeValueResponse;
}
}
执行应用程序时出现此错误
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #2 of URL [file:/C:/Users/Usuario/Documents/IdeaProjects/currency-converter/currency-converter-resource/target/classes/data.sql]: CREATE TABLE CURRENCY ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(250) NOT NULL, code VARCHAR(250) NOT NULL ); nested exception is org.h2.jdbc.JdbcSQLSyntaxErrorException: Tabla "CURRENCY" ya existe
Table "CURRENCY" already exists; SQL statement:
CREATE TABLE CURRENCY ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(250) NOT NULL, code VARCHAR(250) NOT NULL ) [42101-200]
为什么 @Entity 试图重新创建一个本应仅表示的 table,有没有办法禁用它?
发生错误是因为当您 运行 您的应用程序试图重新创建 table 时,解决方法如下:
- 在创建 table 之前在 data.sql 中添加一个
drop table if exists [tablename]
。 - 将语句从
CREATE TABLE
更改为CREATE TABLE IF NOT EXISTS
JPA 具有 DDL 生成功能,这些功能可以在针对数据库启动时设置为 运行。这是通过两个外部属性控制的:
spring.jpa.generate-ddl
(布尔值)打开和关闭该功能 并且独立于供应商。spring.jpa.hibernate.ddl-auto
(枚举)是一个 Hibernate 特性 以更细粒度的方式控制行为。
spring.jpa.hibernate.ddl-auto
属性 是如何工作的?您可以根据环境参考下面的配置。
开发 - 创建、更新
生产 - none
这里的问题是文件 data.sql 的名称,正如 spring 所建议的,有两个重要的文件可以帮助您控制数据库创造,这些就是
schema.sql
- 这个文件顾名思义是创建数据库模式所需的 DDL 语句。data.sql
- 此文件将包含正确填充初始数据库所需的所有 DML 语句。
您的应用程序的问题是 DDL 是在 data.sql
文件中指定的,这会混淆 spring 并且它会尝试将 DDL 视为 DML。
您的问题的解决方案是将 data.sql
重命名为 schema.sql
,spring 将处理其余部分。
我还发现存储库还有一个问题,因为您使用的是自定义查询 @Query("SELECT code FROM CURRENCY WHERE code LIKE %:code%")
可能会在启动存储库时导致错误,因为 java 实体名称区分大小写。您可以通过以下方式解决此问题 -
一个。因为它是一个类似的查询 spring 存储库已经支持它,你可以重写像 -
这样的方法List<Currency> findByCodeLike(@Param("code") String code);
乙。使用 JPQL 查询,除了更改 table 名称外,与您在代码中所做的相同,因为 JPA 实体名称区分大小写
@Query("SELECT code FROM Currency WHERE code LIKE %:code%")
List<Currency> findCurrencyByCode(@Param("code") String code);
C。如果您仍想使用 table 数据库模式“CURRENCY”中的名称保留当前查询,那么您可以在 @Query
中使用 nativeQuery
标志让 spring 知道您是使用本机查询而不是 JPQL -
@Query(value = "SELECT code FROM CURRENCY WHERE code LIKE %:code%", nativeQuery = true)
List<Currency> findCurrencyByCode(@Param("code") String code);
希望对您有所帮助!