Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

org.hibernate.PersistentObjectException: detached entity passed to persist with H2 in memory database

i am using H2 in memory database for testing and my configuration is as follows:

1- SpringTestingConfig:

@Configuration
@ComponentScan(basePackages = "com.myapp.data", excludeFilters = { @Filter(Configuration.class) })
@PropertySource("classpath:/test.properties")
@Profile("test")
public class SpringTestingConfig {
   @Bean
   public DataSource dataSource() {
      DriverManagerDataSource dataSource = new DriverManagerDataSource();
      dataSource.setDriverClassName("org.h2.Driver");
      dataSource.setUrl("jdbc:h2:mem:test;MODE=Mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS MYAPP");
      dataSource.setUsername("sa");
      dataSource.setPassword("");
      return dataSource;
   }
}

2- MyTestClass:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({ WebContextTestExecutionListener.class,
        DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class,
        TransactionalTestExecutionListener.class })
@ActiveProfiles("test")
@DirtiesContext
@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = {
        SpringConfig.class, SpringTestingConfig.class,
        SpringLocalContainerJPAConfig.class, CustomConfiguration.class })
@PrepareForTest({ FacesContext.class })
@PowerMockIgnore({ "javax.management.*", "javax.xml.parsers.*",
    "com.sun.org.apache.xerces.internal.jaxp.*", "ch.qos.logback.*",
    "org.slf4j.*" })
public class MyTestClass{

   private Company company;
   private User user;


   @Test
    public void signup(){

       User user = new User();
    Company company = new Company();
    company.setName("Test");
    company = usService.saveCompany(company);
    user.setFirstName("Test");
    user.setLastName("User");
    user.setEmail("[email protected]");
    user.setPassword("verySecret");
    user.setCompany(company);
    user = usService.saveUser(user); // gives exception

   }

} 

3- Save methods:

@Transactional(propagation = Propagation.REQUIRED)
    public User saveUser(User user) {
        return userRepository.saveAndFlush(user); //JpaRepository
    }

@Transactional(propagation = Propagation.REQUIRED)
    public Company saveCompany(Company company) {
        return companyRepository.saveAndFlush(company); //JpaRepository
    }

4- JPA Config

@Configuration
@EnableSpringConfigured
public class SpringJNDIJPAConfig {
protected static final Logger logger = LoggerFactory.getLogger(SpringConfig.class);
protected static final String HIBERNATE_TRANSACTION_JTA_PLATFORM = "hibernate.transaction.jta.platform";

@Value("${hibernate.naming_strategy:org.hibernate.cfg.DefaultNamingStrategy}")
private String namingStrategy;

@Value("${hibernate.packages_to_scan:com.myapp.data.domain}")
private String packagesToScan;

@Value("${spring_config.project_name}")
private String projectName;

@Value("${hibernate.show_sql:false}")
private String showSql;

@Value("${hibernate.hbm2ddl.auto:update}")
private String hbm2ddlAuto;

@Value("${hibernate.format_sql:false}")
private String formatSql;

@Value("${hibernate.dialect:org.hibernate.dialect.MySQL5InnoDBDialect}")
private String hibernateDialect;

@Value("${hibernate.connection.useUnicode:true}")
private String useUnicode;

@Value("${hibernate.connection.characterEncoding:UTF-8}")
private String characterEncoding;

@Value("${hibernate.charSet:UTF-8}")
private String charSet;

@Value("${hibernate.default_schema}")
private String defaultSchema;

@Value("${hibernate.use_default_schema:true}")
private boolean useDefaultSchema;

@Value("${hibernate.use_sql_comments:true}")
private String useSqlComments;

@Autowired
private ApplicationContext applicationContext;

@Autowired
private DataSource dataSource;

@Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
  return new HibernateExceptionTranslator();
}

@Bean
protected EntityManagerFactory entityManagerFactory() {
  LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();

  JtaPersistenceUnitManager puManager = new JtaPersistenceUnitManager();
  Map<String, DataSource> dataSources = new HashMap<String, DataSource>();
  dataSources.put("dataSource", dataSource);
  puManager.setDataSourceLookup(new MapDataSourceLookup(dataSources));
  puManager.setDefaultDataSource(dataSource);
  puManager.setPackagesToScan(packagesToScan());
  bean.setPersistenceUnitManager(puManager);

  bean.setPersistenceProviderClass(HibernatePersistence.class);
  bean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

  Properties jpaProperties = getHibernateProperties();
  jpaProperties.put(HIBERNATE_TRANSACTION_JTA_PLATFORM, SpringJtaPlatform.class.getName().toString());
  bean.setJpaProperties(jpaProperties);

  puManager.afterPropertiesSet();
  bean.afterPropertiesSet();
  return bean.getObject();
}

protected String getDefaultSchema() {
  String ds = ConfigurationUtil.config().getString("db.schema.name");
  if (ds != null) defaultSchema = ds;
  return defaultSchema;
}

protected String getUseUnicode() {
      return useUnicode;
}

protected String getCharacterEncoding() {
      return characterEncoding;
}

protected String getCharSet() {
      return charSet;
}

protected String getFormatSql() {
  return formatSql;
}

protected String getHbm2ddlAuto() {
  return hbm2ddlAuto;
}

protected String getHibernateDialect() {
  return hibernateDialect;
}

protected Properties getHibernateProperties() {
  Properties properties = new Properties();
  properties.put("hibernate.dialect", getHibernateDialect());
  properties.put("hibernate.hbm2ddl.auto", getHbm2ddlAuto());
  properties.put("hibernate.show_sql", getShowSql());
  properties.put("hibernate.use_sql_comments", getUseSqlComments());
  properties.put("hibernate.format_sql", getFormatSql());
  if(useDefaultSchema) {
     properties.put("hibernate.default_schema", getDefaultSchema());
  }
  //properties.put("hibernate.ejb.naming_strategy", namingStrategy);
  properties.put("hibernate.hbm2ddl.import_files", "/import.sql");
  //properties.put("hibernate.connection.characterEncoding", getCharacterEncoding());
  //properties.put("hibernate.connection.charSet", getCharSet());
  //properties.put("hibernate.connection.useUnicode", getUseUnicode());
  if(logger.isInfoEnabled()) {
     logger.info(MessageFormat.format("SET HIBERNATE PROPERTIES: {0}", properties.toString()));
  }
  return properties;
}

protected String getProjectName() {
  return projectName;
}

protected String getShowSql() {
  return showSql;
}

protected String getUseSqlComments() {
  return useSqlComments;
}

protected String packagesToScan() {
      return packagesToScan;
   }

@Bean
protected JtaTransactionManager transactionManager() {
  SpringBeanFactory.setApplicationContext(applicationContext);
  JtaTransactionManager manager = new JtaTransactionManager();
  manager.setTransactionManagerName("java:jboss/TransactionManager");
  manager.setUserTransactionName("java:jboss/UserTransaction");
  manager.afterPropertiesSet();
  return manager;
}

}

5- User Entity:

@Entity
@Table(name = "User", uniqueConstraints = { @UniqueConstraint(columnNames = {
        "CompanyGID", "MPath" }) })
public class User extends PersistableEntity implements UserDetails {
    /**
     * 
     */
    private static final long serialVersionUID = -6520416613985790209L;

    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "CompanyGID")
    private Company company;

ISSUE: the code in test method works very fine when running the application (local database) on Jboss and invoking the code from backing bean method, but when running it from test (in memory database) it saves company and when trying to save user it gives the exception:

    org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.myapp.data.domain.Company; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.myapp.data.domain.Company
    at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:668)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:106)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:403)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    at com.myapp.service.UserService.saveUser_aroundBody6(UserService.java:98)
    at com.myapp.service.UserService$AjcClosure7.run(UserService.java:1)
    at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96cproceed(AbstractTransactionAspect.aj:59)
    at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect$1.proceedWithInvocation(AbstractTransactionAspect.aj:65)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
    at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(AbstractTransactionAspect.aj:63)
    at com.myapp.service.UserService.saveUser(UserService.java:93)
    at com.myapp.service.UserService$$FastClassByCGLIB$$697e2a1b.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
    at com.myapp.service.UserService$$EnhancerByCGLIB$$abc2864a.saveUser(<generated>)
    at test.myapp.web.controllers.SignUpBeanTest.testSignUp(SignUpBeanTest.java:126)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    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)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.myapp.data.domain.Company
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:141)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:842)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:835)
    at org.hibernate.ejb.engine.spi.EJB3CascadingAction$1.cascade(EJB3CascadingAction.java:53)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:387)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:330)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:424)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:263)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:192)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:135)
    at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:78)
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:208)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:151)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:78)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:852)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:826)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:830)
    at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:875)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:241)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:354)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush(SimpleJpaRepository.java:368)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:333)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:318)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
    ... 38 more
like image 657
Mahmoud Saleh Avatar asked Jul 26 '13 17:07

Mahmoud Saleh


1 Answers

I think the issue is here:

 @Test
    public void signup(){

    User user = new User();
    Company company = new Company();
    company.setName("Test");
    company = usService.saveCompany(company); //object is saved and transaction is closed, so company is detached here. 
    user.setFirstName("Test");
    user.setLastName("User");
    user.setEmail("[email protected]");
    user.setPassword("verySecret");
    user.setCompany(company); //u are setting this detached object to user, NOTE user object's company attr is cascade.all which means this company will be saved as well when you save user. 
    user = usService.saveUser(user); // gives exception, because you are saving new user object with detached company object. 

   }

So how do we solve it? You can let user save company object, so you do not save company explicitly, since cascade.all was on for company object in user, company should be saved as well when user is being saved:

@Test
    public void signup(){

    User user = new User();
    Company company = new Company();
    company.setName("Test");
    user.setFirstName("Test");
    user.setLastName("User");
    user.setEmail("[email protected]");
    user.setPassword("verySecret");
    user.setCompany(company);
    user = usService.saveUser(user); // gives exception

   }
like image 92
Elbek Avatar answered Nov 15 '22 09:11

Elbek