0
votes

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]

1
going to set the "domain" on the subDomain? aka setting both sides of the relation - Neil Stockton
Hi and welcome to Stack Overflow, please take a time to go through the welcome tour to know your way around here (and also to earn your first badge), read how to create a Minimal, Complete, and Verifiable example and also check How to Ask Good Questions so you increase your chances to get feedback and useful answers.... please dont add that much content as not many users will be likely to help - DarkCygnus
Hello, I tried to set the domain on the subdomain but I got a infinite loop (cf my first post) :( - Chouch

1 Answers

1
votes

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