package pl.softwaremill.common.cdi.persistence;

import pl.softwaremill.common.util.persistance.Identifiable;

import javax.inject.Inject;
import javax.persistence.EntityManager;

/**
 * @author Adam Warski (adam at warski dot org)
 * @author Tomasz Szymanski (szimano at szimano dot org)
 */
public class EntityWriter {
    @Inject @ReadOnly
    private EntityManager readOnlyEm;

    @Inject @Writeable
    private EntityManager writeableEm;

    @SuppressWarnings({"unchecked"})
    /**
     * Stores changes made to an entity. All entites must have {@code DETACH} cascade enabled on all associations!
     * @param entity The entity to be written.
     * @return The written entity.
     */
    public <T extends Identifiable<?>> T write(T entity) {
        // First detaching the entity from the RO context
        if (readOnlyEm.contains(entity)) {
            readOnlyEm.detach(entity);
        } else if (entity.getId() != null) {
            // If the entity is not in the RO EM, and is persistent, it is possible that the RO EM contains a different
            // copy of the entity. It must also be detached, hence first looking it up. It is possible that the find()
            // loads the entity into the EM, but it's not possible to check if an entity is loaded into an EM simply
            // by id.
            readOnlyEm.detach(readOnlyEm.find(entity.getClass(), entity.getId()));
        }
        // Writing the changes
        T writtenEntity = writeableEm.merge(entity);
        writeableEm.flush();
        // Now looking up a fresh copy of the entity. We won't get a stale one because we removed it from the context
        // in the beginning
        return (T) readOnlyEm.find(writtenEntity.getClass(), writtenEntity.getId());
    }

    /**
     * Executes an update query, created with the given creator, using the writeable entity manager.
     * The read only entity manager is cleared, so all entities with lazy values will have to be re-read.
     * @param queryCreator A creator to create the update query.
     * @return The number of affected rows.
     */
    public int executeUpdate(QueryCreator queryCreator) {
        int result = queryCreator.createQuery(writeableEm).executeUpdate();

        // Now we need to clear the read only entity manager, so that it doesn't contain stale entities.
        // This could result in some lazy initialization exception.
        readOnlyEm.clear();

        return result;
    }

    /**
     * Deletes an entity and detaches it from the read only manager.
     * @param entity Entity to delete
     */
    @SuppressWarnings({"unchecked"})
    public <T extends Identifiable<?>> void delete(T entity) {
        // First detaching the entity from the RO context
        if (readOnlyEm.contains(entity)) {
            readOnlyEm.detach(entity);
        } else if (entity.getId() != null) {
            // If the entity is not in the RO EM, and is persistent, it is possible that the RO EM contains a different
            // copy of the entity. It must also be detached, hence first looking it up. It is possible that the find()
            // loads the entity into the EM, but it's not possible to check if an entity is loaded into an EM simply
            // by id.
            readOnlyEm.detach(readOnlyEm.find(entity.getClass(), entity.getId()));
        }

        // attach the entity
        entity = (T) writeableEm.find(entity.getClass(), entity.getId());

        // Then delete the entity
        writeableEm.remove(entity);
        writeableEm.flush();
    }
}

@@ -38,24 +38,67 @@
 rsistence;%0A%0A
+import org.hibernate.proxy.HibernateProxy;%0A
 import pl.so
@@ -325,16 +325,75 @@
 ot org)%0A
+ * @author Pawel Stawicki (pawelstawicki at gmail dot com)%0A
  */%0Apubl
@@ -3333,32 +3333,128 @@
       // by id.%0A
+            Class%3CT%3E entityTargetClass = getTargetClassIfProxied((Class%3CT%3E) entity.getClass());%0A
             read
@@ -3485,33 +3485,35 @@
 .find(entity
-.
+Tar
 getClass
 (), entity.g
@@ -3492,34 +3492,32 @@
 ntityTargetClass
-()
 , entity.getId()
@@ -3734,16 +3734,362 @@
 lush();%0A
-
     %7D%0A
+%0A    private %3CT%3E Class%3CT%3E getTargetClassIfProxied(Class%3CT%3E clazz) %7B%0A        if (clazz == null) %7B%0A            return null;%0A        %7D else if (HibernateProxy.class.isAssignableFrom(clazz)) %7B%0A            // Get the source class of Javassist proxy instance.%0A            return (Class%3CT%3E) clazz.getSuperclass();%0A        %7D%0A        return clazz;%0A    %7D%0A%0A
 %7D%0A

Fix for EntityWriter to be able to write proxied, lazy loaded entities.