Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate JPA removing detached object

I have this bit of code in our service layer:

@Override
public <T extends BaseEntity> T find(Long id, Class<? extends BaseEntity> clazz) throws Exception {
    BaseEntity e = em.find(clazz, id);
    if (e != null) {
        deserialize(e, e.getClass());
    } else
        LOG.error("Found null for id " + id);
    return (T) e;
}

public void delete(BaseEntity e, String index, String type) throws Exception {
    if (e == null)
        return;
    if (e.getId() == null)
        return;
    Delete delete = new Delete.Builder(e.getId().toString()).index(index).type(type).build();
    getJestClient().execute(delete);
    if (em.contains(e)) {
        em.remove(e);
    } else {
        BaseEntity ee = find(e.getId(), e.getClass());
        em.remove(ee);
    }
}

 protected void deserialize(BaseEntity dst, Class<?> dstClass) throws Exception {
    Object src  = serializer.deserialize(new String(dst.getContent(), "UTF-8"), dstClass);

    for (Field f : getClassFields(src.getClass())) {
        if (Modifier.isStatic(f.getModifiers())) continue;
        if (!f.isAnnotationPresent(Expose.class)) continue;

        f.setAccessible(true);

        f.set(dst, f.get(src));
        LOG.trace("deserializing " + f.getName() + " : " + f.get(src));
    }
}

However, if I use it to remove the entity, it will fail with:

Caused by: java.lang.IllegalArgumentException: Removing a detached instance FooEntity
at org.hibernate.ejb.event.EJB3DeleteEventListener.performDetachedEntityDeletionCheck(EJB3DeleteEventListener.java:65)
at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:107)
at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:73)
at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:956)
at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:934)
at org.hibernate.ejb.AbstractEntityManagerImpl.remove(AbstractEntityManagerImpl.java:867)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:365)
at $Proxy42.remove(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
at $Proxy31.remove(Unknown Source)
... 16 more

But that makes no sense to me, since line before em.remove(ee), entity is loaded via find method, thus it can't be detached... Transactions are implemented via spring xml configuration

// Edit: added deserialize method. Basically, it will take json content that is stored with every object and create copy of the object, from which then fields are assigned to. getJestClient().execute is handler for elasticsearch, which is unrelated to the hibernate and jpa.

Em is created via spring:

 <bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dsFoo">
    <property name="driverClassName" value="${foo.census.driverClassName}"/>
    <property name="url" value="${foo.census.url}"/>
    <property name="username" value="${foo.census.user}"/>
    <property name="password"   value="${foo.census.password}"/>
    <property name="testOnBorrow" value="true"/>
    <property name="testOnReturn" value="true"/>
    <property name="testWhileIdle" value="true"/>
    <property name="timeBetweenEvictionRunsMillis" value="1800000"/>
    <property name="numTestsPerEvictionRun" value="3"/>
    <property name="minEvictableIdleTimeMillis" value="1800000"/>
    <property name="validationQuery" value="SELECT 1"/>
</bean>

<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="emfCensus">
    <property name="persistenceUnitName" value="puFoo"/>
    <property name="dataSource"          ref="dsFoo"/>
</bean>

<bean id="emFoo" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
    <property name = "entityManagerFactory" ref="emfFoo"/>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="emfFoo" />
    <property name="persistenceUnitName"  value="puFoo" />
</bean>

<bean id="repoFoo" class="service.FooService">
    <property name="entityManager" ref="emFoo" />
</bean>

<aop:aspectj-autoproxy proxy-target-class="true"/>

<aop:config>
    <aop:pointcut id="repoFooOperation" expression="execution(* service.FooService.*(..))"/>
    <aop:advisor advice-ref="txAdviceFoo" pointcut-ref="repoFooOperation"/>
</aop:config>

<tx:advice id="txAdviceFoo" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
like image 507
Enerccio Avatar asked Oct 31 '13 12:10

Enerccio


1 Answers

Okay, thanks to stackoverflow and googling, I managed to find a way to solve it:

    if (em.contains(e)) {
        em.remove(e);
    } else {
        BaseEntity ee = em.getReference(e.getClass(), e.getId());
        em.remove(ee);
    }

is correct way to remove entity that is detached.

like image 165
Enerccio Avatar answered Oct 03 '22 04:10

Enerccio