2013-07-26 11 views
5

sto usando H2 in-memory database per il test e la mia configurazione è la seguente:org.hibernate.PersistentObjectException: separato da entità passò a persistere con H2 in-memory database

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 metodi:

@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- Entity dell'utente:

@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; 

PROBLEMA: il codice nel metodo di prova funziona molto bene quando si esegue l'applicazione (l banca dati Ocal) su JBoss e invocando il codice dal metodo di backing bean, ma quando si esegue da prova (in-memory database) che consente di risparmiare società e quando si cerca di salvare l'utente dà l'eccezione:

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 
+0

po 'di codice è mancante nel tuo test. Cos'è un'azienda? – ben75

risposta

10

Credo che il problema è qui:

@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. 

    } 

Quindi come lo risolviamo? Si può lasciare all'utente di risparmiare oggetto sociale, in modo da non si salva azienda in modo esplicito, in quanto era in cascade.all per oggetto azienda utente, la società deve essere salvato come pure quando l'utente viene salvato:

@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 

    } 
+0

ok ci proverò, ma la cosa strana è che esattamente gli stessi passaggi che ho postato funziona molto bene senza problemi nella modalità di sviluppo (quando eseguo l'applicazione su jboss e invoco il metodo) ma solo l'eccezione nei test, qualsiasi idee? –

+1

hai ragione, questo ha risolto il problema, ma ciò che mi stupisce è che quel codice fallito funziona bene nel metodo di azione bean gestito e fallisce solo nel test !!!!!!!!!!! 1 –

+0

se 'public void signup() 'è contrassegnato come transazione, non fallirà. potrebbe essere il caso. – Elbek

Problemi correlati