org.hibernate.AssertionFailure: null identifier @OneToOne relationship
Asked Answered
P

6

20

I keep getting org.hibernate.AssertionFailure: null identifier when trying to create an object. My classes are as follows:

public class User {

    @Id
    @Column(name = "user_id", nullable = false)
    private String userId;

    @OneToOne(cascade = CascadeType.ALL, optional = true, mappedBy = "user")
    private UserDetail userDetail;
}

public class UserDetail {

    @Id
    @Column(name = "user_id", nullable = false)
    private String userId;

    @OneToOne(optional = false)
    @PrimaryKeyJoinColumn
    private User user;
}

@Service
public class UserDetailService {

   @Autowired
   private UserDetailRepository userDetailRepository;

   public void create(UserDetail userDetail) {
      userDetailRepository.saveAndFlush(userDetail);
   }
}

And when I try to create a UserDetail:

String userId = "user";
User user = userService.findByUserId(userId);
UserDetail userDetail = new UserDetail();
userDetail.setUserId(userId);
userDetail.setUser(user);
user.setUserDetail(userDetail);
userDetailService.create(userDetail);

I get the exception

Stack trace of the exception:

org.hibernate.AssertionFailure: null identifier
at org.hibernate.engine.spi.EntityKey.<init>(EntityKey.java:68)
at org.hibernate.internal.AbstractSessionImpl.generateEntityKey(AbstractSessionImpl.java:327)
at org.hibernate.type.OneToOneType.isNull(OneToOneType.java:110)
at org.hibernate.type.EntityType.resolve(EntityType.java:500)
at org.hibernate.type.EntityType.replace(EntityType.java:366)
at org.hibernate.type.AbstractType.replace(AbstractType.java:178)
at org.hibernate.type.TypeHelper.replace(TypeHelper.java:211)
at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:444)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:249)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:317)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:186)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:85)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:876)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:858)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:863)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1196)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:291)
at com.sun.proxy.$Proxy45.merge(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:410)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush(SimpleJpaRepository.java:421)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:416)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:401)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:373)
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:281)
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)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:122)
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.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy49.saveAndFlush(Unknown Source)
at com.my.services.RepositoryUserDetailService.create(RepositoryUserDetailService.java:17)
at com.my.services.RepositoryUserDetailServiceTest.testCreate(RepositoryUserDetailServiceTest.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
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:73)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:217)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
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:68)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Peyote answered 13/2, 2015 at 20:40 Comment(0)
C
29

I was stuck with a very similar problem. It seems that saving the UserDetails first, while there are also changes to the User (user.setUserDetail(userDetail)), requires an explicitly set cascade mode.

Adding the CascadeType.ALL to the UserDetails @OneToOne definition fixed it for me.

Catafalque answered 28/1, 2019 at 15:44 Comment(5)
This fixed my problem. After migrating from hibernate 4 to 5 I was getting the very same exception.Eiderdown
I was faced with the same issue in hibernate 5.4.15 and Spring data jpa 2.2.7. Adding CascadeType.ALL was the solution.Danzig
But why? Why should I put CascadeType.All?Sanctified
@JinKwon I haven't seen the Hibernate code, so I can only make an educated guess: Since both entities are modified, they will both end up in the pre-save checks and as there is no cascade definition, the assumption is made that they both must be in a state where they can be saved individually. Just that they can't: 'User' references a 'UserDetail' which doesn't have an ID yet. From a database perspective, this is nonsense. From an object layer perspective, this is... nit-picky.Catafalque
@Catafalque Thank you for your comment. Seems related to hibernate.atlassian.net/browse/HHH-13866Sanctified
S
2

In my case it works by setting the relationship to null. Looks like it loads it anyway using the @Id.

UserDetail userDetail = new UserDetail();
userDetail.setUserId(userId);
userDetailService.create(userDetail);
Sputnik answered 30/1, 2020 at 21:17 Comment(2)
This does work for me but I have no idea the reason. Can you explain the reason?Naevus
That's absolutely insance solution, but it does work with Spring Boot 2..6.7!Animism
S
0

For me it helped to upgrade from 4.2.x version of Hibernate to 4.3.11. The exception does not show up anymore, looks like a bug that has been fixed.

Solnit answered 10/10, 2018 at 11:58 Comment(0)
P
0

In my case, save parent before, add parent to relationship field of child (child haven't id), after save child (id get from Parent Entity) references: https://www.baeldung.com/hibernate-identifiers

@Entity
public class UserProfile {

    @Id
    private long profileId;
    
    @OneToOne
    @MapsId
    private User user;

    // ...
}



@Test
public void whenSaveDerivedIdEntity_thenOk() {        
    User user = new User();
    session.save(user);
       
    UserProfile profile = new UserProfile();
    profile.setUser(user);
    session.save(profile);

    assertThat(profile.getProfileId()).isEqualTo(user.getUserId());
}
Pemphigus answered 15/8, 2022 at 20:38 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Polio
I
0

I had a similar issue and I've managed to solve it. Try this:

@OneToOne(cascade = CascadeType.MERGE, optional = false)
@PrimaryKeyJoinColumn(name = "user_id")
private User user;

From what I've been able to find out, when persisting UserDetail, Hibernate takes a different code path and an underlying merge() call is being made instead of persist(), as you have stated in your issue. One fix could be adding CascadeType.MERGE.

Please have in mind that this fix does have the effect that the underlying User could be updated.

Another possible fix could be to use Spring Data Persistable interface, that gives you control of determining if an object is a new object or not, making the repository save method take the correct path (merge or persist).

Sources:

Also answered here: https://mcmap.net/q/663281/-onetoone-association-produces-error-on-5-4-0-when-using-postgresql

Ignatzia answered 19/11, 2022 at 12:49 Comment(0)
A
0

My current fix is by implementing the Persistable-interface as Kashan Nadeem pointed out. Here the relevant code:

@Override
public boolean isNew() {
  return id == null;
}
Admire answered 24/1 at 9:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.