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

win.doyto.query.jdbc.JdbcDataAccess Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2019-2024 Forb Yuan
 *
 * 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 win.doyto.query.jdbc;

import org.apache.commons.lang3.reflect.FieldUtils;
import win.doyto.query.annotation.GeneratedValue;
import win.doyto.query.annotation.Id;
import win.doyto.query.config.GlobalConfiguration;
import win.doyto.query.core.DataAccess;
import win.doyto.query.core.DoytoQuery;
import win.doyto.query.core.IdWrapper;
import win.doyto.query.core.PageList;
import win.doyto.query.entity.Persistable;
import win.doyto.query.jdbc.rowmapper.BeanPropertyRowMapper;
import win.doyto.query.jdbc.rowmapper.ColumnMapRowMapper;
import win.doyto.query.jdbc.rowmapper.RowMapper;
import win.doyto.query.jdbc.rowmapper.SingleColumnRowMapper;
import win.doyto.query.sql.EntityMetadata;
import win.doyto.query.sql.SqlAndArgs;
import win.doyto.query.sql.SqlBuilder;
import win.doyto.query.sql.SqlBuilderFactory;
import win.doyto.query.util.BeanUtil;
import win.doyto.query.util.ColumnUtil;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * JdbcDataAccess
 *
 * @author f0rb
 */
public final class JdbcDataAccess, I extends Serializable, Q extends DoytoQuery> implements DataAccess {

    private static final Map, RowMapper> classRowMapperMap;

    static {
        classRowMapperMap = new ConcurrentHashMap<>();
        classRowMapperMap.put(Map.class, new ColumnMapRowMapper());
    }

    private final DatabaseOperations databaseOperations;
    private final RowMapper rowMapper;
    private final SqlBuilder sqlBuilder;
    private final String[] columnsForSelect;
    private final boolean isGeneratedId;
    private final SingleColumnRowMapper idRowMapper;
    private final Class idClass;
    private final String idColumn;
    private final JdbcDataQueryClient jdbcDataQueryClient;
    private final EntityMetadata entityMetadata;

    public JdbcDataAccess(DatabaseOperations databaseOperations, Class entityClass) {
        this(databaseOperations, entityClass, new BeanPropertyRowMapper<>(entityClass));
    }

    public JdbcDataAccess(DatabaseOperations databaseOperations, Class entityClass, RowMapper rowMapper) {
        classRowMapperMap.put(entityClass, rowMapper);
        this.databaseOperations = databaseOperations;
        this.rowMapper = rowMapper;
        this.sqlBuilder = SqlBuilderFactory.create(entityClass);
        this.columnsForSelect = EntityMetadata.buildViewColumns(entityClass).split(", ");

        Field[] idFields = FieldUtils.getFieldsWithAnnotation(entityClass, Id.class);
        this.isGeneratedId = idFields.length == 1 && idFields[0].isAnnotationPresent(GeneratedValue.class);
        this.idColumn = idFields[0].getName();
        this.idClass = BeanUtil.getIdClass(entityClass, idColumn);
        this.idRowMapper = new SingleColumnRowMapper<>(idClass);
        this.jdbcDataQueryClient = new JdbcDataQueryClient(databaseOperations);
        this.entityMetadata = EntityMetadata.build(entityClass);
    }

    @Override
    public List query(Q query) {
        SqlAndArgs sqlAndArgs = sqlBuilder.buildSelectColumnsAndArgs(query, columnsForSelect);
        List mainEntities = databaseOperations.query(sqlAndArgs, rowMapper);
        jdbcDataQueryClient.querySubEntities(mainEntities, query, entityMetadata);
        return mainEntities;
    }

    @Override
    public PageList page(Q query) {
        query.forcePaging();
        return new PageList<>(query(query), count(query));
    }

    @Override
    public  List queryColumns(Q query, Class clazz, String... columns) {
        if (columns.length == 0) {
            columns = columnsForSelect;
        }
        boolean isSingle = ColumnUtil.isSingleColumn(columns);
        @SuppressWarnings("unchecked")
        RowMapper localRowMapper = (RowMapper) classRowMapperMap.computeIfAbsent(
                clazz, c -> isSingle ? new SingleColumnRowMapper<>(clazz) : new BeanPropertyRowMapper<>(clazz));
        return queryColumns(query, localRowMapper, columns);
    }

    private  List queryColumns(Q q, RowMapper rowMapper, String... columns) {
        SqlAndArgs sqlAndArgs = sqlBuilder.buildSelectColumnsAndArgs(q, columns);
        return databaseOperations.query(sqlAndArgs, rowMapper);
    }

    @Override
    public long count(Q q) {
        SqlAndArgs sqlAndArgs = sqlBuilder.buildCountAndArgs(q);
        return databaseOperations.count(sqlAndArgs);
    }

    @Override
    public int delete(Q q) {
        SqlAndArgs sqlAndArgs = sqlBuilder.buildDeleteAndArgs(q);
        return databaseOperations.update(sqlAndArgs);
    }

    @Override
    public E get(IdWrapper w) {
        SqlAndArgs sqlAndArgs = sqlBuilder.buildSelectById(w, columnsForSelect);
        List list = databaseOperations.query(sqlAndArgs, rowMapper);
        return list.isEmpty() ? null : list.get(0);
    }

    @Override
    public int delete(IdWrapper w) {
        SqlAndArgs sqlAndArgs = sqlBuilder.buildDeleteById(w);
        return databaseOperations.update(sqlAndArgs);
    }

    @Override
    public void create(E e) {
        SqlAndArgs sqlAndArgs = sqlBuilder.buildCreateAndArgs(e);

        if (isGeneratedId) {
            String keyColumn = GlobalConfiguration.dialect().resolveKeyColumn(idColumn);
            I id = databaseOperations.insert(sqlAndArgs, idClass, keyColumn).get(0);
            e.setId(id);
        } else {
            databaseOperations.update(sqlAndArgs);
        }
    }

    @Override
    public int batchInsert(Iterable entities, String... columns) {
        if (!entities.iterator().hasNext()) {
            return 0;
        }
        if (GlobalConfiguration.dialect().supportMultiGeneratedKeys()) {
            SqlAndArgs sqlAndArgs = sqlBuilder.buildCreateAndArgs(entities, columns);
            String keyColumn = GlobalConfiguration.dialect().resolveKeyColumn(idColumn);
            List ids = databaseOperations.insert(sqlAndArgs, idClass, keyColumn);
            int i = 0;
            for (E entity : entities) {
                entity.setId(ids.get(i++));
            }
            return ids.size();
        } else {
            SqlAndArgs sqlAndArgs = sqlBuilder.buildCreateAndArgs(entities, columns);
            return databaseOperations.update(sqlAndArgs);
        }
    }

    @Override
    public int update(E e) {
        SqlAndArgs sqlAndArgs = sqlBuilder.buildUpdateAndArgs(e);
        return databaseOperations.update(sqlAndArgs);
    }

    @Override
    public int patch(E e) {
        SqlAndArgs sqlAndArgs = sqlBuilder.buildPatchAndArgsWithId(e);
        return databaseOperations.update(sqlAndArgs);
    }

    @Override
    public int patch(E e, Q q) {
        SqlAndArgs sqlAndArgs = sqlBuilder.buildPatchAndArgs(e, q);
        return databaseOperations.update(sqlAndArgs);
    }

    @Override
    public List queryIds(Q q) {
        SqlAndArgs sqlAndArgs = sqlBuilder.buildSelectIdAndArgs(q);
        return databaseOperations.query(sqlAndArgs, idRowMapper);
    }

}