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

org.springframework.data.r2dbc.query.CustomUpdateMapper Maven / Gradle / Ivy

There is a newer version: 4.8.8.7
Show newest version
/*
 * Copyright 2019-2020 the original author or authors.
 *
 * 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
 *
 *      https://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 org.springframework.data.r2dbc.query;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
import org.springframework.data.r2dbc.mapping.SettableValue;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.sql.*;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.r2dbc.core.binding.BindMarker;
import org.springframework.r2dbc.core.binding.BindMarkers;
import org.springframework.r2dbc.core.binding.MutableBindings;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * A subclass of {@link CustomQueryMapper} that maps {@link Update} to update assignments.
 *
 * @author Mark Paluch
 */
public class CustomUpdateMapper extends CustomQueryMapper {

    /**
     * Creates a new {@link CustomQueryMapper} with the given {@link R2dbcConverter}.
     *
     * @param dialect   must not be {@literal null}.
     * @param converter must not be {@literal null}.
     */
    public CustomUpdateMapper(R2dbcDialect dialect, R2dbcConverter converter) {
        super(dialect, converter);
    }

    /**
     * Map a {@link Update} object to {@link BoundAssignments} and consider value/{@code NULL} {Bindings}.
     *
     * @param markers bind markers object, must not be {@literal null}.
     * @param update  update definition to map, must not be {@literal null}.
     * @param table   must not be {@literal null}.
     * @param entity  related {@link RelationalPersistentEntity}, can be {@literal null}.
     * @return the mapped {@link BoundAssignments}.
     */
    public BoundAssignments getMappedObject(BindMarkers markers, Update update, Table table,
                                            @Nullable RelationalPersistentEntity entity) {
        return getMappedObject(markers, update.getAssignments(), table, entity);
    }

    /**
     * Map a {@code assignments} object to {@link BoundAssignments} and consider value/{@code NULL} {Bindings}.
     *
     * @param markers     bind markers object, must not be {@literal null}.
     * @param assignments update/insert definition to map, must not be {@literal null}.
     * @param table       must not be {@literal null}.
     * @param entity      related {@link RelationalPersistentEntity}, can be {@literal null}.
     * @return the mapped {@link BoundAssignments}.
     */
    public BoundAssignments getMappedObject(BindMarkers markers, Map assignments, Table table,
                                            @Nullable RelationalPersistentEntity entity) {

        Assert.notNull(markers, "BindMarkers must not be null!");
        Assert.notNull(assignments, "Assignments must not be null!");
        Assert.notNull(table, "Table must not be null!");

        MutableBindings bindings = new MutableBindings(markers);
        List result = new ArrayList<>();

        assignments.forEach((column, value) -> {
            Assignment assignment = getAssignment(column.getReference(), value, bindings, table, entity);
            result.add(assignment);
        });

        return new BoundAssignments(bindings, result);
    }

    /**
     * Map a {@link Criteria} object into {@link Condition} and consider value/{@code NULL} {Bindings}.
     *
     * @param markers  bind markers object, must not be {@literal null}.
     * @param criteria criteria definition to map, must not be {@literal null}.
     * @param tables   must not be {@literal null}.
     * @return the mapped {@link BoundCondition}.
     */
    public BoundCondition getMappedObject(BindMarkers markers, Criteria criteria, Map tables) {

        Assert.notNull(markers, "BindMarkers must not be null!");
        Assert.notNull(criteria, "Criteria must not be null!");
        Assert.notNull(tables, "Table must not be null!");

        Criteria current = criteria;
        MutableBindings bindings = new MutableBindings(markers);

        // reverse unroll criteria chain
        Map forwardChain = new HashMap<>();

        while (current.hasPrevious()) {
            forwardChain.put(current.getPrevious(), current);
            current = current.getPrevious();
        }

        // perform the actual mapping

        Condition mapped = getCondition(current, bindings, tables, null);
        while (forwardChain.containsKey(current)) {

            Criteria nextCriteria = forwardChain.get(current);

            if (nextCriteria.getCombinator() == Criteria.Combinator.AND) {
                mapped = mapped.and(getCondition(nextCriteria, bindings, tables, null));
            }

            if (nextCriteria.getCombinator() == Criteria.Combinator.OR) {
                mapped = mapped.or(getCondition(nextCriteria, bindings, tables, null));
            }

            current = nextCriteria;
        }

        return new BoundCondition(bindings, mapped);
    }

    private Condition getCondition(Criteria criteria, MutableBindings bindings, Map tables,
                                   @Nullable RelationalPersistentEntity entity) {
        SqlIdentifier criteriaColumn = criteria.getColumn();
        Table table = tables.get(StringUtils.EMPTY);
        String columnName = criteriaColumn.getReference();

        if (criteriaColumn.getReference().indexOf('.') > -1) {
            table = tables.get(columnName.substring(0, columnName.indexOf('.')));
            columnName = columnName.substring(columnName.indexOf('.') + 1);
        }

        Field propertyField = createPropertyField(entity, columnName, getMappingContext());
        TypeInformation actualType = propertyField.getTypeHint().getRequiredActualType();

        Object mappedValue;
        Class typeHint;

        if (criteria.getValue() instanceof SettableValue) {
            SettableValue settableValue = (SettableValue) criteria.getValue();

            mappedValue = convertValue(settableValue.getValue(), propertyField.getTypeHint());
            typeHint = getTypeHint(mappedValue, actualType.getType(), settableValue);
        } else {
            mappedValue = convertValue(criteria.getValue(), propertyField.getTypeHint());
            typeHint = actualType.getType();
        }
        Column column = table.column(columnName);
        return createCondition(column, mappedValue, typeHint, bindings, criteria.getComparator());
    }

    private Assignment getAssignment(String columnName, Object value, MutableBindings bindings, Table table,
                                     @Nullable RelationalPersistentEntity entity) {

        Field propertyField = createPropertyField(entity, columnName, getMappingContext());
        Column column = table.column(toSql(propertyField.getMappedColumnName()));
        TypeInformation actualType = propertyField.getTypeHint().getRequiredActualType();

        Object mappedValue;
        Class typeHint;

        if (value instanceof SettableValue) {
            SettableValue settableValue = (SettableValue) value;

            mappedValue = convertValue(settableValue.getValue(), propertyField.getTypeHint());
            typeHint = getTypeHint(mappedValue, actualType.getType(), settableValue);

        } else {
            mappedValue = convertValue(value, propertyField.getTypeHint());

            if (mappedValue == null) {
                return Assignments.value(column, SQL.nullLiteral());
            }

            typeHint = actualType.getType();
        }

        return createAssignment(column, mappedValue, typeHint, bindings);
    }

    private Assignment createAssignment(Column column, @Nullable Object value, Class type, MutableBindings bindings) {

        BindMarker bindMarker = bindings.nextMarker(column.getName().getReference());
        AssignValue assignValue = Assignments.value(column, SQL.bindMarker(bindMarker.getPlaceholder()));

        if (value == null) {
            bindings.bindNull(bindMarker, type);
        } else {
            bindings.bind(bindMarker, value);
        }

        return assignValue;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy