首页 > 解决方案 > JPA 插入 + Oracle 序列和带有 H2 的 Junit

问题描述

我有一个使用 JPA 和 Oracle 作为 DB 的 Spring MvC 项目,具有以下实体:

@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>

我已经使用 AUTO_INCREMENT 选项创建了我正在执行 INSERT 的表:

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")

如果我添加续集创作:

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:

标签: javajunitspring-data-jpaeclipselinkh2

解决方案


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

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

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

根据这些脚本的内容,它在大多数情况下都会起作用,但并不总是像您的情况一样。

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

一方面,在这种特定情况下,您可以IF NOT EXISTS序列创建代码中包含该子句:

CREATE SEQUENCE IF NOT EXISTS SEQ_AUTORISATION_TAURUS...

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

另一方面,Spring Test 也为您提供了类似目的的@DirtiesContext注解:

指示ApplicationContext与测试关联的测试注释是脏的,因此应该关闭并从上下文缓存中删除。

和:

@DirtiesContext可以用作同一类或类层次结构中的类级别和方法级别注释。在这种情况下,ApplicationContext将在任何此类注释方法之前或之后以及当前测试类之前或之后将 标记为脏,具体取决于配置的methodMode()and classMode()

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

@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)

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


推荐阅读