Reputation: 11
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
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
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