Snoopman
Snoopman

Reputation: 11

Spring data + Hibernate

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());
}

Upvotes: 1

Views: 6888

Answers (2)

Oliver Drotbohm
Oliver Drotbohm

Reputation: 83171

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.

Upvotes: 4

Kowser
Kowser

Reputation: 8261

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

Upvotes: 0

Related Questions