简体   繁体   中英

Register stored procedure out parameters using javax.persistence.EntityManager

I am using EntityManager for database operations. I want to execute stored procedure using this EntityManager. I am using below code to execute the procedure but don't know how to register for In/Out parameters .

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

Please help to solve this. Is theren't any way to achieve this problem?

Try this implementations:

Obtain a java.sql.Connection using your EntityManager :

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

With this Connection you can use the java.sql.CallableStatement class to make calls to stored procedures and functions, by this way:

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);
}

Another implementation is based on this post .
Create a class that extends 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);
    }
  }
}

Then, you can invoke your function or stored procedure:

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);

To instantiate GenericDatabaseCaller we add some lines to our 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>

Note that the data source is the same that we use to instantiate the EntityManager .

Then in our class we use the annotation @Respository and add the @Autowired annotation to the GenericDatabaseCaller field.

@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;
}

I hope this works for you.

Carefull with the retrieval of connection from the hibernate session. One might be getting a second connection outside of the current transaction.

Also it is better to use the Session inteface instead of its implementation.

@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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM