When to use $ vs #? - mybatis

I am confused about using $ vs #. I didn't found any guides for this. I used them as
name = #{name}, name like '%${word}%', order by name ${orderAs},where name = #{word}
Sometimes , these are work fine but at the sometimes , parameters aren't included or gave me error like
org.apache.ibatis.reflection.ReflectionException: There is no getter
for property named 'name'.......
So, I'd like to know when to use $ or # ?

Following the myBatis guidelines #{} is used in your sql statements.
If you take a look any of MyBatis Reference in the Section Mapper XML Files it says explicity:
Notice the parameter notation:
#{id}
Otherwise ${} is for
1- Configuration properties.
For example:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
Then the properties can be used like next:
<dataSource type="POOLED">
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
2- String Substitution ${} (Parameters section):
By default, using the #{} syntax will cause MyBatis to generate
PreparedStatement properties and set the values safely against the
PreparedStatement parameters (e.g. ?). While this is safer, faster and
almost always preferred, sometimes you just want to directly inject a
string unmodified into the SQL Statement. For example, for ORDER BY,
you might use something like this:
ORDER BY ${columnName}
Here MyBatis won't modify or escape the string.
NOTE It's not safe to accept input from a user and supply it to a
statement unmodified in this way. This leads to potential SQL
Injection attacks and therefore you should either disallow user input
in these fields, or always perform your own escapes and checks.
So definitively in name like '%${word}%' ororder by name ${orderAs}` you need to use String substitution not a prepared statement.

This (${} - simple variable)
SELECT * from user where usernum = ${usernum}
translates into this
SELECT * from user where usernum = 666
, but this (#{} - equivalent to PreparedStatement in JDBC)
SELECT * from user where usernum = #{usernum}
translates into
SELECT * from user where usernum = ?
, so the best usage would be something like
SELECT * from ${tablename} where name = #{name}

I was also confused with this . Then I did some research. I had a query which is something like select * from tablename h where h.id=#userid# in ibatis. Then I had to migrate it into mybatis 3 . The same statement didnt work. So I had changed it into select * from tablename h where h.id=#{userid}

Related

myBatis: using typehandlers in insert statements

Trying to use the typehandler for an insert statement in mybatis, but it is not working. I am using mybatis-spring 1.2.1, mybatis 3.2.3. But I am getting an error message saying that parameter 2 is not set.
Here is the code,
mybatis config file:
<configuration>
<typeAliases>
.......
.........
<typeAlias type="org.test.util.TSTypeHandler" alias="TSTypeHandler"/>
</typeAliases>
<typeHandlers>
.......
<typeHandler handler="TSTypeHandler" javaType="java.lang.String" jdbcType="TIMESTAMP"/>
</typeHandlers>
<mappers>
......
</mappers>
</configuration>
Mapper xml:
<insert id="saveMyOutput">
INSERT INTO TEST.MY_OUTPUT (
YEAR,
RUN_TMS,
PRODUCT
)
VALUES
<foreach item="element" index="index" collection="mOutput"
open="(" separator="),(" close=")">
#{element.year},
#{element.runTS, typeHandler=TSTypeHandler},
#{element.product}
</foreach>
</insert>
Type handlers declared in mybatis config file are intended to be applied globally, then beware of side effect, especially when common types such are java.lang.String are involved.
In this case, mybatis would apply the type handler to every string to convert it to SQL timestamp. And I suppose you want most of string params be passed as is.
First, add logs in method TSTypeHandler.setNonNullParameter to check whether it is actually called or not.
Then just remove type handler from global config and reference it only in statement.
Try without alias first: use full qualified name.

PostgreSQL jsonb, `?` and JDBC

I am using PostgreSQL 9.4 and the awesome JSONB field type. I am trying to query against a field in a document. The following works in the psql CLI
SELECT id FROM program WHERE document -> 'dept' ? 'CS'
When I try to run the same query via my Scala app, I'm getting the error below. I'm using Play framework and Anorm, so the query looks like this
SQL(s"SELECT id FROM program WHERE document -> 'dept' ? {dept}")
.on('dept -> "CS")
....
SQLException: : No value specified for parameter 5.
(SimpleParameterList.java:223)
(in my actual queries there are more parameters)
I can get around this by casting my parameter to type jsonb and using the #> operator to check containment.
SQL(s"SELECT id FROM program WHERE document -> 'dept' #> {dept}::jsonb")
.on('dept -> "CS")
....
I'm not too keen on the work around. I don't know if there are performance penalties for the cast, but it's extra typing, and non-obvious.
Is there anything else I can do?
As a workaround to avoid the ? operator, you could create a new operator doing exactly the same.
This is the code of the original operator:
CREATE OPERATOR ?(
PROCEDURE = jsonb_exists,
LEFTARG = jsonb,
RIGHTARG = text,
RESTRICT = contsel,
JOIN = contjoinsel);
SELECT '{"a":1, "b":2}'::jsonb ? 'b'; -- true
Use a different name, without any conflicts, like #-# and create a new one:
CREATE OPERATOR #-#(
PROCEDURE = jsonb_exists,
LEFTARG = jsonb,
RIGHTARG = text,
RESTRICT = contsel,
JOIN = contjoinsel);
SELECT '{"a":1, "b":2}'::jsonb #-# 'b'; -- true
Use this new operator in your code and it should work.
Check pgAdmin -> pg_catalog -> Operators for all the operators that use a ? in the name.
In JDBC (and standard SQL) the question mark is reserved as a parameter placeholder. Other uses are not allowed.
See Does the JDBC spec prevent '?' from being used as an operator (outside of quotes)? and the discussion on jdbc-spec-discuss.
The current PostgreSQL JDBC driver will transform all occurrences (outside text or comments) of a question mark to a PostgreSQL specific parameter placeholder. I am not sure if the PostgreSQL JDBC project has done anything (like introducing an escape as discussed in the links above) to address this yet. A quick look at the code and documentation suggests they didn't, but I didn't dig too deep.
Addendum: As shown in the answer by bobmarksie, current versions of the PostgreSQL JDBC driver now support escaping the question mark by doubling it (ie: use ?? instead of ?).
I had the same issue a couple of days ago and after some investigation I found this.
https://jdbc.postgresql.org/documentation/head/statement.html
In JDBC, the question mark (?) is the placeholder for the positional parameters of a PreparedStatement. There are, however, a number of PostgreSQL operators that contain a question mark. To keep such question marks in a SQL statement from being interpreted as positional parameters, use two question marks (??) as escape sequence. You can also use this escape sequence in a Statement, but that is not required. Specifically only in a Statement a single (?) can be used as an operator.
Using 2 question marks seemed to work well for me - I was using the following driver (illustrated using maven dependency) ...
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1201-jdbc41</version>
</dependency>
... and MyBatis for creating the SQL queries and it seemed to work well. Seemed easier / cleaner than creating an PostgreSQL operator.
SQL went from e.g.
select * from user_docs where userTags ?| array['sport','property']
... to ...
select * from user_docs where userTags ??| array['sport','property']
Hopefully this works with your scenario!
As bob said just use ?? instead of ?
SQL(s"SELECT id FROM program WHERE document -> 'dept' ?? {dept}")
.on('dept -> "CS")

Specifying a DB table dynamically? Is it possible?

I am writing a BSP and based on user-input I need to select data from different DB tables. These tables are in different packages. Is it possible to specify the table I want to use, based on its path, like this:
data: path1 type string value 'package1/DbTableName',
path2 type string value 'package2/OtherDbTableName',
table_to_use type string.
if some condition
table_to_use = path1.
elseif some condition
table_to_use = path2.
endif.
select *
from table_to_use
...
endselect
I am new to ABAP & Open SQL and am aware this could be an easy/silly question :) Any help at all would be very much appreciated!
You can define the name of the table to use in a variable, and then use the variable in the FROM close of your request :
data tableName type tabname.
if <some condition>.
tableName='PA0001'.
else.
tableName='PA0002'.
endif.
select * from (tableName) where ...
there are a few limitation to this method, as the stable can not contains fields of type RAWSTRING, STRING or SSTRING.
as for the fact that the table are in different package, i don't think it matters.
Regards,

iReport - parameterize schema name?

I have the following query :
SELECT
blah
FROM
"PUBLIC"."MYACTIVITY" MYACTIVITY
The problem is, the schema name is different for different environments. I tried making "PUBLIC" a parameter like this:
SELECT
blah
FROM
"$P{schemaName}"."MYACTIVITY" MYACTIVITY
while setting schemeName before I compile the report in the params, but no matter what I do, or what the default value is set as, I get the following error:
Error:SQL problems:invalid schema name: ? in statement [SELECT blah FROM "?"."MYACTIVITY" MYACTIVITY
How do I properly parameterize the schema name for this report?
Try using $P!{schemaName}. Note the exclamation mark. Use double quotes in the value:
SELECT
blah
FROM
$P!{schemaName}."MYACTIVITY" MYACTIVITY
And:
Let schemaName = "PUBLIC" (include the quotes).
The $P{} substitution provides an interpreted value, whereas $P!{} uses the literal value. You can use $P!{} to change the query itself.

Why the order of params of function imported from stored procedure changed?

I have a stored procedure in SQL Sever 2008 like:
ALTER PROCEDURE [dbo].[mySP]
#FirstName As varchar(30),
#LastName As varchar(30),
#ID As int
As
Begin
--......
End
then in EF imported this SP as a function like:
public ObjectResult<Nullable<global::System.Int32>> MyFunc(global::System.String LastName, global::System.String FirstName,Nullable<global::System.Int32> ID)
{
//....
}
it works fine for long time.
Then I add some new thing to EF edmx with "update from database" today, the function parameter changed! it became:
public ObjectResult<Nullable<global::System.Int32>> MyFunc(global::System.String LastName,Nullable<global::System.Int32> ID, global::System.String FirstName)
{
//....
}
It's hardto believe it. I already have many codes to call this func and it worked fine. Now all of them are not working. Even I can manually change the parameter, but maybe it back the orginal order with the generated-code!
How to resolve this problem.
This error occurs when using ALTER PROCEDURE on the stored Procedure, and does not appear in all cases.
We have been able to show that the parameter order is controlled by SQL and that after altering a stored proc parameter list (e.g. adding a parameter, especially one with a default value), then using 'Update Model From Database' on the Entity model, the parameter order becomes alphabetical. One possibility is that SQL has a mechanism for maintaining compatibility with compiled procs when an optional parameter is added, and this is manifesting as the observed behavior.
You need to fix it in the database. The only way we have been able to restore the correct parameter order is to DROP and CREATE the stored procedure, then update the model. No change to the storage model will survive the Update from database.
We are using SQL 2000.
As of EF4, default code generation is also based on a T4 template. By drilling into that T4 we can see how it generate codes for the function import:
foreach (EdmFunction edmFunction in container.FunctionImports)
{
IEnumerable<FunctionImportParameter> parameters =
FunctionImportParameter.Create(edmFunction.Parameters, code, ef);
string paramList = string.Join(", ", parameters.Select(p =>
p.FunctionParameterType + " " + p.FunctionParameterName).ToArray());
...
So it's exactly based on how your SSDL schema is look like under you model.
For example, for the uspUpdateEmployeePersonalInfo SP in Adventureworks database:
CREATE PROCEDURE [HumanResources].[uspUpdateEmployeeHireInfo]
#EmployeeID [int],
#Title [nvarchar](50),
#HireDate [datetime],
#RateChangeDate [datetime],
#Rate [money],
#PayFrequency [tinyint],
#CurrentFlag [dbo].[Flag]
The SSDL is like the below:
<Function Name="uspUpdateEmployeePersonalInfo" Aggregate="false"
BuiltIn="false" NiladicFunction="false" IsComposable="false"
ParameterTypeSemantics="AllowImplicitConversion" Schema="HumanResources">
<Parameter Name="EmployeeID" Type="int" Mode="In" />
<Parameter Name="NationalIDNumber" Type="nvarchar" Mode="In" />
<Parameter Name="BirthDate" Type="datetime" Mode="In" />
<Parameter Name="MaritalStatus" Type="nchar" Mode="In" />
<Parameter Name="Gender" Type="nchar" Mode="In" />
</Function>
And it will result in this code to be generated inside the ObjectContext:
public int UpdateEmployeePersonalInfo(Nullable<global::System.Int32> employeeID,
global::System.String nationalIDNumber,
Nullable<global::System.DateTime> birthDate,
global::System.String maritalStatus,
global::System.String gender)
My guess is that the order of the properties in your SSDL schema in your model has been changed and EF generates new codes to match that. So after validating that the parameters are declared in the desired order in you SP inside the database, try removing the SP from you model and update your model from database one more time and you'll see that the generated method code will be changed accordingly.
We are seeing this issue happen with lots of our stored procedure and we have not been able to identify why. Any new information on this will be a lot of help.
I have seen it happen on stored procedure with a lot of input and output parameters more consistently. Moreover on update form database command which when stored procedure is not changed and also edmx file is not changed but the designer causes the reorder of parameters.
Verify that the compatibility level of the database is not below 90
sp_dbcmptlevel 'your_database_name'
If it reports a value below 90 then run
sp_dbcmptlevel 'your_database_name', 90
Thanks to lajones in Codeplex