Reputation: 31
I am trying to implement tests in my spring boot app, but I have problems with ManyToOne/OneToMany links.
It seems that the link from the subobject is not created, while I use CascadeType = ALL.
I tried creating it in the BusinesDomain.addSubdomain, adding "subDomain.domain(this);" but this causes a loop.
I found out other topics with the same error but non of them seem to cover my issue.
Could you please help me find out what seems to be the issue ?
Thanks a lot!
Here are my Entities
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.Setter;
import lombok.experimental.Accessors;
@Accessors(fluent = true)
@Data
@Entity
@NoArgsConstructor
public class BusinessDomain {
@Setter(AccessLevel.NONE)
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private long id;
@NonNull
@Column(unique=true)
private String name;
@OneToMany(mappedBy = "domain", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<BusinessSubDomain> subDomains = new LinkedHashSet<>();
public void addSubDomain(BusinessSubDomain subDomain) {
subDomains.add(subDomain);
}
}
@Accessors(fluent = true)
@Data
@Entity
@NoArgsConstructor
public class BusinessSubDomain {
@Setter(AccessLevel.NONE)
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private long id;
@ManyToOne(optional = false)
@JoinColumn(name = "business_domain_id", referencedColumnName="id")
private BusinessDomain domain;
}
My service class:
@Override
public BusinessDomain saveOrUpdate(BusinessDomain businessDomain) throws OrcaException {
try {
BusinessDomain domain = businessDomainRepository.findByName(businessDomain.name());
if(null == domain) {
// Repository class not overriden (extends CrudRepository<BusinessDomain, Long>)
return businessDomainRepository.save(businessDomain);
} else {
domain.description(businessDomain.description());
domain.subDomains(businessDomain.subDomains());
return businessDomainRepository.save(domain);
}
} catch (Exception e) {
throw new OrcaException(e);
}
}
And my Test:
@RunWith(SpringRunner.class)
@SpringBootTest
public class BusinessDomainTest {
@Test
@Transactional
public void createBusinessDomainWithSubDomain() throws OrcaException {
BusinessDomain passengerBusinessDomain = new BusinessDomain().name(DOMAIN_NAME).description(DOMAIN_DESCRIPTION);
BusinessSubDomain distributionBusinessSubDomain = new BusinessSubDomain().name(SUBDOMAIN_NAME)
.description(SUBDOMAIN_DESCRIPTION);
passengerBusinessDomain.addSubDomain(distributionBusinessSubDomain);
businessDomainService.saveOrUpdate(passengerBusinessDomain);
BusinessDomain domain = businessDomainService.findByName(DOMAIN_NAME);
}
}
With this I get the error message:
[main] INFO com.myproject.project.test.model.soadatamodel.business.domaining.BusinessDomainTest - Started BusinessDomainTest in 24.161 seconds (JVM running for 26.159) [main] INFO org.springframework.test.context.transaction.TransactionContext - Began transaction (1) for test context [DefaultTestContext@76480730 testClass = BusinessDomainTest, testInstance = com.myproject.project.test.model.soadatamodel.business.domaining.BusinessDomainTest@405a296e, testMethod = createBusinessDomainWithArea@BusinessDomainTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@5f5c2451 testClass = BusinessDomainTest, locations = '{}', classes = '{class com.myproject.project.TestApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.SpringBootTestContextCustomizer@6eceb130, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@47db50c5, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@68ceda24], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@28d97205]; rollback [true] [main] INFO org.hibernate.hql.internal.QueryTranslatorFactoryInitiator - HHH000397: Using ASTQueryTranslatorFactory Hibernate: select businessdo0_.id as id1_1_, businessdo0_.description as descript2_1_, businessdo0_.name as name3_1_ from BusinessDomain businessdo0_ where businessdo0_.name=? Hibernate: call next value for hibernate_sequence Hibernate: call next value for hibernate_sequence [main] WARN org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 23502, SQLState: 23502 [main] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - NULL not allowed for column "BUSINESS_DOMAIN_ID"; SQL statement: insert into BusinessSubDomain (description, business_domain_id, name, id) values (?, ?, ?, ?) [23502-168] [main] INFO org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl - HHH000010: On release of batch it still contained JDBC statements org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:278) at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:488) at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59) at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy134.findByName(Unknown Source) at com.myproject.project.service.soadatamodel.business.domaining.impl.BusinessDomainServiceImpl.findByName(BusinessDomainServiceImpl.java:40) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy137.findByName(Unknown Source) at com.myproject.project.test.model.soadatamodel.business.domaining.BusinessDomainTest.createBusinessDomainWithArea(BusinessDomainTest.java:82) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)Hibernate: call next value for hibernate_sequence Hibernate: insert into BusinessDomain (description, name, id) values (?, ?, ?) Hibernate: insert into BusinessSubDomain (description, business_domain_id, name, id) values (?, ?, ?, ?) could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
at java.lang.reflect.Method.invoke(Method.java:483) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:239) at org.junit.rules.RunRules.evaluate(RunRules.java:20) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:283) at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:173) at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:153) at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:128) at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:203) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:155) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:103) Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:112) at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95) at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:207) at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:45) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2949) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3449) at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:89) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:582) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:456) at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:50) at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1251) at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1319) at org.hibernate.internal.QueryImpl.list(QueryImpl.java:87) at org.hibernate.jpa.internal.QueryImpl.list(QueryImpl.java:606) at org.hibernate.jpa.internal.QueryImpl.getSingleResult(QueryImpl.java:529) at org.hibernate.jpa.criteria.compile.CriteriaQueryTypeQueryAdapter.getSingleResult(CriteriaQueryTypeQueryAdapter.java:54) at org.springframework.data.jpa.repository.query.JpaQueryExecution$SingleEntityExecution.doExecute(JpaQueryExecution.java:206) at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:85) at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:116) at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:106) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:483) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ... 56 more Caused by: org.h2.jdbc.JdbcSQLException: NULL not allowed for column "BUSINESS_DOMAIN_ID"; SQL statement: insert into BusinessSubDomain (description, business_domain_id, name, id) values (?, ?, ?, ?) [23502-168] at org.h2.message.DbException.getJdbcSQLException(DbException.java:329) at org.h2.message.DbException.get(DbException.java:169) at org.h2.message.DbException.get(DbException.java:146) at org.h2.table.Column.validateConvertUpdateSequence(Column.java:293) at org.h2.table.Table.validateConvertUpdateSequence(Table.java:689) at org.h2.command.dml.Insert.insertRows(Insert.java:120) at org.h2.command.dml.Insert.update(Insert.java:84) at org.h2.command.CommandContainer.update(CommandContainer.java:75) at org.h2.command.Command.executeUpdate(Command.java:230) at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:156) at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:142) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:114) at com.sun.proxy.$Proxy130.executeUpdate(Unknown Source) at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204) ... 84 more [main] INFO org.springframework.test.context.transaction.TransactionContext - Rolled back transaction for test context [DefaultTestContext@76480730 testClass = BusinessDomainTest, testInstance = com.myproject.project.test.model.soadatamodel.business.domaining.BusinessDomainTest@405a296e, testMethod = createBusinessDomainWithArea@BusinessDomainTest, testException = [ERROR CODE : UNKNWN001] Unknown Error [Exception :org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement], mergedContextConfiguration = [WebMergedContextConfiguration@5f5c2451 testClass = BusinessDomainTest, locations = '{}', classes = '{class com.myproject.project.TestApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.SpringBootTestContextCustomizer@6eceb130, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@47db50c5, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@68ceda24], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]]. [main] INFO org.springframework.test.context.transaction.TransactionContext - Began transaction (1) for test context [DefaultTestContext@76480730 testClass = BusinessDomainTest, testInstance = com.myproject.project.test.model.soadatamodel.business.domaining.BusinessDomainTest@685e8e17, testMethod = createSimpleBusinessDomain@BusinessDomainTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@5f5c2451 testClass = BusinessDomainTest, locations = '{}', classes = '{class com.myproject.project.TestApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.SpringBootTestContextCustomizer@6eceb130, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@47db50c5, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@68ceda24], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@28d97205]; rollback [true]
Upvotes: 0
Views: 972
Reputation: 31
Finally I understood.
First, the entity adder must be
public void addSubDomain(BusinessSubDomain subDomain) {
subDomains.add(subDomain);
subDomain.domain(this);
}
But this was causing an infinite loop with an infinite call to hash method.
I solved this (caused by lombok) using @Getter and @Setter instead of @Data in my entity.
Thanks
Upvotes: 1