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

edu.internet2.middleware.grouper.hibernate.BySqlStatic Maven / Gradle / Ivy

There is a newer version: 5.13.5
Show newest version
/**
 * Copyright 2014 Internet2
 *
 * 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 edu.internet2.middleware.grouper.hibernate;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.hibernate.HibernateException;
import org.hibernate.internal.SessionImpl;
import org.hibernate.type.Type;

import edu.internet2.middleware.grouper.internal.dao.GrouperDAOException;
import edu.internet2.middleware.grouper.util.GrouperUtil;
import edu.internet2.middleware.grouper.util.PerformanceLogger;

/**
 * 
 * for simple HQL, use this instead of inverse of control.
 * this will do proper error handling and descriptive exception
 * handling.  This will by default use the transaction modes
 * GrouperTransactionType.READONLY_OR_USE_EXISTING, and 
 * GrouperTransactionType.READ_WRITE_OR_USE_EXISTING depending on
 * if a transaction is needed.
 * 
 * @author mchyzer
 *
 */
public class BySqlStatic {
  
  /** logger */
  @SuppressWarnings("unused")
  private static final Log LOG = GrouperUtil.getLog(BySqlStatic.class);

  
  /**
   * constructor
   *
   */
  BySqlStatic() {}

  /**
   * execute some sql
   * @param sql can be insert, update, delete, or ddl
   * @return the number of rows affected or 0 for ddl
   */
  public int executeSql(final String sql) {
    return executeSql(sql, null, null);
  }

  /**
   * execute some sql
   * @param sql can be insert, update, delete, or ddl
   * @param params prepared statement params
   * @return the number of rows affected or 0 for ddl
   * @deprecated doesnt work with postgres, need to pass in param types explicitly since cant determine them if null
   */
  @Deprecated
  public int executeSql(final String sql, final List params) {
    return executeSql(sql, params, convertParamsToTypes(params));
  }

  /**
   * execute some sql
   * @param sql can be insert, update, delete, or ddl
   * @param params prepared statement params
   * @param types is the types of the params
   * @return the number of rows affected or 0 for ddl
   */
  public int executeSql(final String sql, final List params, final List types) {
  
    int result = (Integer)HibernateSession.callbackHibernateSession(
        GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_NOT_AUDIT, new HibernateHandler() {

      public Object callback(HibernateHandlerBean hibernateHandlerBean)
          throws GrouperDAOException {
        
        //lets flush before the query
        HibernateSession hibernateSession = hibernateHandlerBean.getHibernateSession();
        hibernateSession.misc().flush();
        
        PreparedStatement preparedStatement = null;
        long startNanos = System.nanoTime();
        
        try {

          //we dont close this connection or anything since could be pooled
          Connection connection = ((SessionImpl)hibernateSession.getSession()).connection();
          preparedStatement = connection.prepareStatement(sql);
          preparedStatement.setFetchSize(1000);
      
          attachParams(preparedStatement, params, types);
          
          GrouperContext.incrementQueryCount();
          int result = preparedStatement.executeUpdate();
          
          return result;

        } catch (Exception e) {
          throw new RuntimeException("Problem with query in bysqlstatic: " + sql, e);
        } finally {
          GrouperUtil.closeQuietly(preparedStatement);
          PerformanceLogger.performanceTimingAllDuration(PerformanceLogger.PERFORMANCE_LOG_LABEL_SQL, System.nanoTime()-startNanos);

        }
      }
      
    });
    return result;
  
  }

  /**
   * select one object from sql (one row, one col
   * @param returnClassType type to be returned (currnetly supports string and int
   * @param  the type
   * @param sql can be insert, update, delete, or ddl
   * @return the number of rows affected or 0 for ddl
   */
  public  T select(final Class returnClassType, final String sql) {
    return select(returnClassType, sql, null, null);
  }

  /**
   * select one object from sql (one row, one col
   * @param returnClassType type to be returned (currnetly supports string and int
   * @param  the type
   * @param sql can be insert, update, delete, or ddl
   * @param params prepared statement params
   * @return the number of rows affected or 0 for ddl
   * @deprecated doesnt work with postgres, need to pass in param types explicitly since cant determine them if null
   */
  @Deprecated
  public  T select(final Class returnClassType, final String sql, final List params) {
    return select(returnClassType, sql, params, convertParamsToTypes(params));
  }

  /**
   * select one object from sql (one row, one col
   * @param returnClassType type to be returned (currnetly supports string and int
   * @param  the type
   * @param sql can be insert, update, delete, or ddl
   * @param params prepared statement params
   * @param types types of params
   * @return the number of rows affected or 0 for ddl
   */
  public  T select(final Class returnClassType, final String sql, final List params, final List types) {
  
    //TODO incorporate this with the listSelect
    T theResult = (T)HibernateSession.callbackHibernateSession(
        GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_NOT_AUDIT, new HibernateHandler() {

      public Object callback(HibernateHandlerBean hibernateHandlerBean)
          throws GrouperDAOException {
        HibernateSession hibernateSession = hibernateHandlerBean.getHibernateSession();
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        long startNanos = System.nanoTime();
        
        try {

          //we dont close this connection or anything since could be pooled
          Connection connection = ((SessionImpl)hibernateSession.getSession()).connection();
          preparedStatement = connection.prepareStatement(sql);
          preparedStatement.setFetchSize(1000);
      
          attachParams(preparedStatement, params);
          
          GrouperContext.incrementQueryCount();
          resultSet = preparedStatement.executeQuery();
          
          boolean hasResults = resultSet.next();
          
          if (!hasResults) {
            throw new RuntimeException("Expected 1 row but received none");
          }
          
          T result = null;
          boolean isInt = int.class.equals(returnClassType);
          boolean isLong = long.class.equals(returnClassType);
          boolean isPrimitive = isInt || isLong;
          if (isInt || Integer.class.equals(returnClassType)) {
            BigDecimal bigDecimal = resultSet.getBigDecimal(1);
            if (bigDecimal != null) {
              result = (T)(Object)bigDecimal.intValue();
            }
          } else if (isLong || Long.class.equals(returnClassType)) {
            BigDecimal bigDecimal = resultSet.getBigDecimal(1);
            if (bigDecimal != null) {
              result = (T)(Object)bigDecimal.longValue();
            }
          } else if (String.class.equals(returnClassType)) {
            result = (T)resultSet.getString(1);
          } else {
            throw new RuntimeException("Unexpected type: " + returnClassType);
          }
          
          if (result == null && isPrimitive) {
            throw new NullPointerException("expecting primitive (" + returnClassType.getSimpleName() 
                + "), but received null");
          }
          
          if (resultSet.next()) {
            throw new RuntimeException("Expected 1 row but received multiple");
          }
          
          return result;
  
        } catch (Exception e) {
          throw new RuntimeException("Problem with query in select: " + sql, e);
        } finally {
          GrouperUtil.closeQuietly(preparedStatement);
          PerformanceLogger.performanceTimingAllDuration(PerformanceLogger.PERFORMANCE_LOG_LABEL_SQL, System.nanoTime()-startNanos);

        }
      }
    });
    return theResult;
  
  }

  /**
   * select one object from sql (one row, one col
   * @param returnClassType type to be returned (currnetly supports string and int
   * @param  the type
   * @param query can be insert, update, delete, or ddl
   * @param params prepared statement params
   * @return the number of rows affected or 0 for ddl
   * @deprecated doesnt work with postgres, need to pass in param types explicitly since cant determine them if null
   */
  @SuppressWarnings("deprecation")
  public  List listSelectHiberateMapped(final Class returnClassType, final String query, final List params) {
    return listSelectHiberateMapped(returnClassType, query, params, convertParamsToTypes(params));
  }
  /**
   * select one object from sql (one row, one col
   * @param returnClassType type to be returned (currnetly supports string and int
   * @param  the type
   * @param query can be insert, update, delete, or ddl
   * @param params prepared statement params
   * @param types types of params
   * @return the number of rows affected or 0 for ddl
   */
  public  List listSelectHiberateMapped(final Class returnClassType, final String query, final List params, final List types) {
    //Get the alias and the types.  First see if already there
    final String alias = HibUtils.parseAlias(query, false);
    
    //if (StringUtils.isEmpty(alias)) {
      //if not there, massage the query
    //  query = HibUtils.massageQuery(query);
      //might be standard alias, might be a custom one
    //  alias = HibUtils.parseAlias(query);
    //}
    
    
    List theResult = (List)HibernateSession.callbackHibernateSession(
        GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_NOT_AUDIT, new HibernateHandler() {

      /**
       * 
       * @see edu.internet2.middleware.grouper.hibernate.HibernateHandler#callback(edu.internet2.middleware.grouper.hibernate.HibernateHandlerBean)
       */
      public Object callback(HibernateHandlerBean hibernateHandlerBean)
          throws GrouperDAOException {
        
        return hibernateHandlerBean.getHibernateSession()
            .retrieveListBySql(query, alias, returnClassType, params, types);
        
      }
    });
    return theResult;

  }
  
  /**
   * select one object from sql (one row, one col
   * @param returnClassType type to be returned (currnetly supports string and int
   * @param  the type
   * @param sql can be insert, update, delete, or ddl
   * @param params prepared statement params
   * @return the number of rows affected or 0 for ddl
   * @deprecated doesnt work with postgres, need to pass in param types explicitly since cant determine them if null
   */
  @SuppressWarnings("deprecation")
  @Deprecated
  public  List listSelect(final Class returnClassType, final String sql, final List params) {
    return listSelect(returnClassType, sql, params, convertParamsToTypes(params));
  }

  /**
   * select one object from sql (one row, one col
   * @param returnClassType type to be returned (currnetly supports string and int
   * @param  the type
   * @param sql can be insert, update, delete, or ddl
   * @param params prepared statement params
   * @param types
   * @return the number of rows affected or 0 for ddl
   */
  @SuppressWarnings("deprecation")
  public  List listSelect(final Class returnClassType, final String sql, final List params, final List types) {

    boolean isPrimitiveClass = HibUtils.handleAsPrimitive(returnClassType);

    if (!isPrimitiveClass) {
      return listSelectHiberateMapped(returnClassType, sql, params, types);
    }

    List theResult = (List)HibernateSession.callbackHibernateSession(
        GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_NOT_AUDIT, new HibernateHandler() {

      /**
       * 
       * @see edu.internet2.middleware.grouper.hibernate.HibernateHandler#callback(edu.internet2.middleware.grouper.hibernate.HibernateHandlerBean)
       */
      public Object callback(HibernateHandlerBean hibernateHandlerBean)
          throws GrouperDAOException {

        
        HibernateSession hibernateSession = hibernateHandlerBean.getHibernateSession();
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List resultList = new ArrayList();
        try {
          
          //we dont close this connection or anything since could be pooled
          Connection connection = ((SessionImpl)hibernateSession.getSession()).connection();
          preparedStatement = connection.prepareStatement(sql);
          preparedStatement.setFetchSize(1000);
      
          attachParams(preparedStatement, params, types);
          
          resultSet = preparedStatement.executeQuery();
          
          ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
          
          List columnTypes = new ArrayList();
          int columnCount = resultSet.getMetaData().getColumnCount();
          
          for (int i=0;i componentType = returnClassType.getComponentType();
              result = (T)Array.newInstance(componentType, columnCount);
              for (int i=0;i
       * @param returnClassType
       * @param resultSet
       * @return the answer
       * @throws SQLException
       */
      public  T extractPrimitiveFromResultset(final Class returnClassType,
          ResultSet resultSet, int positionOneIndexed) throws SQLException {
        
        T result = null;
        boolean isInt = int.class.equals(returnClassType);
        boolean isDouble = double.class.equals(returnClassType);
        boolean isPrimitive = isInt || isDouble;
        if (isInt || Integer.class.equals(returnClassType)) {
          BigDecimal bigDecimal = resultSet.getBigDecimal(positionOneIndexed);
          if (bigDecimal != null) {
            result = (T)(Object)bigDecimal.intValue();
          }
        } else if (isDouble || Double.class.equals(returnClassType)) {
          BigDecimal bigDecimal = resultSet.getBigDecimal(positionOneIndexed);
          if (bigDecimal != null) {
            result = (T)(Object)bigDecimal.doubleValue();
          }
        } else if (String.class.equals(returnClassType)) {
          result = (T)resultSet.getString(positionOneIndexed);
        } else if (Timestamp.class.equals(returnClassType)) {
          result = (T)resultSet.getTimestamp(positionOneIndexed);
        } else {
          throw new RuntimeException("Unexpected type: " + returnClassType);
        }
        if (result == null && isPrimitive) {
          throw new NullPointerException("expecting primitive (" + returnClassType.getSimpleName() 
              + "), but received null");
        }
        return result;
      }
    });
    return theResult;
  
  }

  /**
   * 
   * @param componentType
   * @param columnType
   * @return the class
   */
  private static Class massageComponentType(Class componentType, int columnType) {
    if (componentType != Object.class) {
      return componentType;
    }
    
    switch (columnType) {
      case Types.BIGINT: 
      case Types.INTEGER:
      case Types.SMALLINT:
      case Types.TINYINT:
        return Integer.class;
        
      case Types.NUMERIC:
      case Types.REAL:
      case Types.DECIMAL:
      case Types.DOUBLE:
      case Types.FLOAT:
        
        return Double.class;
        
      case Types.CHAR:
      case Types.VARCHAR:
      case Types.LONGVARCHAR:

        return String.class;

      case Types.DATE:
      case Types.TIMESTAMP:
        
        return Timestamp.class; 
        
      default:
        throw new RuntimeException("Type not supported: " + columnType);
    }    
  }
  
      
  /**
   * Attach params for a prepared statement.  The type of the params and types must be the
   * same (e.g. either both array or list, but not one is Array, and the other list
   * @param statement
   * @param params either null, Object, Object[], or List of Objects
   * @throws HibernateException
   * @throws SQLException
   * @deprecated doesnt work with postgres, pass in types explicitly
   */
  @SuppressWarnings("unchecked")
  @Deprecated
  public static void attachParams(PreparedStatement statement, Object params)
      throws HibernateException, SQLException {
    attachParams(statement, params, convertParamsToTypes(params));
  }

  /**
   * convert params to types
   * @param params
   * @return the types
   * @deprecated doesnt work with postgres.  pass in types explicitly
   */
  @Deprecated
  public static List convertParamsToTypes(Object params) {
    List typeList = null;
    if (GrouperUtil.length(params) > 0) {
      List paramList = GrouperUtil.toList(params);
      typeList = HibUtils.hibernateTypes(paramList);
    }
    return typeList;
  }
  
  /**
   * Attach params for a prepared statement.  The type of the params and types must be the
   * same (e.g. either both array or list, but not one is Array, and the other list
   * @param statement
   * @param params either null, Object, Object[], or List of Objects
   * @param types either null, Type, Type[], or List of Objects
   * @throws HibernateException
   * @throws SQLException
   */
  public static void attachParams(PreparedStatement statement, Object params, Object types)
      throws HibernateException, SQLException {
    int paramLength = GrouperUtil.length(params);
    int typeLength = GrouperUtil.length(types);
    
    //nothing to do if nothing to do
    if (paramLength == 0 && typeLength == 0) {
      return;
    }
  
    if (paramLength != typeLength) {
      throw new RuntimeException("The params length must equal the types length and params " +
      "and types must either both or neither be null");
    }
  
  
    List paramList = GrouperUtil.toList(params);
    List typeList = GrouperUtil.toList(types);
  
  
    //loop through, set the params
    Type currentType = null;
    for (int i = 0; i < paramLength; i++) {
      //not sure why the session implementer is null, if this ever fails for a type, 
      //might want to not use hibernate and brute force it
      currentType = (Type) typeList.get(i);
      currentType.nullSafeSet(statement, paramList.get(i), i + 1, (SessionImpl)HibernateSession._internal_hibernateSession().getSession());
    }
  
  }
  
  
  
}