Зарегистрируйте параметры хранимой процедуры, используя javax.persistence.EntityManager. - PullRequest
1 голос
/ 22 февраля 2012

Я использую EntityManager для операций с базой данных. Я хочу выполнить хранимую процедуру, используя этот EntityManager. Я использую приведенный ниже код для выполнения процедуры, но не знаю, как зарегистрироваться для параметров In / Out .

        Query query = appsEntityManager.createNativeQuery("{call test(?,?,?)}");
        query.setParameter(1, "");
        query.setParameter(2, "");
        query.setParameter(3, "");
        query.getResultList();

Пожалуйста, помогите решить эту проблему. Неужели нет способа решить эту проблему?

Ответы [ 2 ]

6 голосов
/ 19 июня 2012

Попробуйте следующие реализации:

Получите java.sql.Connection, используя EntityManager:

Connection cc = ((SessionImpl) em.getDelegate()).connection();

С этим Connection вы можете использовать класс java.sql.CallableStatement для выполнения вызововхранимые процедуры и функции следующим образом:

CallableStatement callableStatement;

try {  
  callableStatement = cc.prepareCall("{call stored_proc(?,?,?,?,?)}");

  callableStatement.setString(1, "1");//Parameter #1
  callableStatement.setString(2, "ET");////Parameter #2
  callableStatement.setString(3, "|s|");// //Parameter #3
  callableStatement.registerOutParameter(4, Types.INTEGER); //Output # 1
  callableStatement.registerOutParameter(5, Types.VARCHAR); //Output # 2
  callableStatement.execute();

  Integer outputValue = callableStatement.getInt(4);
  String outputValue1 = callableStatement.getString(5);
}

Другая реализация основана на этом post .
Создайте класс, который расширяет StoredProcedure:

/**
 * Class to provide access to the database. With this class you can invoke functions and stored procedures.
 */
public class GenericDatabaseCaller {

  /**Data source. */
  private DataSource dataSource;

  /**
   * This method requires LinkedHashMaps for inParams and outParams so that parameters can be set in a
   * sequence.
   * @param functionName Name of the stored procedure or function.
   * @param isFunction indicates if the process to execute is a Function or a Stored procedure.
   * @param inParams {@link LinkedHashMap} of IN parameters.
   * @param outParams {@link LinkedHashMap} of OUT Parameters.
   * @return {@link Map} with the output parameters.
   */
  public Map executeSimpleProcedure(String functionName, boolean isFunction, Map<String, Object> inParams,
      Map<String, Object> outParams) {
    InnerStoredProcedure innerStoredProcedure = new InnerStoredProcedure(dataSource, functionName, isFunction,
        inParams, outParams);
    return innerStoredProcedure.executeProcedure(inParams);
  }

  private class InnerStoredProcedure extends StoredProcedure {

    /**
     * @param ds
     * @param SQL
     * @param isFunction
     * @param inParams
     * @param outParams
     */
    public InnerStoredProcedure(DataSource ds, String SQL, boolean isFunction,  Map<String, Object> inParams, Map<String, Object> outParams) {
      setDataSource(ds);
      setFunction(isFunction);
      setSql(SQL);
      configerParameters(inParams, outParams);
      compile();
    }

    /**
     * Configure the input and output parameters for the stored procedure
     * @param inParams
     * @param outputParamers
     */
    public void configerParameters(Map<String, Object> inParams, Map<String, Object> outputParamers) {
      if (inParams != null && inParams.size() > 0) {
        Iterator<String> keySetIterator = inParams.keySet().iterator();
        while (keySetIterator.hasNext()) {
          String key = keySetIterator.next();
          if (inParams.get(key) instanceof String) {
            declareParameter(new SqlParameter(key, Types.VARCHAR));
          } else if (inParams.get(key) instanceof Integer) {
            declareParameter(new SqlParameter(key, Types.INTEGER));
          } else if (inParams.get(key) instanceof Date || inParams.get(key) instanceof java.sql.Date) {
            declareParameter(new SqlParameter(key, Types.DATE));
          }
          // TODO Add more types.
        }
      }

      if (outputParamers != null && outputParamers.size() > 0) {
        Iterator<String> keySetIterator = outputParamers.keySet().iterator();
        while (keySetIterator.hasNext()) {
          String key = keySetIterator.next();
          if (outputParamers.get(key) instanceof String) {
            declareParameter(new SqlOutParameter(key, Types.VARCHAR));
          } else if (outputParamers.get(key) instanceof Integer) {
            declareParameter(new SqlOutParameter(key, Types.INTEGER));
          }
        }
      }
    }

    public Map executeProcedure(Map inputs) {

      return execute(inputs);
    }
  }
}

Затем вы можете вызвать вашу функцию или хранимую процедуру:

String procedureName = "stored_proc";

Map<String, Object> inMap = new LinkedHashMap<String, Object>();
inMap.put("parameter1", "10");
inMap.put("parameter2", "|Lib");
inMap.put("parameter3", "P");   

Map<String, Object> outMap = new LinkedHashMap<String, Object>();
outMap.put("output", 0);
outMap.put("output1", "");

Map resultMap = genericDatabaseCaller.executeSimpleProcedure(procedureName, inMap, outMap);

Чтобы создать экземпляр GenericDatabaseCaller, мы добавим несколько строк к нашему application-context.xml

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>

<bean id="genericDatabaseCaller"
    class="co.com.custom.GenericDatabaseCaller">
    <property name="dataSource" ref="dataSource" />
</bean>

Примечание что источник данных тот же, который мы используем для создания экземпляра EntityManager.

Затем в нашем классе мы используем аннотацию @Respository и добавляем аннотацию @Autowired в поле GenericDatabaseCaller.

@Repository(value = "customDao")
public class JPACustomDao implements CustomDao {

  /** entity manager. */
  private EntityManager em = null;

  /**
   * Sets the entity manager.
   * 
   * @param entityManager {@link EntityManager}.
   */
  @PersistenceContext
  public void setEntityManager(EntityManager entityManager) {
    this.em = entityManager;
  }

  @Autowired
  private GenericDatabaseCaller genericStoredProcedure;
}

Надеюсь, это сработает для вас.

1 голос
/ 19 февраля 2016

Осторожно с извлечением соединения из сеанса гибернации. Можно получить второе соединение за пределами текущей транзакции.

Также лучше использовать интерфейс Session вместо его реализации.

@PersistenceContext
private EntityManager em;

@Transactional
@Override
public String create(final JpaPojo pojo) throws SQLException {
    ReturningWork<Integer> work = new ReturningWork<Integer>() {
        @Override
        public String execute(Connection con) throws SQLException {
            CallableStatement call = con.prepareCall("{?= call MyFunction(?,?,?)}");
            call.registerOutParameter(1, Types.INTEGER);
            call.setString(2, pojo.getFooId());
            (...)
            call.execute();
            return call.getString(1);
        }
    }
    Session session = (Session) em.getDelegate();
    return session.doReturningWork(work);
}
...