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

win.doyto.query.core.AbstractService Maven / Gradle / Ivy

There is a newer version: 0.2.2.1-RELEASE
Show newest version
package win.doyto.query.core;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.support.NoOpCache;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.SimpleTransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionOperations;
import org.springframework.transaction.support.TransactionTemplate;
import win.doyto.query.cache.CacheWrapper;
import win.doyto.query.entity.EntityAspect;
import win.doyto.query.entity.Persistable;
import win.doyto.query.entity.UserIdProvider;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.Id;

import static win.doyto.query.core.CrudBuilder.resolveColumn;

/**
 * AbstractService
 *
 * @author f0rb on 2019-05-28
 */
public abstract class AbstractService, I extends Serializable, Q>
    implements CommonCrudService {

    private final RowMapper rowMapperForId = new SingleColumnRowMapper<>();

    protected DataAccess dataAccess;

    protected final CacheWrapper entityCacheWrapper = CacheWrapper.createInstance();

    @Autowired(required = false)
    private UserIdProvider userIdProvider;

    @Autowired(required = false)
    protected List> entityAspects = new LinkedList<>();

    protected Class entityType;

    protected TransactionOperations transactionOperations = NoneTransactionOperations.instance;
    private final String idColumn;

    public AbstractService() {
        entityType = getEntityType();
        dataAccess = new MemoryDataAccess<>(entityType);
        Field idField = FieldUtils.getFieldsWithAnnotation(entityType, Id.class)[0];
        idColumn = resolveColumn(idField);
    }

    @SuppressWarnings("unchecked")
    private Class getEntityType() {
        return (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    @Autowired
    public void setJdbcOperations(JdbcOperations jdbcOperations) {
        dataAccess = new JdbcDataAccess<>(jdbcOperations, entityType);
    }

    @Autowired(required = false)
    public void setJdbcOperations(PlatformTransactionManager transactionManager) {
        transactionOperations = new TransactionTemplate(transactionManager);
    }

    @Autowired(required = false)
    public void setCacheManager(CacheManager cacheManager) {
        String cacheName = getCacheName();
        if (cacheName != null) {
            entityCacheWrapper.setCache(cacheManager.getCache(cacheName));
        }
    }

    protected abstract String resolveCacheKey(E e);

    protected String getCacheName() {
        return null;
    }

    public List query(Q query) {
        return dataAccess.query(query);
    }

    public final long count(Q query) {
        return dataAccess.count(query);
    }

    public final List queryIds(Q query) {
        return queryColumns(query, rowMapperForId, idColumn);
    }

    public final  List queryColumns(Q query, RowMapper rowMapper, String... columns) {
        return dataAccess.queryColumns(query, rowMapper, columns);
    }

    public final  List queryColumns(Q query, Class clazz, String... columns) {
        return queryColumns(query, new BeanPropertyRowMapper<>(clazz), columns);
    }

    public final  List queryColumn(Q query, Class clazz, String column) {
        return queryColumns(query, new SingleColumnRowMapper<>(clazz), column);
    }

    public final void create(E e) {
        if (userIdProvider != null) {
            userIdProvider.setupUserId(e);
        }
        if (!entityAspects.isEmpty()) {
            transactionOperations.execute(s -> {
                dataAccess.create(e);
                entityAspects.forEach(entityAspect -> entityAspect.afterCreate(e));
                return null;
            });
        } else {
            dataAccess.create(e);
        }
        entityCacheWrapper.evict(resolveCacheKey(e));
    }

    public final void update(E e) {
        doUpdate(e, () -> dataAccess.update(e));
    }

    public final void patch(E e) {
        doUpdate(e, () -> dataAccess.patch(e));
    }

    private void doUpdate(E e, Runnable runnable) {
        if (userIdProvider != null) {
            userIdProvider.setupUserId(e);
        }
        if (!entityAspects.isEmpty()) {
            transactionOperations.execute(s -> {
                E origin = dataAccess.fetch(e.getId());
                runnable.run();
                entityAspects.forEach(entityAspect -> entityAspect.afterUpdate(origin, e));
                return null;
            });
        } else {
            runnable.run();
        }
        entityCacheWrapper.evict(resolveCacheKey(e));
    }

    public int batchInsert(Iterable entities) {
        int insert = dataAccess.batchInsert(entities);
        entityCacheWrapper.clear();
        return insert;
    }

    public final int patch(E e, Q q) {
        int patch = dataAccess.patch(e, q);
        entityCacheWrapper.clear();
        return patch;
    }

    public final int delete(Q query) {
        int delete = dataAccess.delete(query);
        entityCacheWrapper.clear();
        return delete;
    }

    public final boolean exists(Q query) {
        return count(query) > 0;
    }

    protected boolean caching() {
        return !(entityCacheWrapper.getCache() instanceof NoOpCache);
    }

    private static class NoneTransactionOperations implements TransactionOperations {
        private static final TransactionOperations instance = new NoneTransactionOperations();
        private static final TransactionStatus TRANSACTION_STATUS = new SimpleTransactionStatus();

        @Override
        public  T execute(TransactionCallback transactionCallback) {
            return transactionCallback.doInTransaction(TRANSACTION_STATUS);
        }
    }
}