sql >> Databáze >  >> RDS >> Mysql

IllegalArgumentException:Typ nemůže být null

Spring Data JPA od 1.11.1 nepodporuje SP vracející sady výsledků. Nahlásil jsem odpovídající závadu s jarními daty.

Řešením je sestoupit na úroveň API a použít pouze JPA. Zde je obecná třída, kterou jsem napsal a která funguje s MS SQL SP.

import com.google.common.base.Strings;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.ParameterMode;
import javax.persistence.Query;
import javax.persistence.StoredProcedureQuery;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class StoredProcRepository {

  //region Injected beans (via a RequiredArgsConstructor)
  private final EntityManager em;
  //endregion 

  /**
   * Calls a stored procedure via JPA and retrieves a single implicit result set (in DBs that
   * support them e.g. MS SQL or MySQL). The call is not dependent on a DB dialect. Be
   * aware that large result sets should be paginated and not entirely read to memory. Recreates
   * StoredProcedureQuery instance and its parameters on each call.
   * To execute MS SQL SPs performing multiple queries, SET NOCOUNT ON.
   *
   * @param procedureName stored procedure name, optionally qualified per DB syntax
   * @param resultClass converts (maps) each result set row into instances of resultClass via JPA
   * @param spArgs stored procedure arguments, supplied positionally (optional SP arguments at the
   * end of the list could be omitted)
   * @param <T> class of row instances converted per JPA
   * @return the entire result set
   */
  public <T> List<T> queryViaStoredProc(String procedureName, Class<T> resultClass,
      Object... spArgs) {
    StoredProcedureQuery spq = em.createStoredProcedureQuery(procedureName, resultClass);
    int pos = 0;
    for (Object arg : spArgs) {
      spq.registerStoredProcedureParameter(++pos, arg.getClass(), ParameterMode.IN);
      spq.setParameter(pos, arg);
    }
    return spq.getResultList();
  }

  /**
   * Calls a stored procedure via JPA and retrieves only the top row of a single implicit result
   * set (in DBs that support them e.g. MS SQL or MySQL).
   * Assumes that result set has at least one row.
   * The call is not dependent on a DB dialect.
   * Be aware that large result sets should be paginated and not entirely read to memory.
   * Recreates StoredProcedureQuery instance and its parameters on each call.
   * To execute MS SQL SPs performing multiple queries, SET NOCOUNT ON.
   *
   * @param procedureName stored procedure name, optionally qualified per DB syntax
   * @param resultClass converts (maps) each result set row into instances of resultClass via JPA
   * @param spArgs stored procedure arguments, supplied positionally (optional SP arguments at the
   * end of the list could be omitted)
   * @param <T> class of row instances converted per JPA
   * @return the entire result set
   */
  public <T> T queryTopRowViaStoredProc(String procedureName, Class<T> resultClass,
      Object... spArgs) {
    return queryViaStoredProc(procedureName, resultClass, spArgs).get(0);
  }
}

Pro MS SQL SP je dalším požadavkem mít SET NOCOUNT ON pro všechny SP provádějící více než jeden dotaz. To lze nastavit jedním z nejméně tří způsobů:

  1. V obecném obalu Java pomocí JPA (viz kód níže). Tento přístup funguje pouze s ovladačem jTDS JDBC. Odpovídající problém byl podán s projektem ovladače MS JDBC.
  2. Na začátku každého SP.
  3. Globálně ve vaší databázi .

Její je kód pro #1:odpovídající metody pro stejné StoredProcRepository třída.

  /**
   * Calls an MS SQL stored procedure via JPA and retrieves a single implicit result set.
   * Protects against lack of SET NOCOUNT in stored procedures.
   * This works with jTDS JDBC driver, but not with MS JDBC driver.
   * Be aware that large result sets should be paginated and not entirely read to memory.
   *
   * @param procedureName stored procedure name, optionally qualified per DB syntax
   * @param resultClass converts (maps) each result set row into instances of resultClass via JPA
   * @param spArgs stored procedure arguments, supplied positionally (optional SP arguments at the
   * end of the list could be omitted)
   * @param <T> class of row instances converted per JPA
   * @return the entire result set
   */
  public <T> List<T> queryViaMsSqlStoredProc(String procedureName, Class<T> resultClass,
      Object... spArgs) {
    String spBindParams = (spArgs.length == 0) ? "" : "?" + Strings.repeat(",?", spArgs.length - 1);

    // The following works with jTDS driver, but not with MS driver
    String spQuery = String.format("EXEC %s %s", procedureName, spBindParams);

    // The following works with jTDS driver, but not with MS driver
    /*
    String spQuery = String.format("{call %s(%s)}", procedureName, spBindParams);
    Query q = em.createNativeQuery("SET NOCOUNT ON; " + spQuery, resultClass)
        .setHint("org.hibernate.readOnly", true);
    */

    Query q = em.createNativeQuery(spQuery, resultClass);
    int pos = 0;
    for (Object arg : spArgs) {
      q.setParameter(++pos, arg);
    }
    return q.getResultList();
  }    

  /**
   * Calls an MS SQL stored procedure via JPA and retrieves only the top row of a single implicit
   * result set.
   * Assumes that result set has at least one row.
   * The call sets the "NOCOUNT ON" MS SQL batch option.
   * Be aware that large result sets should be paginated and not entirely read to memory.
   *
   * @param procedureName stored procedure name, optionally qualified per DB syntax
   * @param resultClass converts (maps) each result set row into instances of resultClass via JPA
   * @param spArgs stored procedure arguments, supplied positionally (optional SP arguments at the
   * end of the list could be omitted)
   * @param <T> class of row instances converted per JPA
   * @return the entire result set
   */
  public <T> T queryTopRowViaMsSqlStoredProc(String procedureName, Class<T> resultClass,
      Object... spArgs) {
    return queryViaMsSqlStoredProc(procedureName, resultClass, spArgs).get(0);
  }


  1. SQL:Aktualizace řádku a vrácení hodnoty sloupce pomocí 1 dotazu

  2. Graf k aktualizaci z rozbalovací nabídky

  3. Jak napsat dotaz MySQL, který vrátí dočasný sloupec obsahující příznaky, zda položka související s tímto řádkem existuje v jiné tabulce

  4. Oracle ORA-00979 - není výraz GROUP BY