How to avoid getters when using mybatis dynamic sql? - mybatis

In mybatis we can write an update statement like this:
<update id="update" parameterType="com.test.User">
update USERS
<set>
<if test="user.name != null">NAME=#{user.name},</if>
<if test="user.age != null">AGE=#{user.age}</if>
</set>
</update>
Then it requires User class to have getters for name and age properties, without the update statement, the mapping can work without adding any getters, is there a way to avoid the getters for the update statement?

It should work if you use the latest version 3.5.1 (the change was made in 3.5.0).
If you cannot upgrade for some reason, it may still work if you 1) remove the #Param("user") from the method parameter and 2) reference the fields directly as follows.
<update id="update">
update USERS
<set>
<if test="name != null">NAME=#{name},</if>
<if test="age != null">AGE=#{age}</if>
</set>
</update>
The direct property reference works only when the bean (i.e. User) is the only parameter to the mapper method.

Related

How can I use <bind> within a loop while using MyBatis?

I wanted to build a sql like "(column like 'a%' or column like 'b%' or ...)" and a,b,etc. are elements in a collection.
So I came up with xml file like this
<foreach item="item" collection="items" open="(" separator=" or " close=")">
<bind name="pattern" value="item + '%'" />
column like #{pattern}
</foreach
But it didn't work out as I thought it would be.
The sql was like "(column like 'c%' or column like 'c%' or ...)" which c is the last element in the collection.
Now I use a CONCAT function to do the job.
column like CONCAT(#{pattern},'%')
Is there other good ways to do the job?
Thanks.
It is a known limitation i.e. <bind /> cannot be used inside <foreach /> and there is an open issue.
Using CONCAT is a good workaround for now.

SAPUI5 Smart Table - Smart Field make field mandatory with annotation

Is it possible to make a Property in SAPUI5 Smart Table mandatory so that a user can't leave a field empty while editing it?
I have found this document saying that it should be possible to set mandatory field control on Property in metadata.xml file with annotation like this:
<Property Name="NameLast" Type="Edm.String" Nullable="false" MaxLength="40" sap:label="Last name" sap:field-control="7" />
But with this setting I am getting following error in console:
2017-04-14 11:37:36.691429 MockServer: Resource not found for the segment '7'
2017-04-14 11:37:36.707985 The following problem occurred: HTTP request failed404,Not Found,{"error":{"code":404,"message":{"lang":"en","value":"Resource not found for the segment '7'"}}} -
EDIT:
Later I found out that sap:field-control should not contain number, but a path expression to another property which contains the number. However this also doesn't work:
<Property Name="NameLast" Type="Edm.String" Nullable="false" MaxLength="40" sap:label="Last name" sap:field-control="Name_fc" />
<Property Name="Name_fc" Type="Edm.Byte" />
Value of Name_fc property is '7'. I don't see any console error now, however I can still leave the input (NameLast) field empty without any validation and send it to OData service, which is not what I expect.
Here is a link to an example from sap where they use required fields. I have no idea though how they made it.
https://sapui5.netweaver.ondemand.com/sdk/explored.html#/sample/sap.ui.comp.sample.smartfield/code/SmartField.view.xml
For OData v2 the sap:field-control annotation on the Property can be
used to specify whether the field is mandatory.
<Property Name="Customer" ... sap:field-control="mandatory"/>
<Property Name="CompanyCode" ... sap:field-control="mandatory"/>
https://sapui5.hana.ondemand.com/#docs/api/symbols/sap.ui.comp.smartfield.SmartField.html
The mandatory property of the entity has to be nullable="false". That's it.
Have a look at the smart field example from your link:
<Property Name="Name" Type="Edm.String" Nullable="false"
MaxLength="30" sap:label="Name" sap:creatable="false"
sap:quickinfo="Property annotation DataFieldWithUrl"
sap:updatable="true" sap:sortable="false" />
Btw. thanks for sharing your smart table example!
From my understanding there are 2 options:
1) Define a specific property as Mandatory --> Nullable="false"
2) Link a property to another property in the entity, the "field-control".
This field control can contain numbers and "7" means mandatory.
The linking from option 2 can be done in the MPC_EXT class (redefine the define method).
The actual value in the "field-control" property is set in the get_entity / get_en
However I am also having an issue with the smarttable. The mandatory fields do not light up red when empty.
Do it in brute force way in the Object Page extension controller.
var oField = this.getView().byId(<FieldId>)
oField.getDataProperty().property.nullable = "false" or "true".
"false" and "true" must be a string.
To convert a boolean to string use <boolean>.toString();

How can I reuse an SQL fragment with parameters?

I'm intending to make a fragment for reusing with parameters.
<insert ...>
<selectKey keyProperty="id" resultType="_long" order="BEFORE">
<choose>
<when test="_databaseId == 'derby'">
VALUES NEXT VALUE FOR SOME_ID_SEQ
</when>
<otherwise>
SELECT SOME_ID_SEQ.NEXTVAL FROM DUAL
</otherwise>
</choose>
</selectKey>
INSERT INTO ...
</insert>
Can I make an SQL fragment using parameters?
<sql id="selectKeyFromSequence">
<selectKey keyProperty="id" resultType="_long" order="BEFORE">
<choose>
<when test="_databaseId == 'derby'">
VALUES NEXT VALUE FOR #{sequenceName}
</when>
<otherwise>
SELECT #{sequenceName}.NEXTVAL FROM DUAL
</otherwise>
</choose>
</selectKey>
</sql>
So that I can reuse them like this?
<insert ...>
<include refid="...selectKeyFromSequence"/> <!-- How can I pass a parameter? -->
INSERT INTO ...
</insert>
Is this possible?
As of version 3.3.0 you can do it like this:
<sql id="myinclude">
from ${myproperty}
</sql>
<include refid="myinclude">
<property name="myproperty" value="tablename"/>
</include>
See section SQL in http://www.mybatis.org/mybatis-3/sqlmap-xml.html
You cannot pass parameter to tags. There is a similar SO question, iBatis issue and a MyBatis issue.
Includes are in-lined when the xmls are parsed so the do not exist as
their own once the startup process finishes (from MyBatis issue).
However, you can use variables inside tags. You do not pass it as a parameter but you can give it as a parameter to the function that has the include tag. You need to use the same variable name in all functions, i.e. #{sequenceName}.

MyBatis iterate hashmap with foreach

I have a small problem with MyBatis. I'm trying to iterate over a Hashmap but it seems, that MyBatis is not able to do it. The name of the Hshmap is serviceMap. Here is the code of the MyBatis foreach:
<foreach item="item" index="key" collection="serviceMap" open="(" separator="or" close=")">
(upper(p.ENDPOINT) like upper(#{key})
and
upper(p.ENDPOINT_OPERATION) like upper(#item))
</foreach>
The exception is:
org.apache.ibatis.builder.BuilderException: Error evaluating expression 'serviceMap'. Return value ({vehicle=register}) was not iterable.
at org.apache.ibatis.builder.xml.dynamic.ExpressionEvaluator.evaluateIterable(ExpressionEvaluator.java:59)
at org.apache.ibatis.builder.xml.dynamic.ForEachSqlNode.apply(ForEachSqlNode.java:51)
at org.apache.ibatis.builder.xml.dynamic.MixedSqlNode.apply(MixedSqlNode.java:29)
at org.apache.ibatis.builder.xml.dynamic.IfSqlNode.apply(IfSqlNode.java:31)
at org.apache.ibatis.builder.xml.dynamic.MixedSqlNode.apply(MixedSqlNode.java:29)
Is MyBatis simply not able to do it or did I make an mistake?
It would be really nice, if anybody can help me.
Thanks a lot!
Stefan
A HashMap is not iterable. You need to iterate the entrySet.
<foreach item="item" index="key" collection="serviceMap.entrySet" open="(" separator="or" close=")">
(upper(p.ENDPOINT) like upper(#{key})
and
upper(p.ENDPOINT_OPERATION) like upper(#item))
<foreach item="item" index="key" collection="serviceMap.entrySet()" open="((" separator="),(" close="))">
(upper(p.ENDPOINT) like upper(#{item.key})
and
upper(p.ENDPOINT_OPERATION) like upper(#item.value))
</foreach>
Use the exact hashmap mechanism

Nant nested if condition

I am trying to do certain things when certain conditions are met, but it seems that somewhere my syntax is wrong. I need to have three if conditions to do one thing, but I'm not sure how to put in one of the if statements.
<if test="${build.MoniXXXtor == 'true'} or test="${build.XXX== 'true'}" or test="${build.yyy== 'true'}">
<property name="solutionFile" value="${svn.Lib.path}"/>
<property name="LocalPath" value="${Local.Lib.path}"/>
<call target="getLatest" if="${source.getLatest == 'true'}"/>
</if>
It seems the syntax for the OR condition above is wrong.
According to the documentation, you should use only one equals sign ( = instead of ==), e.g:
<if test="${build.configuration='release'}">
<echo>Build release configuration</echo>
</if>
Assuming that build.MoniXXXtor, build.XXX, and build.yyy are boolean properties the code is simply:
<if test="${build.MoniXXXtor or build.XXX or build.yyy}">
If they are string properties, they need to be converted to booleans:
<if test="${convert::to-boolean(build.MoniXXXtor) or
convert::to-boolean(build.XXX) or
convert::to-boolean(build.yyy)}">