1

I'm using Spring Data and Hibernate and get an error when I'm trying to save entity with set id:

org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.snp.cm.persistency.contact.Contact; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.snp.cm.persistency.contact.Contact
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:668)

Please help me how can I update an entity via JpaRepositories or what config do I miss?

persistence.xml:

<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="contactManagerPU">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>

    <class>com.snp.cm.persistency.contact.Contact</class>
    <class>com.snp.cm.persistency.contact.TelephoneNumber</class>

    <exclude-unlisted-classes>false</exclude-unlisted-classes>

    <properties>
        <property name="hibernate.max_fetch_depth" value="3" />
    </properties>
</persistence-unit>

spring context:

<?xml version="1.0" encoding="UTF-8" standalone="no"?><beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
    http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"
default-lazy-init="true">

<import resource="classpath*:META-INF/spring/dbs-repository-context.xml" />

<!-- Activates JPA's @PersistenceContext and @PersistenceUnit (if available) 
    annotations to be detected in bean classes. -->
<context:annotation-config />

<!-- Enable Transaction using @Transactional annotation -->
<tx:annotation-driven />

<bean id="daTM"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
    p:dataSource-ref="dataSource" />

<!-- Create local transaction manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
    p:entityManagerFactory-ref="entityManagerFactory" lazy-init="true"
    p:dataSource-ref="dataSource" />

<!-- Create EntityManagerFactory for injection into services. -->
<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
    p:dataSource-ref="dataSource" p:persistenceXmlLocation-ref="persistenceXmlLocation">
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="true" />
        </bean>
    </property>
</bean>

<!-- Database access configuration -->
<bean id="persistenceXmlLocation" class="java.lang.String">
    <constructor-arg value="classpath*:META-INF/persistence.xml"></constructor-arg>
</bean>

<context:property-placeholder
    location="classpath:META-INF/spring/jdbc.properties" />

<!-- Dev's env DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="${jdbc.driver}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>

I used OpenJpa in another project and didn't have such issue: 'save' methode did create and update if an entity has set id. So how can I update an object? Should I implement my own update methode?

Failed test:

@Test
public void testRepositorySaveMethod() {
    Contact contact = new Contact();
    contact.setLastName("Carter");
    contact.setName("John");
    contact.setNickName(null);
    contact = this.contactRepository.save(contact);
    Assert.assertNotNull(contact.getContactId());

    TelephoneNumber telephoneNumber1 = new TelephoneNumber();
    telephoneNumber1.setTelephoneNumber("777 7777777");
    telephoneNumber1.setContact(contact);
    telephoneNumber1.setTelephoneType(TelephoneType.HOME);
    this.telephoneNumberRepository.save(telephoneNumber1);    // failes cause contact has already id

    contact = this.contactRepository.findOne(contact.getContactId());

    Assert.assertEquals(1, contact.getTelephoneNumbers().size());
}
Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
Snoopman
  • 11
  • 1
  • 4
  • You need to show some code. You have only provided the configurations for your ORM and beans here. The root cause is surely your code. – Satadru Biswas Aug 04 '12 at 22:57

2 Answers2

4

The reason is the following. The save(…) method of the repository proxy is a transactional one. As you have a JpaTransactionManager configured the lifecycle of the Hibernate Session is bound to the transaction. This results in the Session (and transaction) being closed when the call returns from save(…) in your test case. Thus the entity is not attached to the Session anymore which causes the exception you see later on.

The solution in test cases usually is to mark the test method as @Transactional. Assuming you're using the Spring test context framework, this will cause a transaction (and thus a Session) being opened (and held open) for the entire test method.

Aside from that, the declaration of the DataSourceTransactionManager is obsolete as you don't seem to use it anywhere.

Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
  • yeah, thanks a lot, you are right. Hm, but now I got an error on last assert cause my @OneToMany relationship does not work. – Snoopman Aug 16 '12 at 19:58
  • @Snoopman Because the telephone collection is lazily loaded and the session is already closed when assert is called. Did you mark the test method with `@Transactional` as Oliver suggested? – Adam Dyga Nov 21 '13 at 15:26
0

Everything is said by: detached entity passed to persist

Probably you are trying to do something like below:

Entity detachedEntity = getDetachedEntitySomehow();
em.persist(detachedEntity); // it is causing error.

It seems id is set for your entity any trying to be updated using persist.

You can take a look at this answer too: “detached entity passed to persist error” with JPA/EJB code

Community
  • 1
  • 1
Kowser
  • 8,123
  • 7
  • 40
  • 63