#java #junit #spring-data-jpa #eclipselink #h2
#java #junit #spring-data-jpa #eclipselink #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>
Я создал таблицу, в которую я выполняю ВСТАВКУ, используя параметр 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")
Если я добавлю создание продолжения:
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:
Комментарии:
1. H2 1.4.200 выдает неверное сообщение об ошибке, когда
.NEXTVAL
синтаксис Oracle используется для несуществующей последовательности. Сначала вам нужно создать эту последовательность сCREATE SEQUENCE SEQ_AUTORISATION_TAURUS
помощью или каким-либо другим способом. Вы также не должны использоватьAUTO_INCREMENT
предложение в стиле MySQL в режиме совместимости с Oracle в H2 и не должны пытаться использовать столбцы идентификаторов и отдельные последовательности для одного и того же столбца вместе, вам нужно выбрать тот или иной подход.
Ответ №1:
Вероятно, проблема с вашей настройкой заключается в том, что ваши тесты создают в несколько раз контекст приложения Spring.
Каждый раз, когда запускается новый тест, он воссоздает dataSource
компонент и, кроме того, пытается запустить сценарии инициализации базы данных H2.
В обычном случае первый тест создаст папку базы данных H2 и связанные с ней материалы, следующий будет использовать ее повторно.
В зависимости от содержимого этих скриптов он будет работать в большинстве случаев, но не всегда, как в вашем случае.
Чтобы избежать этой проблемы, у вас есть несколько вариантов.
С одной стороны, в этом конкретном случае вы можете включить предложение IF NOT EXISTS
в свой код создания последовательности:
CREATE SEQUENCE IF NOT EXISTS SEQ_AUTORISATION_TAURUS...
В общем случае вы можете изменить свой скрипт, чтобы учесть этот факт и создать, если не существует, разные элементы H2 или сначала DROP
, а затем CREATE
все, что вам нужно.
С другой стороны, Spring Test также предлагает вам @DirtiesContext
аннотацию для аналогичной цели:
Тестовая аннотация, которая указывает, что
ApplicationContext
связанный с тестом является грязным и поэтому должен быть закрыт и удален из кэша контекста.
И:
@DirtiesContext
может использоваться в качестве аннотации на уровне класса и метода в пределах одного и того же класса или иерархии классов. В таких сценарияхApplicationContext
они будут помечены как грязные до или после любого такого аннотированного метода, а также до или после текущего тестового класса, в зависимости от настроенногоmethodMode()
иclassMode()
.
Как вы можете видеть, вам нужно только аннотировать свой класс или методы тестирования с помощью этой аннотации, и Spring соответствующим образом воссоздаст контекст:
@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
Пожалуйста, имейте в виду, что этот подход повлияет на производительность ваших тестов, поскольку необходимо воссоздать контекст приложения Spring, хотя, с другой стороны, он всегда обеспечит вам чистое и детерминированное — в соответствии с вашими сценариями инициализации — состояние базы данных.