将sql.oracle.ARRAY SimpleJdbcCall传递给StoredProcedure

时间:2014-08-25 18:08:51

标签: jdbctemplate

我使用JBoss 7.1.Final作为应用服务器,Oracle作为数据库使用。我们正在使用Spring框架3.x和Java 6.尝试传入一个字符串数组并将它们在存储过程中转换为varchars数组。我还没有找到一个很好的例子。如果您可以访问任何文档或以前的论坛帖子,请提供指针。我搜索过但没找到一个似乎适用的。

存储过程定义为:

CREATE OR REPLACE PROCEDURE GET_TEST_CONTENTS
(IN_RR_ARRAY IN RR_ARRAY,
  IN_ORDER_STATE           IN  VARCHAR2,
  OUT_FLAG      OUT VARCHAR2,
  OUT_RETURN_CODE          OUT VARCHAR2,
  OUT_RETURN_DESC          OUT VARCHAR2,
  OUT_RETURN_TYPE          OUT VARCHAR2,
  OUT_RETURN_VAL           OUT NUMBER
  )

类型RR_ARRAY定义为: 创建或替换 键入RR_ARRAY作为varchar2(15)的表;

在我的java代码中,我有:

jdbcTemplate = new JdbcTemplate(dataSource);

jdbcTemplate.setResultsMapCaseInsensitive(true);
this.getTestContents = new SimpleJdbcCall(jdbcTemplate)
        .withCatalogName("STAR")
        .withoutProcedureColumnMetaDataAccess()
        .withProcedureName("GET_TEST_CONTENTS")
        .declareParameters(
                new SqlParameter("IN_RR_ARRAY", OracleTypes.ARRAY,
                        "RR_ARRAY"),
                new SqlParameter("IN_ORDER_STATE", OracleTypes.VARCHAR),                        new SqlOutParameter("OUT_FLAG",
                OracleTypes.VARCHAR),
                new SqlOutParameter("OUT_RETURN_VAL", OracleTypes.INTEGER), 
                new SqlOutParameter("OUT_RETURN_CODE", OracleTypes.VARCHAR),
                new SqlOutParameter("OUT_RETURN_DESC", OracleTypes.VARCHAR),
                new SqlOutParameter("OUT_RETURN_TYPE", OracleTypes.VARCHAR));

         //I get a different error here so creating new connection for testing
        //conn = jdbcTemplate.getDataSource().getConnection();

        Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
        conn = DriverManager.getConnection(jdbcURL, user, passwd);
                ArrayDescriptor desc = new ArrayDescriptor("STAR.RR_ARRAY", conn);
        ARRAY arr = new ARRAY(desc, conn, testArray); // testArray is just 
                                                      // String[] with 2 values
        Map<String, Object> hm = new HashMap<String, Object>();
        hm.put("IN_RR_ARRAY", arr);
        hm.put("IN_ORDER_STATE", stateCode);
        hm.put("OUT_FLAG", Types.VARCHAR);
        hm.put("OUT_RETURN_CODE", Types.VARCHAR);
        hm.put("OUT_RETURN_DESC", Types.VARCHAR);
        hm.put("OUT_RETURN_TYPE", Types.VARCHAR);

        SqlParameterSource in = new MapSqlParameterSource().addValues(hm);
        Map out = getTestContents .execute(in);

返回的堆栈跟踪是:

11:24:43,691 ERROR [com.test.repository.TestContentsDao] (http-localhost-127.0.0.1-8080-1) Error while calling GET_TEST_CONTENTS Stored procedure: org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback; uncategorized SQLException for SQL [{call STAR.GET_TEST_CONTENTS(?, ?, ?, ?, ?, ?, ?)}]; SQL state [99999]; error code [17059]; Fail to convert to internal representation: oracle.sql.ARRAY@2a081f8f; nested exception is java.sql.SQLException: Fail to convert to internal representation: oracle.sql.ARRAY@2a081f8f
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:83) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:969) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:1003) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.core.simple.AbstractJdbcCall.executeCallInternal(AbstractJdbcCall.java:388) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:351) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:181) [spring-jdbc-3.0.7.RELEASE.jar:3.0.7.RELEASE]
at com.test.repository.TestContentsDao.isGood(TestContentsDao.java:147) [classes:]

任何有关示例或文档的建议或指示都将受到赞赏

3 个答案:

答案 0 :(得分:4)

我找到了解决方法。现在我使用这个字符串列表:

 List<String> ndcList;

我将数组参数从OracleTypes.ARRAY更改为java.sql.stypes.ARRAY并在数组名称上指定了模式前缀。并更改了代码并在底部创建了一些新的便捷方法。

我需要包装连接,并且必须将此依赖项添加到我的pom:

<dependency>
    <groupId>jboss</groupId>
    <artifactId>jboss-common-jdbc-wrapper</artifactId>
    <version>3.2.3</version>
</dependency>

----方法代码从这里开始------------

jdbcTemplate = new JdbcTemplate(dataSource);

jdbcTemplate.setResultsMapCaseInsensitive(true);
this.getTestContents = new SimpleJdbcCall(jdbcTemplate)
    .withCatalogName("STAR")
    .withoutProcedureColumnMetaDataAccess()
    .withProcedureName("GET_TEST_CONTENTS")
    .declareParameters(
            new SqlParameter("IN_RR_ARRAY", java.sql.types.ARRAY,
                    "STAR.RR_ARRAY"),
            new SqlParameter("IN_ORDER_STATE", OracleTypes.VARCHAR),                        
            new SqlOutParameter("OUT_FLAG",
            OracleTypes.VARCHAR),
            new SqlOutParameter("OUT_RETURN_VAL", OracleTypes.INTEGER), 
            new SqlOutParameter("OUT_RETURN_CODE", OracleTypes.VARCHAR),
            new SqlOutParameter("OUT_RETURN_DESC", OracleTypes.VARCHAR),
            new SqlOutParameter("OUT_RETURN_TYPE", OracleTypes.VARCHAR));

    Map<String, Object> hm = new HashMap<String, Object>();
    hm.put("IN_RR_ARRAY", new ScriptArray(ndcList));
    hm.put("IN_ORDER_STATE", stateCode);
    hm.put("OUT_FLAG", Types.VARCHAR);
    hm.put("OUT_RETURN_CODE", Types.VARCHAR);
    hm.put("OUT_RETURN_DESC", Types.VARCHAR);
    hm.put("OUT_RETURN_TYPE", Types.VARCHAR);

    SqlParameterSource in = new MapSqlParameterSource().addValues(hm);
    Map out = getTestContents .execute(in);
    ----  method code ends here------------

    public class ScriptArray extends AbstractSqlTypeValue {
    private List<String> values;

    public ScriptArray(List<String> values) {
        this.values = values;
    }

    public Object createTypeValue(Connection con, int sqlType,
            String typeName) throws SQLException {
        oracle.jdbc.OracleConnection wrappedConnection = con
                .unwrap(oracle.jdbc.OracleConnection.class);
        con = wrappedConnection;
        ArrayDescriptor desc = new ArrayDescriptor(typeName, con);
        return new ARRAY(desc, con,
                (String[]) values.toArray(new String[values.size()]));
    }
}

答案 1 :(得分:2)

与一天的类似问题作斗争。 This article帮助了我。 这是代码备份,以防页面不可用:

-- custom type
create or replace TYPE "MY_TYPE"
as object(name varchar(255),
value varchar(255))

-- array of MY_TYPE
create or replace
TYPE "MY_ARRAY"
as table of MY_TYPE

-- echo like SP, doesn't do too much
create or replace
procedure foo(
i_array in MY_ARRAY,
o_array out MY_ARRAY)
as
begin
o_array := MY_ARRAY();
for i in 1 .. i_array.count loop

o_array.extend;
o_array(i) := MY_TYPE(i_array(i).name, i_array(i).value);
end loop;
end;

Java代码:

public class FooStoredProcedure {

  private static final String SP_NAME = "FOO";
  private static final String MY_ARRAY = "MY_ARRAY";
  private static final String MY_TYPE = "MY_TYPE";
  private static final String I_ARRAY = "i_array";
  private static final String O_ARRAY = "o_array";


  private final StoredProcedure storedProcedure;

  public FooStoredProcedure(DataSource dataSource) {
    storedProcedure = new StoredProcedure(dataSource, SP_NAME) {
      {
        declareParameter(new SqlParameter(I_ARRAY, Types.ARRAY, MY_ARRAY));
        declareParameter(new SqlOutParameter(O_ARRAY, Types.ARRAY, MY_ARRAY, new SqlReturnType() {

          @Override
          public Object getTypeValue(CallableStatement cs, int paramIndex,
              int sqlType, String typeName) throws SQLException {
            Connection connection = cs.getConnection();
            Map<String, Class<?>> typeMap = connection.getTypeMap();
            typeMap.put(MY_TYPE, MyType.class);

            return cs.getObject(paramIndex);
          }
        }));

        compile();
      }
    };
  }

  /**
   * @return array of {@link MyType} objects or <code>null</code> 
   */
  public MyType[] execute(final MyType[] values) {

    Map<String, Object> params = new HashMap<String, Object>();
    params.put(I_ARRAY, new AbstractSqlTypeValue() {

      @Override
      protected Object createTypeValue(Connection con, int sqlType, String typeName) throws SQLException {
        ArrayDescriptor descriptor = new ArrayDescriptor(typeName, con);
        return new ARRAY(descriptor, con, values);
      }
    });

    Map<?, ?> result = storedProcedure.execute(params);
    if ((!result.containsKey(O_ARRAY) || result.get(O_ARRAY) == null)) {
      return null;
    }
    try {
      Object[] resultArray = (Object[]) ((ARRAY) result.get(O_ARRAY)).getArray();
      return Arrays.copyOf(resultArray, resultArray.length, MyType[].class);
    } catch (SQLException e) {
      throw new DataRetrievalFailureException("Unable to retrieve array", e);
    }
  }

  public static class MyType implements SQLData {
    private String name;
    private String value;

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    public String getValue() {
      return value;
    }

    public void setValue(String value) {
      this.value = value;
    }

    @Override
    public String getSQLTypeName() throws SQLException {
      return MY_TYPE;
    }

    @Override
    public void readSQL(SQLInput stream, String typeName) throws SQLException {
      name = stream.readString();
      value = stream.readString();
    }

    @Override
    public void writeSQL(SQLOutput stream) throws SQLException {
      stream.writeString(name);
      stream.writeString(value);
    }

    @Override
    public String toString() {
      return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
  }
}

答案 2 :(得分:1)

我看了互联网,很难将其与许多人提供的解决方案一起使用。..这是工作代码示例..在pom.xml中创建此依赖项。

<dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-oracle</artifactId>
            <version>2.0.0.M1</version>
        </dependency>

oracle示例代码

create table employee (EMPNO number(12) not null, FNAME varchar2(60), LNAME varchar2(60), EMAIL varchar2(120));

CREATE SEQUENCE empno_seq  START WITH     1 INCREMENT BY   1 NOCACHE NOCYCLE;

CREATE OR REPLACE TYPE employee_type
  AS OBJECT (EMPNO number(12), FNAME varchar2(60), LNAME varchar2(60), EMAIL varchar2(120));
/
CREATE OR REPLACE TYPE employee_table_type AS TABLE OF employee_type;
/

create or replace  PROCEDURE SAVE_EMPLOYEES(p_emp_insert_array in employee_table_type) AS
   BEGIN

   FORALL i IN p_emp_insert_array.first .. p_emp_insert_array.last
            insert into employee(
                    empno,
                    FNAME,
                    LNAME,
                    EMAIL)
                values (
                empno_seq.nextval,
                p_emp_insert_array(i).FNAME,
                p_emp_insert_array(i).LNAME,
                p_emp_insert_array(i).EMAIL
                );

  END SAVE_EMPLOYEES;
  /

import com.abc.employeepoc.domain.Employee;
import org.springframework.data.jdbc.support.oracle.StructMapper;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Struct;

/**
 *
 * @author rsharma
 */
public class EmployeeStructMapper implements StructMapper<Employee> {

    @Override
    public Struct toStruct(Employee emp, Connection conn, String oracleTypeName) throws SQLException {
        Object[] attributes = {
            emp.getEmpno(),
            emp.getFirstName(),
            emp.getLastName(),
            emp.getEmailAddress()
        };        

        return conn.createStruct(oracleTypeName, attributes);
    }

    @Override
    public Employee fromStruct(Struct struct) throws SQLException {
        Employee emp= new Employee();
        Object[] attributes = struct.getAttributes();
        emp.setEmpno(((Number) attributes[0]).longValue());
        emp.setFirstName(String.valueOf(attributes[1]));
        emp.setLastName(String.valueOf(attributes[2]));
        emp.setEmailAddress(String.valueOf(attributes[3]));        
        return emp;
    }



}

春季的SqlStructArrayValue在OracleConnection强制转换方面存在一些问题,因此我创建了自己的类似于它们的

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Struct;
import oracle.jdbc.OracleConnection;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.jdbc.support.oracle.SqlStructArrayValue;
import org.springframework.data.jdbc.support.oracle.StructMapper;

/**
 *
 * @author rsharma
 */
public class OracleSqlStructArrayValue<T> extends SqlStructArrayValue<T> {

    private T[] values;

    /**
     * The object that will do the mapping *
     */
    private StructMapper<T> mapper;

    /**
     * The type name of the STRUCT *
     */
    private String structTypeName;

    /**
     * The type name of the ARRAY *
     */
    private String arrayTypeName;

    public OracleSqlStructArrayValue(T[] values, StructMapper<T> mapper, String structTypeName) {
        super(values, mapper, structTypeName);
        this.values = values;
        this.mapper = mapper;
        this.structTypeName = structTypeName;
    }

    public OracleSqlStructArrayValue(T[] values, StructMapper<T> mapper, String structTypeName, String arrayTypeName) {
        super(values, mapper, structTypeName, arrayTypeName);
        this.values = values;
        this.mapper = mapper;
        this.structTypeName = structTypeName;
        this.arrayTypeName = arrayTypeName;
    }

    @Override
    protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
        if (typeName == null && arrayTypeName == null) {
            throw new InvalidDataAccessApiUsageException(
                    "The typeName for the array is null in this context. Consider setting the arrayTypeName.");
        }
        Struct[] structValues = new Struct[values.length];
        for (int i = 0; i < values.length; i++) {
            structValues[i] = mapper.toStruct(values[i], conn, structTypeName);
        }
        OracleConnection oracleConn = (OracleConnection) conn;
        return oracleConn.createOracleArray(typeName != null ? typeName : arrayTypeName, structValues);
    }

}

现在在您的DAO类中,执行以下操作...

public class EmployeeDAO {

     private static final Logger logger = LoggerFactory.getLogger(EmployeeDAO.class);

    @Autowired
    private DataSource dataSource;

    private JdbcTemplate jdbcTemplate;

    private SimpleJdbcCall saveEmployeesArrayCall;    


    @PostConstruct
    private void postConstruct() {
        jdbcTemplate = new JdbcTemplate(dataSource);        


        this.saveEmployeesArrayCall =
                new SimpleJdbcCall(dataSource).withProcedureName(SQLConstants.SAVE_EMPLOYEES_STORE_PROC)
                    .withoutProcedureColumnMetaDataAccess()
                    .declareParameters(new SqlParameter("p_emp_insert_array", Types.ARRAY, SQLConstants.EMPLOYEE_OBJ_TABLE_TYPE));

    }

 public void saveEmployees(List<Employee> employees) {         
                Map<String, Object> in = new HashMap<>();                
        in.put("p_emp_insert_array", new OracleSqlStructArrayValue<>(employees.toArray(new Employee[0]), new EmployeeStructMapper(), SQLConstants.EMPLOYEE_OBJ_TYPE));
        saveEmployeesArrayCall.execute(in);
    }

}


import io.swagger.annotations.ApiModelProperty;
import java.util.Objects;
import org.springframework.data.annotation.Id;

/**
 *
 * @author rsharma
 */
public class Employee implements java.io.Serializable{

    @Id
    @ApiModelProperty(notes = "The database generated Employee Number")
    private Long empno;

    @ApiModelProperty(notes = "First Name of the Employee", required = true)
    private String firstName;
    @ApiModelProperty(notes = "Last Name of the Employee")
    private String lastName;

    private String emailAddress;

    public Employee() {
        super();
    }

    public Employee(Long empno, String emailAddress, String firstName, String lastName) {
        this.empno = empno;
        this.emailAddress = emailAddress;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public Long getEmpno() {
        return empno;
    }

    public void setEmpno(Long empno) {
        this.empno = empno;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    @Override
    public String toString() {
        return "Employee{" + "empno=" + empno + ", firstName=" + firstName + ", lastName=" + lastName + ", emailAddress=" + emailAddress + '}';
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 59 * hash + Objects.hashCode(this.empno);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Employee other = (Employee) obj;
        if (!Objects.equals(this.empno, other.empno)) {
            return false;
        }
        return true;
    }

}