I'm trying to call a stored procedure with JDBC to an Oracle database, but I have a couple of issues.
SQL package definition with stored procedure to call
create or replace PACKAGE PACK AS
TYPE type_rec IS RECORD (
module VARCHAR2(40),
value VARCHAR2(80),
message VARCHAR2(4000));
TYPE type_msg_array IS VARRAY(250) OF type_rec;
PROCEDURE FUNC (
provco IN VARCHAR2,
id IN VARCHAR2,
code OUT NUMBER,
msg OUT VARCHAR2,
msg_array IN OUT type_msg_array);
END PACK;
Main method that creates the request and calls the stored procedure
public Map<String, Object> executeFunc(final String db, final RequestPojo request) {
Map<String, Object> inParamMap = new LinkedHashMap<>();
Map<String, Object> funcOutput = null;
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(getJdbcTemplate(db).getJdbcTemplate().getDataSource());
MessagePojo message = new MessagePojo();
List<MessagePojo> messageList = new ArrayList<>();
messageList.add(message);
List<Object> objectList = new ArrayList<>(messageList);
try (final Connection connectionWrapper = jdbcCall.getJdbcTemplate().getDataSource().getConnection()){
buildFuncParameters(jdbcCall, inParamMap, request);
inParamMap.put("msg_array", new ScriptArray(objectList, connectionWrapper));
funcOutput = jdbcCall.execute(inParamMap);
} catch (SQLException sqlEx) {
throw new DataRetrievalFailureException(sqlEx.getMessage());
}
return funcOutput;
}
Helping method that builds the request
private void buildFuncParameters(SimpleJdbcCall jdbcCall, Map<String, Object> inParamMap, RequestPojo request) {
jdbcCall.withCatalogName("PACK");
jdbcCall.withProcedureName("FUNC");
jdbcCall.declareParameters(
new SqlParameter("provco", Types.VARCHAR),
new SqlParameter("id", Types.VARCHAR),
new SqlOutParameter("code", Types.NUMERIC),
new SqlOutParameter("msg", Types.VARCHAR),
new SqlInOutParameter("msg_array", java.sql.Types.ARRAY, "PACK".concat(".TYPE_MSG_ARRAY"))
);
jdbcCall.withoutProcedureColumnMetaDataAccess();
jdbcCall.setAccessCallParameterMetaData(false);
jdbcCall.withReturnValue();
inParamMap.put("provco", findCritSeqRequest.getProvco());
inParamMap.put("id", findCritSeqRequest.getId());
}
ScriptArray utility class
public class ScriptArray extends AbstractSqlTypeValue {
private List<Object> values;
private Connection oracleConnection;
public ScriptArray(List<Object> values, final Connection oracleConnection) {
this.values = values;
this.oracleConnection = oracleConnection;
}
#Override
protected Object createTypeValue(Connection con, int sqlType, String typeName)
throws SQLException {
return oracleConnection.unwrap(OracleConnection.class).createARRAY(typeName, values.toArray(new Object[values.size()]));
}
}
After the execution, I get the following error
ERROR org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback; uncategorized SQLException for SQL [{? = call PACK.FUNC(?, ?, ?, ?)}]; SQL state [99999]; error code [17074]; invalid name pattern: PACK.TYPE_MSG_ARRAY; nested exception is java.sql.SQLException: invalid name pattern: PACK.TYPE_MSG_ARRAY
Hope you can help me out on this one.
Related
This is the sql script that I have used to create the necessary classes :
CREATE CLASS ProductSummary;
CREATE PROPERTY ProductSummary.name STRING (NOTNULL, MANDATORY TRUE);
CREATE PROPERTY ProductSummary.modelNumber LONG (NOTNULL, MANDATORY TRUE);
ALTER CLASS ProductSummary STRICTMODE TRUE;
CREATE CLASS PricingSummary;
CREATE PROPERTY PricingSummary.price LONG (NOTNULL, MANDATORY TRUE);
CREATE PROPERTY PricingSummary.discount LONG (NOTNULL, MANDATORY TRUE);
ALTER CLASS PricingSummary STRICTMODE TRUE;
CREATE CLASS TotalSummary EXTENDS V;
CREATE PROPERTY TotalSummary.projectLink LINK Project (NOTNULL, MANDATORY TRUE);
CREATE PROPERTY TotalSummary.productSummaries EMBEDDEDLIST ProductSummary;
CREATE PROPERTY TotalSummary.pricingSummaries EMBEDDEDLIST PricingSummary;
ALTER CLASS TotalSummary STRICTMODE TRUE;
CREATE INDEX TotalSummary_projectLink_idx ON TotalSummary (projectLink) UNIQUE;
I am trying to insert some values into my TotalSummary class, where I also need to insert some values into the EmbeddedList for pricingSummaries and productSummaries.
public TotalSummary create(final TotalSummary totalSummary) {
final Long projectId = 1;
final StatementBuilder builder = new StatementBuilder();
final StringBuilder query = new StringBuilder();
final List<Map<?, ?>> productSummaries = totalSummary.getProductSummaries().stream()
.map(ProductSummary::toMap)
.collect(Collectors.toList());
final List<Map<?, ?>> pricingSummaries = totalSummary.getPricingSummaries().stream()
.map(PricingSummary::toMap)
.collect(Collectors.toList());
builder.addAttribute("projectLink = (SELECT FROM project WHERE id = ?)", projectId);
if ( ! productSummaries.isEmpty()) {
builder.addAttribute("productSummaries = ?", productSummaries);
}
if ( ! pricingSummaries.isEmpty()) {
builder.addAttribute("pricingSummaries = ?", pricingSummaries);
}
try {
insert(TotalSummary.class.getSimpleName(), builder.attributes(), statement -> {
builder.init(statement);
return statement;
});
} catch (final UncategorizedSQLException e) {
throw new ConstraintViolationException(totalSummary, ExceptionUtils.getRootCauseMessage(e), e);
}
return assertNotNull(findById(projectId));
}
This is the utility method that I am using to build the insert query :
protected String insert(final String vertex, final String fieldValuePairs, final PreparedStatementInitializer initializer) {
final String sql = "INSERT INTO " + vertex + " SET " + fieldValuePairs + " RETURN #rid";
return executeStatement(sql, initializer);
}
The toMap methods to convert the List<ProductSummary> to List<Map<?,?>>
**ProductSummary**
public Map<String, Object> toMap() {
final Map<String, Object> ret = new HashMap<>();
ret.put("name", name);
ret.put("model number", Long.valueOf(modelNumber));
return ret;
}
The toMap methods to convert the List<PricingSummary> to List<Map<?,?>>
**PricingSummary**
public Map<String, Object> toMap() {
final Map<String, Object> ret = new HashMap<>();
ret.put("price", Long.valueOf(price));
ret.put("discount", Long.valueOf(discount));
return ret;
}
I am getting the following exception when I execute the code
Constraint violation for: TotalSummary#58f79eeb[recordId=<null>,customProperties=[]]. Reason: OValidationException: The field 'TotalSummary.productSummaries' has been declared as EMBEDDEDLIST but an incompatible type is used.
Things that I have already tried :
I have tried to covert to list to json format before adding it to
the builder like so new Gson().toJson(pricingSummaries);
Converted the pricingSummaries and productSummaries toArray().
You defined the summaries as EMBEDDEDLIST ProductSummary but you are passing lists of maps as values.
Try to create real ProductSummary objects instead eg.
OElement ret = db.newEmbeddedElement("ProductSummary");
ret.setProperty("price", Long.valueOf(price));
ret.setProperty("discount", Long.valueOf(discount));
I am trying to run a SQL Server 2014 stored procedure from Java (Spring) code and get some xml results.
When I run this in a SQL client e.g. RazorSQL I get a bunch of xmls (which is expected because the there are multiple stored procedures within it, that returns those xml).
Here is the Exec call from my SQL client:
EXEC [dbo].[sp_GetType]
#TRAN_ID = 42
#QUAL_ID = 0
GetType does a RETURN 0 at the end (so basically if all steps succeed, it returns 0)
This opens multiple tabs in my client with the xmls.
And one example stored procedure within GetType has these lines:
SELECT TOP 1 ModifiedBy = CASE WHEN #IS_ID = 1 FROM TABLE23.dbo.TRX
TrxId WITH (NOLOCK) WHERE #DD_ID = #TRAN_ID
FOR XML AUTO, ELEMENTS
My goal is to capture all the xmls returned by GetType into a List of objects.
Here is my dao:
private final JdbcTemplate jdbcTemplate;
#Autowired
public TransactionDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Transactional(readOnly = true)
public List<Object> getTransaction(Integer tranId, Integer qualId) {
Object dt = new Object();
List<Object> resultList = (List<Object>) jdbcTemplate.execute(
new CallableStatementCreator() {
public CallableStatement createCallableStatement(Connection con) throws SQLException {
String storedProc = "{call sp_GetType(?,?)}";
CallableStatement cs = con.prepareCall(storedProc);
cs.setInt(1, tranId);
cs.setInt(2, qualId);
return cs;
}
}, new CallableStatementCallback() {
public Object doInCallableStatement(CallableStatement > cs) throws SQLException,
DataAccessException {
List<Object> results = new ArrayList<Object>();
cs.execute();
if(cs.getMoreResults())
{
ResultSet rs = cs.getResultSet();
while(rs.next()) //rs has only one record
{
InputStream in = null;
Clob clob = rs.getClob(1);
in = clob.getAsciiStream();
}
rs.close();
}
return results;
}
});
return resultList;
}
I'm using the jtds 1.3.1 driver (I tried connecting using mssql-jdbc but no luck).
Any help is much appreciated.
I'm trying to use ResultSetExtractor to send a SQL statement to the database, but the method is getting stuck at the line l.setId(resultSet.getInt("Id"));.
This is my error message:
"message": "PreparedStatementCallback; uncategorized SQLException for
SQL [SELECT Cities.Name FROM cities INNER JOIN Notes on Cities.Id = Notes.CitiesId WHERE Notes.CitiesId = ? AND Notes.id = ?]; SQL state [S0022]; error code [0]; Column 'Id' not
found.; nested exception is java.sql.SQLException: Column 'Id' not
found.",
The Cities table has 3 columns named Id, Name, and Code. I checked my SQL statement in Workbench and it is correct. What's wrong with my code?
public List<Cities> GetCity(String CityId, String NoteId) {
final String sql = "SELECT Cities.Name FROM cities INNER JOIN Notes on Cities.Id = Notes.CitiesId WHERE Notes.CitiesId = ? AND Notes.id = ?";
final List<Cities> cityList = jdbcTemplate.query(sql, new ResultSetExtractor<List<Cities>>() {
#Override
public List<Cities> extractData(ResultSet resultSet) throws SQLException, DataAccessException {
List<Cities> list = new ArrayList<Cities>();
while (resultSet.next()) {
Cities l = new Cities();
l.setId(resultSet.getInt("Id"));
l.setName(resultSet.getString("Name"));
l.setCode(resultSet.getString("Code"));
list.add(l);
}
System.out.println(list);
return list;
}
}, CityId, NoteId);
return cityList;
}
Here is my JPA code which fails to execute with error in topic header.
#Override
public void validateVelocityRules(long organizationId, long accountId, long userId, Map<Integer, VelocityRuleChecker> rulesById, BigDecimal depositAmount, int depositItemCount) {
List<VelocityScrutinyRuleRec> velocityScrutinyRuleTab = new ArrayList<>();
for (Integer ruleId : rulesById.keySet()) {
VelocityScrutinyRuleRec velocityScrutinyRuleRec = new VelocityScrutinyRuleRec();
velocityScrutinyRuleRec.setId(ruleId);
velocityScrutinyRuleRec.setIsFailed(null);
velocityScrutinyRuleTab.add(velocityScrutinyRuleRec);
}
PLSQLrecord record = new PLSQLrecord();
record.setTypeName("VELOCITY_SCRUTINY_RULES_REC");
record.setCompatibleType("VELOCITY_SCRUTINY_RULES_REC");
record.setJavaType(VelocityScrutinyRuleRec.class);
record.addField("ID", JDBCTypes.NUMERIC_TYPE, 20,0);
record.addField("IS_FAILED", JDBCTypes.CHAR_TYPE, 1);
PLSQLCollection collection = new PLSQLCollection();
collection.setTypeName("VELOCITY_SCRUTINY_RULES_TAB");
collection.setCompatibleType("VELOCITY_SCRUTINY_RULES_TAB");
collection.setJavaType(List.class);
collection.setNestedType(record);
PLSQLStoredProcedureCall call = new PLSQLStoredProcedureCall();
call.setProcedureName("VELOCITY_PKG.SP_VALIDATE_RULES");
call.addNamedArgument("p_org_id", JDBCTypes.INTEGER_TYPE);
.
.
.
. call.addNamedInOutputArgument("p_rule", collection,JDBCTypes.ARRAY_TYPE.getSqlCode());
DataReadQuery dataReadQuery = new DataReadQuery(call);
dataReadQuery.addArgument("p_org_id");
dataReadQuery.addArgument("p_rule", List.class);
Vector args = new Vector();
args.add(organizationId);
args.add(velocityScrutinyRuleTab);
ServerSession session =
((JpaEntityManager)entityManager.getDelegate()).getServerSession();
ObjectRelationalDataTypeDescriptor descriptor = new ObjectRelationalDataTypeDescriptor();
descriptor.setJavaClass(VelocityScrutinyRuleRec.class);
descriptor.setTableName("VELOCITY_SCRUTINY_RULES_REC");
descriptor.setStructureName("VELOCITY_SCRUTINY_RULES_REC");
descriptor.setPrimaryKeyFieldName("ID");
descriptor.addFieldOrdering("ID");
descriptor.addFieldOrdering("IS_FAILED");
descriptor.addDirectMapping("id", "ID");
descriptor.addDirectMapping("isFailed", "IS_FAILED");
session.addDescriptor(descriptor);
Object object = JpaHelper.getEntityManager(entityManager).getActiveSession().executeQuery(dataReadQuery, args);
This code call a stored procedure with definition that takes following OBJECT TYPE as IN and OUT argument.
I have a stored procedure defined in PostgresSql as below:
CREATE OR REPLACE FUNCTION update_points(accountId bigint, points numeric(19,5)) RETURNS void AS $$
...
...
$$ LANGUAGE plpgsql;
And I call the following method to call the procedure:
public void updatePoints(final Account account, final BigDecimal points) {
SimpleJdbcCall simpleCall = new SimpleJdbcCall(coreJdbcTemplate).withFunctionName("update_points");
SqlParameterSource inputs = new MapSqlParameterSource()
.addValue("accountId", account.getId())
.addValue("points", points);
simpleCall.execute(inputs);
}
When the method is called, I get following spring error:
org.springframework.dao.DataIntegrityViolationException: CallableStatementCallback; SQL [{? = call update_points()}]; No value specified for parameter 1.; nested exception is org.postgresql.util.PSQLException: No value specified for parameter 1.
at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:102)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:1137)
at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:1173)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.executeCallInternal(AbstractJdbcCall.java:388)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:348)
at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:190)
at net.exchangesolutions.veo.dao.AccountTransactionDaoImpl.updateRewardsConsumedAmount(AccountTransactionDaoImpl.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
I tried another way to call the procedure with CallableStatement in which case the function is not called at all.
Do you know what is the problem with the code or have you any suggestion about how to call the procedure from SpringJPA?
Thanks!
EDIT:
This is how I call with CallableStatement:
public void updatePoints(final Account account, final BigDecimal points) {
Connection connection;
try {
connection = coreJdbcTemplate.getDataSource().getConnection();
CallableStatement callableSt = connection.prepareCall("{call update_points(?, ?)}");
callableSt.setLong(1, account.getId());
callableSt.setBigDecimal(2, points);
callableSt.executeUpdate();
} catch (SQLException e) {
}
}
I resolved this problem by using following method. I could not figure out why the two approaches above did not work. Anyway, following solution is working now.
public void updatePoints(final Account account,
final BigDecimal points) {
coreJdbcTemplate.execute(new CallableStatementCreator() {
public CallableStatement createCallableStatement(Connection con)
throws SQLException {
CallableStatement cs = con
.prepareCall("{call update_points(?, ?)}");
cs.setLong(1, account.getId());
cs.setBigDecimal(2, points);
return cs;
}
}, new CallableStatementCallback() {
public Object doInCallableStatement(CallableStatement cs)
throws SQLException {
cs.execute();
return null;
}
});