All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.landawn.abacus.core.SessionImpl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2015, Haiyang Li.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.landawn.abacus.core;

import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.landawn.abacus.DirtyMarker;
import com.landawn.abacus.EntityId;
import com.landawn.abacus.EntityManager;
import com.landawn.abacus.IsolationLevel;
import com.landawn.abacus.Session;
import com.landawn.abacus.Transaction;
import com.landawn.abacus.Transaction.Action;
import com.landawn.abacus.Transaction.Status;
import com.landawn.abacus.condition.Condition;
import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.util.ExceptionUtil;
import com.landawn.abacus.util.N;
import com.landawn.abacus.util.OperationType;
import com.landawn.abacus.util.Options;
import com.landawn.abacus.util.u.Optional;

// TODO: Auto-generated Javadoc
/**
 *
 * @author Haiyang Li
 * @param 
 * @since 0.8
 */
@SuppressWarnings("deprecation")
final class SessionImpl implements Session {

    private static final Logger logger = LoggerFactory.getLogger(SessionImpl.class);

    private final Map attachedEntities = new IdentityHashMap<>();

    private final List attachedEntityList = new LinkedList<>();

    private final EntityManager entityManager;

    private final IsolationLevel isolationLevel;

    private Map options;

    private TransactionProxy transaction;

    private boolean isClosed = false;

    SessionImpl(EntityManager entityManager, IsolationLevel isolationLevel) {
        this.entityManager = entityManager;
        this.isolationLevel = isolationLevel;
    }

    /**
     *
     * @param isolationLevel
     * @return
     */
    @Override
    public Transaction beginTransaction(IsolationLevel isolationLevel) {
        assertNotClosed();

        if (isInTransaction()) {
            throw new IllegalStateException("Transaction is already started. ");
        }

        transaction = new TransactionProxy(entityManager, isolationLevel);

        return transaction;
    }

    /**
     *
     * @param 
     * @param entityId
     * @param selectPropNames
     * @return
     */
    @Override
    public  Optional get(EntityId entityId, Collection selectPropNames) {
        return Optional.ofNullable((T) gett(entityId, selectPropNames));
    }

    /**
     * Gets the t.
     *
     * @param 
     * @param entityId
     * @param selectPropNames
     * @return
     */
    @SuppressWarnings("unchecked")
    @Override
    public  T gett(EntityId entityId, Collection selectPropNames) {
        assertNotClosed();

        Map options = setTransaction();

        T entity = (T) entityManager.gett(entityId, selectPropNames, options);

        if (entity != null) {
            attach(OperationType.UPDATE, (E) entity);
        }

        return entity;
    }

    /**
     *
     * @param 
     * @param entityName
     * @param selectPropNames
     * @param condition
     * @return
     */
    @SuppressWarnings("unchecked")
    @Override
    public  List list(String entityName, Collection selectPropNames, Condition condition) {
        assertNotClosed();

        Map options = setTransaction();

        List entities = entityManager.list(entityName, selectPropNames, condition, options);

        if (N.notNullOrEmpty(entities)) {
            attach(OperationType.UPDATE, (List) entities);

            return entities;
        } else {
            return new ArrayList<>();
        }
    }

    /**
     *
     * @param entities
     */
    @SuppressWarnings("unchecked")
    @Override
    @SafeVarargs
    public final void add(E... entities) {
        assertNotClosed();

        attach(OperationType.ADD, entities);
    }

    /**
     *
     * @param entities
     */
    @Override
    public void add(Collection entities) {
        E[] arrayOfEntity = collection2Array(entities);

        add(arrayOfEntity);
    }

    /**
     *
     * @param entities
     */
    @SuppressWarnings("unchecked")
    @Override
    @SafeVarargs
    public final void update(E... entities) {
        attach(OperationType.UPDATE, entities);
    }

    /**
     *
     * @param entities
     */
    @Override
    public void update(Collection entities) {
        E[] arrayOfEntity = collection2Array(entities);

        update(arrayOfEntity);
    }

    /**
     *
     * @param entities
     */
    @SuppressWarnings("unchecked")
    @Override
    @SafeVarargs
    public final void delete(E... entities) {
        assertNotClosed();

        attach(OperationType.DELETE, entities);
    }

    /**
     *
     * @param entities
     */
    @Override
    public void delete(Collection entities) {
        E[] arrayOfEntity = collection2Array(entities);

        delete(arrayOfEntity);
    }

    /**
     *
     * @param operationType
     * @param entities
     */
    @SuppressWarnings("unchecked")
    protected void attach(OperationType operationType, E... entities) {
        assertNotClosed();

        if (N.isNullOrEmpty(entities)) {
            return;
        }

        for (E entity : entities) {
            if (attachedEntities.put(entity, operationType) == null) {
                attachedEntityList.add(entity);
            }
        }
    }

    /**
     *
     * @param operationType
     * @param entities
     */
    @SuppressWarnings("unchecked")
    protected void attach(OperationType operationType, List entities) {
        assertNotClosed();

        if (N.isNullOrEmpty(entities)) {
            return;
        }

        for (E entity : entities) {
            if (attachedEntities.put(entity, operationType) == null) {
                attachedEntityList.add(entity);
            }
        }
    }

    /**
     *
     * @param entities
     */
    @SuppressWarnings("unchecked")
    @Override
    @SafeVarargs
    public final void detach(E... entities) {
        assertNotClosed();

        if (N.isNullOrEmpty(entities)) {
            return;
        }

        for (E entity : entities) {
            if (attachedEntities.remove(entity) != null) {
                for (int i = 0; i < attachedEntityList.size(); i++) {
                    if (attachedEntityList.get(i) == entity) {
                        attachedEntityList.remove(i);

                        break;
                    }
                }
            }
        }
    }

    /**
     *
     * @param entities
     */
    @Override
    public void detach(Collection entities) {
        E[] arrayOfEntity = collection2Array(entities);

        detach(arrayOfEntity);
    }

    /**
     *
     * @param entity
     * @return true, if successful
     */
    @Override
    public boolean contains(E entity) {
        assertNotClosed();

        return attachedEntities.containsKey(entity);
    }

    /**
     * Flush.
     */
    @Override
    public void flush() {
        assertNotClosed();

        if (N.isNullOrEmpty(attachedEntityList)) {
            return;
        }

        Map options = setTransaction();
        String transactionId = null;

        if (!isInTransaction() && (attachedEntityList.size() > 1)) {
            transactionId = entityManager.beginTransaction(isolationLevel, null);

            if (options == null) {
                options = ParametersUtil.asOptions();
            }

            options.put(Options.TRANSACTION_ID, transactionId);
        }

        OperationType preOperationType = null;
        OperationType operationType = null;
        List preEntities = new ArrayList<>();
        boolean isOk = false;

        try {
            for (E entity : attachedEntityList) {
                operationType = attachedEntities.get(entity);

                if ((preEntities.size() > 0) && ((operationType != preOperationType) || (!entity.getClass().equals(preEntities.get(0).getClass())))) {
                    commit(preEntities, preOperationType, options);

                    preEntities.clear();
                }

                if (operationType == OperationType.ADD) {
                    preEntities.add(entity);
                } else if (operationType == OperationType.DELETE) {
                    preEntities.add(entity);
                } else {
                    if (entity instanceof DirtyMarker) {
                        if (DirtyMarkerUtil.isDirty((DirtyMarker) entity)) {
                            entityManager.update(entity, options);
                        }
                    } else {
                        entityManager.update(entity, options);
                    }
                }

                preOperationType = operationType;
            }

            if (preEntities.size() > 0) {
                commit(preEntities, preOperationType, options);

                preEntities.clear();
            }

            isOk = true;
        } finally {
            if (transactionId != null) {
                options.remove(Options.TRANSACTION_ID);

                if (isOk) {
                    entityManager.endTransaction(transactionId, Action.COMMIT, null);
                } else {
                    try {
                        entityManager.endTransaction(transactionId, Action.ROLLBACK, null);
                    } catch (Exception e) {
                        // ignore
                        logger.error("Failed to roll back with transaction id: " + transactionId + ". " + ExceptionUtil.getMessage(e), e);
                    }
                }
            }

            if (isOk) {
                refreshStatus();
            }
        }
    }

    /**
     * Clear.
     */
    @Override
    public void clear() {
        assertNotClosed();

        attachedEntities.clear();
        attachedEntityList.clear();
    }

    /**
     * Checks if is dirty.
     *
     * @return true, if is dirty
     */
    @Override
    public boolean isDirty() {
        assertNotClosed();

        OperationType operationType = null;

        for (E entity : attachedEntities.keySet()) {
            operationType = attachedEntities.get(entity);

            if ((operationType == OperationType.ADD) || (operationType == OperationType.DELETE)) {
                return true;
            } else {
                if (entity instanceof DirtyMarker) {
                    if (DirtyMarkerUtil.isDirty((DirtyMarker) entity)) {
                        return true;
                    }
                } else {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Close.
     */
    @Override
    public void close() {
        assertNotClosed();

        boolean isOk = false;

        try {
            flush();

            isOk = true;
        } finally {
            if (isInTransaction()) {
                if (isOk) {
                    transaction.commit();
                } else {
                    transaction.rollback();
                }
            }
        }

        clear();

        isClosed = true;
    }

    /**
     * Checks if is closed.
     *
     * @return true, if is closed
     */
    @Override
    public boolean isClosed() {
        return isClosed;
    }

    /**
     *
     * @param entities
     * @param operationType
     * @param options
     */
    @SuppressWarnings("unchecked")
    private void commit(List entities, OperationType operationType, Map options) {
        if (N.isNullOrEmpty(entities)) {
            return;
        }

        if (operationType == OperationType.DELETE) {
            if (entities.size() == 1) {
                entityManager.delete(entities.get(0), options);
            } else {
                entityManager.deleteAll(entities, options);
            }
        } else if (operationType == OperationType.ADD) {
            if (entities.size() == 1) {
                entityManager.add(entities.get(0), options);
            } else {
                entityManager.addAll(entities, options);
            }
        }
    }

    /**
     * Collection 2 array.
     *
     * @param entities
     * @return
     */
    @SuppressWarnings("unchecked")
    private E[] collection2Array(Collection entities) {
        E[] arrayOfEntity = null;

        if (entities != null) {
            arrayOfEntity = (E[]) entities.toArray();
        }

        return arrayOfEntity;
    }

    /**
     * Refresh status.
     */
    @SuppressWarnings("unchecked")
    private void refreshStatus() {
        List keys = new ArrayList<>(attachedEntities.keySet());
        OperationType op = null;

        for (E e : keys) {
            op = attachedEntities.get(e);

            if (op == OperationType.DELETE) {
                detach(e);
            } else if (op == OperationType.ADD) {
                attachedEntities.put(e, OperationType.UPDATE);
            }
        }
    }

    /**
     * Sets the transaction.
     *
     * @return
     */
    private Map setTransaction() {
        if (isInTransaction()) {
            if (options == null) {
                options = ParametersUtil.asOptions();
            }

            options.put(Options.TRANSACTION_ID, transaction.id);
        } else {
            if (options != null) {
                options.remove(Options.TRANSACTION_ID);
            }
        }

        return options;
    }

    /**
     * Checks if is in transaction.
     *
     * @return true, if is in transaction
     */
    private boolean isInTransaction() {
        if (transaction == null) {
            return false;
        } else if (transaction.status() == Status.ACTIVE) {
            return true;
        } else {
            transaction = null;

            return false;
        }
    }

    /**
     * Assert not closed.
     */
    private void assertNotClosed() {
        if (isClosed) {
            throw new IllegalStateException("The session has been closed. ");
        }
    }

    @Override
    public String toString() {
        return "{isClosed=" + isClosed + ", transactionId=" + ((transaction == null) ? "null" : transaction.id) + ", attached entities: "
                + attachedEntities.toString() + "}";
    }

    /**
     * The Class TransactionProxy.
     *
     * @author Haiyang Li
     * @version $Revision: 0.8 $ 07/03/05
     */
    static final class TransactionProxy implements Transaction {

        /** The entity manager. */
        private final EntityManager entityManager;

        /** The id. */
        private final String id;

        /** The isolation level. */
        private final IsolationLevel isolationLevel;

        /** The status. */
        private Status status;

        /**
         * Instantiates a new transaction proxy.
         *
         * @param entityManager
         * @param isolationLevel
         */
        TransactionProxy(EntityManager entityManager, IsolationLevel isolationLevel) {
            this.entityManager = entityManager;
            this.id = entityManager.beginTransaction(isolationLevel, null);
            this.isolationLevel = isolationLevel;
            this.status = Status.ACTIVE;
        }

        /**
         *
         * @return
         */
        @Override
        public String id() {
            return id;
        }

        /**
         *
         * @return
         */
        @Override
        public IsolationLevel isolationLevel() {
            return isolationLevel;
        }

        /**
         *
         * @return
         */
        @Override
        public Status status() {
            return status;
        }

        /**
         * Checks if is active.
         *
         * @return true, if is active
         */
        @Override
        public boolean isActive() {
            return status == Status.ACTIVE;
        }

        /**
         * Commit.
         */
        @Override
        public void commit() {
            if (!status.equals(Status.ACTIVE)) {
                throw new IllegalStateException("transaction is already " + status);
            }

            status = Status.FAILED_COMMIT;

            entityManager.endTransaction(id, Action.COMMIT, null);

            status = Status.COMMITTED;
        }

        /**
         * Rollback.
         */
        @Override
        public void rollback() {
            if (!status.equals(Status.ACTIVE)) {
                throw new IllegalStateException("transaction is already " + status);
            }

            status = Status.FAILED_ROLLBACK;

            entityManager.endTransaction(id, Action.ROLLBACK, null);

            status = Status.ROLLED_BACK;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy