JPA Insert + Oracle Sequences 和 Junit with H2

JPA Insert + Oracle Sequences and Junit with H2

我有一个 Spring MvC 项目,使用 JPA 和 Oracle 作为数据库,具有这个实体:

@Entity
@Table(name = "AUTORISATION_TAURU")
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
@EqualsAndHashCode(of = {"autorisationTaurusId"})
public class AutorisationTauru implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_TAURU")
@SequenceGenerator(sequenceName = "SEQ_AUTORISATION_TAURUS", allocationSize = 1, name = "SEQ_TAURU")
    @Column(name = "AUTORISATION_TAURUS_ID")
    private Long autorisationTaurusId;
..
}

在我的 xml 配置文件中,我有这个;

<bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.h2.Driver" />
    <property name="url" value="jdbc:h2:mem:~/test2;DB_CLOSE_DELAY=-1;MODE=Oracle;INIT=RUNSCRIPT FROM 'classpath:create_db.sql'\;
                    RUNSCRIPT FROM 'classpath:create_db2.sql'\;
                    RUNSCRIPT FROM 'classpath:create_func.sql'" />
    <property name="username" value="sa" />
    <property name="password" value="" />
</bean>



  <bean id="jpaVendorAdapter"
              class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
   
        <property name="database" value="H2" />
       <bean id="entityManagerFactory"
              class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="persistenceUnitName" value="bonanza-entities" />
        <property name="packagesToScan">
            <array>
              <value>com.bonanza.model</value>           
            </array>
        </property>
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
        <property name="jpaProperties">
            <props>
                <prop key="eclipselink.target-database">org.eclipse.persistence.platform.database.OraclePlatform</prop>
                
            </props>
        </property>
    </bean>

我已经创建了 table 我正在使用 AUTO_INCREMENT 选项进行插入:

CREATE TABLE IF NOT EXISTS AUTORISATION_TAURU
(
  AUTORISATION_TAURUS_ID NUMBER ,

但是当我 运行 我的本地测试时,我得到了这个错误:

Local Exception Stack: 
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.h2.jdbc.JdbcSQLSyntaxErrorException: 
Syntax error in SQL statement "SELECT SEQ_AUTORISATION_TAURUS.NEXTVAL FROM[*] DUAL"; expected "identifier"; SQL statement:
SELECT SEQ_AUTORISATION_TAURUS.NEXTVAL FROM DUAL [42001-200]
Error Code: 42001
Call: SELECT SEQ_AUTORISATION_TAURUS.NEXTVAL FROM DUAL
Query: ValueReadQuery(sql="SELECT SEQ_AUTORISATION_TAURUS.NEXTVAL FROM DUAL")

如果我添加 sequel 创建:

CREATE SEQUENCE SEQ_AUTORISATION_TAURUS
MINVALUE 1 MAXVALUE 9223372036854775807
START WITH 1 INCREMENT BY 1 CACHE 8 NOCYCLE;

我在 运行 进行测试时遇到了这个错误:

    ... 43 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Exception [EclipseLink-28019] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Deployment of PersistenceUnit [bonanza-entities] failed. Close all factories for this PersistenceUnit.
Internal Exception: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.h2.jdbc.JdbcSQLSyntaxErrorException: Sequence "SEQ_AUTORISATION_TAURUS" already exists; SQL statement:

您设置的问题可能在于您的测试创建了数倍于 Spring 应用程序上下文。

每次新测试 运行 它都会重新创建 dataSource bean,此外,它还会尝试启动 H2 数据库初始化脚本。

在正常情况下,第一次测试会创建H2数据库文件夹和相关的东西,接下来会重复使用它。

根据这些脚本的内容,它在大多数情况下都能正常工作,但并不总是像您的情况那样。

为避免该问题,您有多种选择。

一方面,在这种特定情况下,您可以在 sequence creation 代码中包含子句 IF NOT EXISTS

CREATE SEQUENCE IF NOT EXISTS SEQ_AUTORISATION_TAURUS...

在一般情况下,您可以修改脚本以考虑到这一事实,如果不存在则创建不同的 H2 元素,或者首先 DROP 然后 CREATE 每个您需要的元素。

另一方面,Spring 测试也为您提供了 @DirtiesContext annotation 用于类似目的:

Test annotation which indicates that the ApplicationContext associated with a test is dirty and should therefore be closed and removed from the context cache.

并且:

@DirtiesContext may be used as a class-level and method-level annotation within the same class or class hierarchy. In such scenarios, the ApplicationContext will be marked as dirty before or after any such annotated method as well as before or after the current test class, depending on the configured methodMode() and classMode().

如您所见,您只需要使用此注释注释您的 class 或测试方法,Spring 将相应地重新创建上下文:

@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)

请注意,此方法会对测试性能产生影响,因为 Spring 应用程序上下文需要重新创建,但另一方面,它会始终为您提供干净且确定的- 根据您的初始化脚本 - 数据库状态。