so I have a stored procedure with parameter, the parameter is no_skbdn which i can get from the $skbdn
$skbdn = DB::select('SELECT no_skbdn FROM selectSKBDN');
$nama = DB::select('EXEC selectNameInSO :no_skbdn', $skbdn);
return view('layouts.form_so', ['skbdn' => $skbdn, 'nama' => $nama]);
but when i ran this code, all i get is an error
Object of class stdClass could not be converted to string
Related
I have several stored procedures in an application that were functioning perfectly (for years) until our recent upgrade from ColdFusion 2010 to ColdFusion 2016. Now, I am getting error messages of either too many parameters or a certain parameter is not a parameter is not contained in the procedure that is being called.
I have opted to upload some code so people can better understand what is actually happening. Still learning how to format code here so please forgive me if it is still lacking.
In both cases I have double checked the parameter lists in the stored procedure in the procedure calls and have found that they are all indeed correct. In fact, nothing has changed in this code for over 5 years. This behavior has only begun since the upgrade has taken place.
Below is the first example. I will list the procedure call (in cfscript)
first then the parameter list from the stored procedure and then the error message it produced:
public query function readStorage(numeric group1=0,numeric group2=0) {
local.group1Value = arguments.group1?arguments.group1:"";
local.group2Value = arguments.group2?arguments.group2:"";
spService = new storedproc();
spService.setDatasource(variables.dsn);
spService.setUsername(variables.userName);
spService.setPassword(variables.password);
spService.setProcedure("usp_readCompatibilityStorage");
spService.addParam(dbvarname="#group1Id",cfsqltype="cf_sql_integer"
, type="in",value=local.group1Value,null=!arguments.group1);
spService.addParam(dbvarname="#group2Id",cfsqltype="cf_sql_integer"
,type="in",value=local.group2Value,null=!arguments.group2);
spService.addProcResult(name="rs1",resultset=1);
local.result = spService.execute();
return local.result.getProcResultSets().rs1;
}
Below is the parameter list from the stored procedure:
#groupId1 int = NULL
,#groupId2 int = NULL
Below is the error message I get:
[Macromedia][SQLServer JDBC Driver][SQLServer]#group1Id is not a
parameter for procedure usp_readCompatibilityStorage.
Second Example:
public query function read(string cribIdList="",
numeric cribNumber=0,
string isAnnex="",
numeric siteId=0,
string parentCribIdList="",
numeric supervisorId=0,
numeric statusId=0,
string orderBy="cribNumber ASC") {
local.cribNumberValue = arguments.cribNumber?arguments.cribNumber:"";
local.siteIdValue = arguments.siteId?arguments.siteId:"";
local.superIdValue = arguments.supervisorId ? arguments.supervisorId:"";
local.statusIdValue = arguments.statusId ? arguments.statusId:"";
spService = new storedproc();
spService.setDatasource(variables.dsn);
spService.setUsername(variables.userName);
spService.setPassword(variables.password);
spService.setProcedure("usp_readCrib");
spService.addParam(dbvarname="#cribIdList",cfsqltype="cf_sql_varchar"
,type="in",value=arguments.cribIdList
,null=!len(arguments.cribIdList));
spService.addParam(dbvarname="#cribNumber",cfsqltype="cf_sql_integer"
,type="in",value=local.cribNumberValue
,null=!arguments.cribNumber);
spService.addParam(dbvarname="#isAnnex",cfsqltype="cf_sql_varchar"
,type="in",value=arguments.isAnnex,null=!len(arguments.isAnnex));
spService.addParam(dbvarname="#siteId",cfsqltype="cf_sql_integer"
,type="in",value=local.siteIdValue,null=!arguments.siteId);
spService.addParam(dbvarname="#parentCribIdList"
, cfsqltype="cf_sql_varchar", type="in"
, value=arguments.parentCribIdList
, null=!len(arguments.parentCribIdList));
spService.addParam(dbvarname="#supervisorId",
cfsqltype="cf_sql_integer", type="in",value=local.superIdValue
, null=!arguments.supervisorId);
spService.addParam(dbvarname="#statusId"
, cfsqltype="cf_sql_integer", type="in"
, value=local.statusIdValue, null=!arguments.statusId);
spService.addParam(dbvarname="#orderBy",cfsqltype="cf_sql_varchar"
, type="in",value=arguments.orderBy);
spService.addProcResult(name="rs1",resultset=1);
local.result = spService.execute();
return local.result.getProcResultSets().rs1;
}
Below is the parameter list from the stored procedure:
#cribIdList varchar(500) = NULL
,#cribNumber int = NULL
,#isAnnex varchar(3) = NULL
,#siteId int = NULL
,#parentCribIdList varchar(500) = NULL
,#supervisorId int = NULL
,#statusId int = NULL
,#orderBy varchar(50)
Below is the message returned from the server:
[Macromedia][SQLServer JDBC Driver][SQLServer]Procedure or function
usp_readCrib has too many arguments specified.
In the case of both errors, they seem to be occurring at the following path:
Error Details - struct
COLUMN 0
ID CFSTOREDPROC
LINE 489
RAW_TRACE at cfbase2ecfc235349229$funcINVOKETAG.runFunction(E:\ColdFusion2016\cfusion\CustomTags\com\adobe\coldfusion\base.cfc:489)
TEMPLATE E: \ColdFusion2016\cfusion\CustomTags\com\adobe\coldfusion\base.cfc
TYPE CFML````
ColdFusion 10 and greater limit the amount of parameters in a request to 100 by default. Fortunately this can be updated and changed to reflect the required amount of parameters you need for your stored procedures.
In an instance of SQL Server 2016 I have a stored procedure with dozens of parameters. For example:
CREATE PROCEDURE spName (
#par1 INT = NULL,
#par2 VARCHAR(10) = NULL,
....
....
#par98 INT = NULL,
#par99 INT = NULL,
) AS
BEGIN
....
....
END
I have a client written in C# that calls the stored procedure specifying only the parameters with a value. Ex:
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "spName";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = dbConn;
cmd.Parameters.Add(new SqlParameter("par1", "val1"));
cmd.Parameters.Add(new SqlParameter("par47", "val47"));
...
cmd.ExecuteNonQuery();
It works perfectly! So, the procedure is executed and only the 2 parameters (par1 and par47) have a value. Other parameters maintain the default value (NULL).
I would do the same from a Java client using Microsoft JDBC driver 6.2.
I specify the parameters with List<Map<String, Object>>, so a list of couple parameterName-->parameterValue. The following method builds the PreparedStatement object:
private CallableStatement prepareStatement(String spName, Map<String, ?> parameters) throws SQLException {
setupConnection();
CallableStatement stmt = null;
try {
stmt = conn.prepareCall(getSpCallString(spName, parameters));
if (parameters != null) {
for (String parName : parameters.keySet())
stmt.setObject(parName, parameters.get(parName));
}
} catch (SQLException e) {
ApplicationLogging.severe("Cannot prepare callable statement", e);
throw e;
}
return stmt;
}
The method getSpCallString() generates a string of the type { call spName ?,?, ... , ? } with a number of ? as the number of parameters with a value passed to the procedure, so not all 99 parameters. If I have 2 parameter it generates the string { call spName ?,? }.
By passing for example par15=val15 and par47=val47 it raises the following exception:
com.microsoft.sqlserver.jdbc.SQLServerException: The index 2 is out of range.
I could resolve this putting in the call command the same number of ? as the number of parameter of the stored procedure but... I don't know the number of parameters for each stored procedure (and their position)!
In C# this is simply resolved because the parameters are assigned only with their name, so the number and the order of parameters can be really a black box.
Can I do this in some way in Java?
This is a confirmed deficiency in the current implementation of named parameter support for CallableStatement in the mssql-jdbc driver. Despite section 13.3.2 of the JDBC 4.2 specification stating ...
Named parameters can be used to specify only the values that have no default value.
... we seem to be required to provide a parameter placeholder for every possible parameter, and there doesn't appear to be a way to specify DEFAULT for the parameters we might otherwise simply omit.
As a workaround we could use code like this
public static ResultSet executeStoredProcedureQuery(
Connection conn, String spName, Map<String, Object> paramItems)
throws SQLException {
StringBuffer sqlBuf = new StringBuffer("EXEC ");
sqlBuf.append(spName);
int paramCount = 1;
for (String paramName : paramItems.keySet()) {
sqlBuf.append(
(paramCount++ > 1 ? ", " : " ") +
(paramName.startsWith("#") ? "" : "#") + paramName + "=?");
}
String sql = sqlBuf.toString();
myLogger.log(Level.INFO, sql);
// e.g., EXEC dbo.BreakfastSP #helpings=?, #person=?, #food=?
PreparedStatement ps = conn.prepareStatement(sql);
paramCount = 1;
for (String paramName : paramItems.keySet()) {
ps.setObject(paramCount++, paramItems.get(paramName));
}
return ps.executeQuery();
}
which we could call like this
// test data
Map<String, Object> paramItems = new HashMap<>();
paramItems.put("#person", "Gord");
paramItems.put("#food", "bacon");
paramItems.put("#helpings", 3);
//
ResultSet rs = executeStoredProcedureQuery(conn, "dbo.BreakfastSP", paramItems);
If using a third party library to facilitate calling such procedures is an option for you, then jOOQ certainly helps via its code generator for stored procedures, which generates stubs for each of your procedures, making such calls type safe. It includes support for:
Table valued functions
Table valued parameters
Defaulted parameters
In / Out parameters
Optional return value of procedures
Fetching undeclared update counts and result sets
Much more
In your case, you could write:
Spname sp = new Spname();
sp.setPar1("val1");
sp.setPar47("val47");
sp.execute(configuration); // The object containing your JDBC connection
sp.getResults(); // The result set(s) and update counts, if any
Behind the scenes, a JDBC CallableStatement is created, just like you would do manually:
try (CallableStatement s = c.prepareCall(
"{ ? = call [dbo].[spName] (#par1 = ?, #par47 = ?) }"
)) {
// Get the optional procedure return value that all procedures might return
s.registerOutParameter(1, Types.INTEGER);
s.setString(2, "val1");
s.setString(3, "val47");
s.execute();
// Lengthy procedure to fetch update counts and result set(s)
}
See this article if you want to generically fetch update counts and result set(s) with JDBC.
Disclaimer: I work for the company behind jOOQ.
I'm using Spring Data JPA 1.10.2 with com.microsoft.sqlserver's sqljdbc 4.2. I get the following error:
o.h.engine.jdbc.spi.SqlExceptionHelper : Error preparing CallableStatement [User.pTest]
com.microsoft.sqlserver.jdbc.SQLServerException: The index 4 is out of range.
My entity class is:
#Entity
#NamedStoredProcedureQuery(name = "User.getUser", procedureName = "User.pTest", parameters = {
#StoredProcedureParameter(mode = ParameterMode.OUT, name = "session", type = byte[].class),
#StoredProcedureParameter(mode = ParameterMode.IN, name = "name", type = String.class),
#StoredProcedureParameter(mode = ParameterMode.IN, name = "date", type = Date.class)
})
#Data //lombok
public class User {
// serves no purpose other than to meet
// JPA requirement
#Id
private Long id;
}
The repository code is
public interface UserRepository extends Repository<User, Long> {
#Procedure("User.pTest")
byte[] getUserSession(#Param("name") String name,
#Param("date") Date date
);
}
My test code is as follows and when I run it I get the error:
#Test
public void testGettingApxSession() {
Calendar cal = new GregorianCalendar(2016,6,5);
byte[] b = userRepository.getUserSession("myName", cal.getTime());
}
How do I resolve the error?
Update
Forgot to include the relevant part of the SQL Server stored proc:
ALTER procedure [User].[pTest]
#session varbinary(max) out
,#name nvarchar(max) = null
,#opt nvarchar(max) = null
,#date datetime
as
begin
set #session = CAST(N'<?xml version="1.0" encoding="UTF-16" ?><Session session = 45"'/>' as varbinary(max))
end
If you specify the nth parameter, Microsoft SQL Server driver requires that you also include the other parameters up to n. For example, let's say your procedure has five parameters (in this order) each having a default value of null:
param1 = null
param2 = null
param3 = null
param4 = null
param5 = null
You may only want to specify a value for param4. But if you do, you also must at least specify param1, param2, and param3 in the call to the stored procedure.
That may not seem too much of an issue. But if you have many default params and only need to specify say the 15th parameter, it will be quite tedious to also have to specify the other 14.
At least that's not fatal. But it is fatal in combination with the following Hibernate rule: Hibernate requires that each specified param is not null. But what if the procedure requires that only param3 or param4 have a value (but not both)? If you specify param4 to have a value, then you must include param3 (per MS driver) and you must give it a value (per Hibernate). So, you're sunk.
The only solutions are
Use a different driver such as jTDS (which has an issue on truncating >8000 bytes) OR
Write a wrapper procedure to just have only the needed params (e.g. param4) and let it call the procedure with five parameters.
Details of the exception in the OP
The Microsoft SQL Server driver has found (in the db metadata) four stored procedure parameters and returns the name and index of each. Then, the Hibernate / JPA code is building the CallableStatement.Via #NamedStoredProcedureonly three parameters have been defined. So, it builds something like pTest(?,?,?). But then Hibernate uses the metadata to say that Date should be in the fourth position which is out of range. The built pTest only has three parameters.
I have a problem with slick. It looks like after using it scala is not a statically typed language anymore:)
I used slick to call a SqlServer stored procedure that should return some values (and it does when I call the procedure directly in my SQL client). Unfortunately as you can see below the result is that I have a val with type Option[ProcessUserRoleDto], with ProcessUserRoleDto being just an ordinary case class, that in fact hold value of Some(1) and the '1' is of type java.lang.Integer. How is this possible? Do you have any ideas how to resolve this problem?
case class ProcessUserRoleDto(
processUserRoleId: Int,
processUserId: Int,
processRoleId: Int,
dataCollectionId: Int,
recognizingInstanceId: Int,
processRoleName: String,
dataCollectionName: String,
recognizingInstanceName: String,
isActive: Boolean)
def assign(dto: ProcessUserRoleAssignDto): Future[Option[ProcessUserRoleDto]] = {
val db = implicitly[SQLServerDriver.backend.DatabaseDef]
val sql = sql"exec EVO.spProcessUserRoleAssignToRecognizingInstance ${dto.ProcessUser_ID}, ${dto.ProcessRole_ID}, ${dto.RecognizingInstance_ID}"
implicit val registerProcessUserResult = GetResult { r =>
ProcessUserRoleDto(r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<)
}
val resultFut: Future[Option[ProcessUserRoleDto]] = db
.run(sql.as[ProcessUserRoleDto])
.map(_.headOption)
val singleResult: Option[ProcessUserRoleDto] = Await.result(resultFut, 10 seconds)
println(s"singleResult = $singleResult")
// in runtime result was: Some(1)
// WTF ?!?
println(s"class inside Some() = ${singleResult.get.getClass.getCanonicalName}")
// in runtime result is: java.lang.Integer
println(s"Option[Class] = ${singleResult.map(_.getClass.getCanonicalName)}")
// here exception is thrown: java.lang.ClassCastException: java.lang.Integer cannot be cast to ProcessUserRoleDto
}
The stored procedure was executing an insert statement and then a select statement. I turns out that it confused the JDBC driver because the insert was returning the number of modified records and that adding "SET NOCOUNT ON" to the stored procedure stoped that (https://stackoverflow.com/a/4809815/1185138).
Still, slick quietly messed up types instead of reaturning a nice error message about that..
I have this function auto-generated by EF that calls my stored procedure passing it a byte[]:
public virtual ObjectResult<string> IPv6Country_Get(byte[] ipBytes)
{
var ipBytesParameter = ipBytes != null ?
new ObjectParameter("ipBytes", ipBytes) :
new ObjectParameter("ipBytes", typeof(byte[]));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<string>("IPv6Country_Get", ipBytesParameter);
}
My stored procedure is:
CREATE Procedure [dbo].[IPv6Country_Get]
(
#ipBytes varbinary
)
AS
SELECT
[countrySHORT] AS Country
FROM
[IPv6Country]
WHERE
#ipBytes >= [ipFROM] AND
#ipBytes <= [ipTO]
RETURN 0
When the EF function is called the stored procedure gets only the first byte of the array in #ipBytes. What do I need to change so it receives the full byte[]?
Just add lenght of varbinary
CREATE Procedure [dbo].[IPv6Country_Get]
(
#ipBytes varbinary(max)
)