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

org.everit.persistence.querydsl.dtoquery.DTOQuery Maven / Gradle / Ivy

/*
 * Copyright © 2011 Everit Kft. (http://www.everit.biz)
 *
 * 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 org.everit.persistence.querydsl.dtoquery;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;

import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.SimpleExpression;
import com.querydsl.sql.Configuration;
import com.querydsl.sql.RelationalPathBase;
import com.querydsl.sql.SQLQuery;

/**
 * Queries a DTO class.
 *
 * @param 
 *          Type of the DTO.
 * @param 
 *          Type of the foreign key
 */
public class DTOQuery {

  /**
   * Helper method to create a DTOQuery using setters and selecting all fields from the table as is.
   *
   * @param 
   *          The type of the foreign key if the dtoQuery is called as a property selector of
   *          another DTO.
   * @param 
   *          The type of the DTO.
   * @param configuration
   *          The Querydsl configuration.
   * @param qTable
   *          The Querydsl Q class of the table.
   * @param dtoType
   *          The type of the DTO that should be instantiated.
   * @param foreignKeyPath
   *          The path to the foreign key in the generated SQL.
   * @return The {@link DTOQuery} instance that can be further configured.
   */
  public static  DTOQuery beanFullTable(Configuration configuration,
      RelationalPathBase qTable, Class dtoType, SimpleExpression foreignKeyPath) {

    return DTOQuery.create(
        (fks) -> new SQLQuery(configuration).select(Projections.bean(dtoType, qTable.all()))
            .from(qTable).where(foreignKeyPath.in(fks)));
  }

  /**
   * Helper method to be able to write simpler code. If the constructor was used, the type
   * parameters should be defined. The same as calling {@link DTOQuery#queryGenerator(Function)} on
   * a newly created {@link DTOQuery} instance.
   */
  public static  DTOQuery create(
      Function, SQLQuery> queryGenerator) {

    return new DTOQuery().queryGenerator(queryGenerator);
  }

  /**
   * Helper method to create a DTOQuery using public member variable access and selecting all fields
   * from the table as is.
   *
   * @param 
   *          The type of the foreign key if the dtoQuery is called as a property selector of
   *          another DTO.
   * @param 
   *          The type of the DTO.
   * @param configuration
   *          The Querydsl configuration.
   * @param qTable
   *          The Querydsl Q class of the table.
   * @param dtoType
   *          The type of the DTO that should be instantiated.
   * @param foreignKeyPath
   *          The path to the foreign key in the generated SQL.
   * @return The {@link DTOQuery} instance that can be further configured.
   */
  public static  DTOQuery dtoFullTable(Configuration configuration,
      RelationalPathBase qTable, Class dtoType, SimpleExpression foreignKeyPath) {

    return DTOQuery.create(
        (fks) -> new SQLQuery(configuration).select(Projections.fields(dtoType, qTable.all()))
            .from(qTable).where(foreignKeyPath.in(fks)));
  }

  /**
   * Helper method to create a {@link DTOQuery} for the root DTO type. In case of root DTO type one
   * does not need to handle incoming foreign keys.
   *
   * @param 
   *          The type of the DTOs that are queried from the database.
   * @param query
   *          The query that is used to select the DTOs.
   * @return A DTO query instance that can be further configured.
   */
  public static  DTOQuery root(SQLQuery query) {
    return new DTOQuery().queryGenerator((fks) -> query);
  }

  /**
   * Helper method to create a DTOQuery using setters and selecting all fields from the table as is.
   * This function can be used to select the root DTOs.
   *
   * @param 
   *          The type of the DTO.
   * @param configuration
   *          The Querydsl configuration.
   * @param qTable
   *          The Querydsl Q class of the table.
   * @param dtoType
   *          The type of the DTO that should be instantiated.
   * @return The {@link DTOQuery} instance that can be further configured.
   */
  public static  DTOQuery rootBeanFullTable(
      Configuration configuration, RelationalPathBase qTable,
      Class dtoType) {

    return DTOQuery
        .root(new SQLQuery<>(configuration).select(Projections.bean(dtoType, qTable.all()))
            .from(qTable));
  }

  /**
   * Helper method to create a DTOQuery using public member variable access and selecting all fields
   * from the table as is. This function can be used to select the root DTOs.
   *
   * @param 
   *          The type of the DTO.
   * @param configuration
   *          The Querydsl configuration.
   * @param qTable
   *          The Querydsl Q class of the table.
   * @param dtoType
   *          The type of the DTO that should be instantiated.
   * @return The {@link DTOQuery} instance that can be further configured.
   */
  public static  DTOQuery rootDTOFullTable(
      Configuration configuration, RelationalPathBase qTable,
      Class dtoType) {

    return DTOQuery
        .root(new SQLQuery<>(configuration).select(Projections.fields(dtoType, qTable.all()))
            .from(qTable));
  }

  private List> propertyQueries = new ArrayList<>();

  private Function, SQLQuery> queryGenerator;

  /**
   * Adds a property query to this DTO so the given property will be queried after all DTOs by this
   * {@link DTOQuery} is selected.
   *
   * @param 

* Type of the property. * @param * Type of the key that connects the property and the DTOs selected by this * {@link DTOQuery}. * @param propertyQuery * The query of the property that must be built by the programmer. See * {@link PropertyQuery}. * @return This {@link DTOQuery} instance. */ public DTOQuery prop(PropertyQuery propertyQuery) { this.propertyQueries.add(propertyQuery); return this; } /** * Runs the query on the database. * * @param connection * The database connection. * @return Collection of DTOs. */ public Collection queryDTO(Connection connection) { return this.queryDTO(connection, Collections.emptySet()); } /** * Queries the set of DTOs by filtering the specific foreign keys. * * @param connection * The database connection. * @param foreignKeys * The foreign keys to filter in the query. * @return The collection of DTOs that were pulled out from the database. */ Collection queryDTO(Connection connection, Collection foreignKeys) { SQLQuery query = this.queryGenerator.apply(foreignKeys); List resultSet = query.clone(connection).fetch(); queryDTOProperties(connection, resultSet); return resultSet; } private void queryDTOProperties(Connection connection, Collection resultSet) { for (PropertyQuery propertyQuery : this.propertyQueries) { queryDTOProperty(connection, resultSet, propertyQuery); } } private void queryDTOProperty(Connection connection, Collection dtos, PropertyQuery propertyQuery) { Map dtosByForeignKeys = new HashMap<>(); for (T result : dtos) { Function keyInSourceResolver = propertyQuery.keyInSourceDTOResolver; PFK keyInSource = keyInSourceResolver.apply(result); dtosByForeignKeys.put(keyInSource, result); } Collection

properties = propertyQuery.dtoQuery.queryDTO(connection, dtosByForeignKeys.keySet()); Map> propertyCollectionByForeignKeyMap = new HashMap<>(); Function keyInPropertyResolver = propertyQuery.keyInPropertyResolver; for (P prop : properties) { PFK propertyForeignKey = keyInPropertyResolver.apply(prop); propertyCollectionByForeignKeyMap.computeIfAbsent(propertyForeignKey, k -> new ArrayList<>()) .add(prop); } setPropertiesInDTOs(dtosByForeignKeys, propertyCollectionByForeignKeyMap, propertyQuery); } /** * Specifying the query generator for this DTO. * * @param queryGenerator * The generator of the SQLQuery of this {@link DTOQuery}. The generator is a function * where the parameter is the list of foreign keys coming from the source DTO and the * result is the generated SQLQuery. * @return This {@link DTOQuery} instance. */ public DTOQuery queryGenerator(Function, SQLQuery> queryGenerator) { this.queryGenerator = queryGenerator; return this; } private void setPropertiesInDTOs(Map dtosByForeignKeys, Map> propertyCollectionByForeignKeyMap, PropertyQuery propertyQuery) { for (Entry dtoEntry : dtosByForeignKeys.entrySet()) { PFK foreignKey = dtoEntry.getKey(); T dto = dtoEntry.getValue(); if (dto == null) { throw new NullPointerException("No DTO found for foreign key: " + foreignKey.toString()); } Collection

propertyCollection = propertyCollectionByForeignKeyMap.get(foreignKey); if (propertyCollection != null) { if (propertyQuery.setter != null) { // There is at least one element at this point otherwise the propertyCollection would be // null if (propertyCollection.size() > 1) { throw new IllegalArgumentException( "More than one property found for foreign key: " + foreignKey); } P prop = propertyCollection.iterator().next(); propertyQuery.setter.accept(dto, prop); } else if (propertyQuery.oneToManySetter != null) { propertyQuery.oneToManySetter.accept(dto, propertyCollection); } else { throw new IllegalArgumentException( "No setter defined for property query: " + propertyQuery.toString()); } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy