How can I use <bind> within a loop while using MyBatis? - 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.

Related

codefluent custom stored procedure

I have a custom stored procedure with in parameters that return fields of different tables how I can map this custom stored to an entity? I only want to use like a read only values for a report I don't want to save or something like that I try to add the extra fields to the most similar entity but when I execute the method in code the extra fields are null
Solution 1: Using a view
A view allows to aggregate data from different entities.
<Article>
<Id />
<Name />
<Lines typeName="LineCollection" />
<cf:method name="LoadArticlesByCommand" body="load(string commandName) from ArticleByCommand where CommandName = #commandName" />
<cf:view name="ArticleByCommand" autoLightweight="true">
<ArticleName expression="Name"/>
<ArticleQty expression="Lines.Quantity" />
<CommandName expression="Lines.Command.Name" />
</cf:view>
</Article>
<Command>
<Id />
<Name />
<Lines typeName="LineCollection" />
</Command>
<Line setType="List">
<Article typeName="Article" key="true" />
<Command typeName="Command" key="true" />
<Quantity typeName="int" />
</Line>
http://blog.codefluententities.com/2014/04/22/views-auto-lightweight-and-the-modeler/
https://www.softfluent.com/documentation/Views_PersistentViews.html
Solution 2: Using a lightweight entity
Instead of creating a view, you can can create a lightweight entity that contains only the properties used by the stored procedure.
<cf:entity name="Person" lightweight="true">
<cf:property name="FirstName" typeName="string" />
<cf:property name="lastName" typeName="string" />
<cf:method name="ComputeBalance"
body="load () raw"
rawBody="SELECT 'John' AS FirstName, 'Doe' AS LastName" />
</cf:entity>
Solution 3: Custom mapping
For more specific values or types, a custom method can be provided to map the database values to .NET types. This custom method will be called with a DataReader as parameter, meaning that a developer could do whatever he wants.
<cf:entity name="Sample">
<cf:method name="LoadPair" body="raw" rawBody="SELECT 1234,5678"
returnTypeName="CodeFluent.Runtime.Utilities.Pair<System.Int32,System.Int32>"
cfom:methodName="On{0}" />
<cf:snippet>
private static CodeFluent.Runtime.Utilities.Pair<int,int> OnLoadPair(System.Data.IDataReader reader)
{
return new Pair<int, int>(reader.GetInt32(0), reader.GetInt32(1));
}
</cf:snippet>
</cf:entity>
You can also use OnAfterReadRecord or OnBeforeReadRecord rules
If it is not essential that you map the results of the custom stored procedure to an entity than another option is to use the built in support for DataSets.
http://blog.codefluententities.com/2011/06/22/dataset-support-in-codefluent-entities/
<cf:method name="LoadAllCities" body="raw" returnTypeName="System.Data.DataSet">
SELECT $Address::City$ FROM $Address$
</cf:method>
.
DataSet ds = Address.LoadAllCities();
foreach (DataTable table in ds.Tables)
{
foreach (DataRow row in table.Rows)
{
Console.WriteLine("City: " + row[0]);
}
}
Upon re-reading you're question I am providing another answer.
In response to the part where you said "I try to add the extra fields to the most similar entity but when I execute the method in code the extra fields are null". The following steps should be able to solve that problem.
Execute one of the automatically created stored procedure in SQL Management Studio.
Execute the stored procedure you manually created.
Verify that the fieldnames returned by both stored procedures match.
I think the above will solve your immediate problem but I still don't like the solution. The reason is that you said you picked the most similar entity. I think that is going to cause problems in the future especially if the stored procedure is not being mapped to all of the entities properties.
I would recommend either lightweight entity, view or DataSet.

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}.

Typo3 6.2: Table Records in FCE (Flux)

I am trying to get a list of table-records in my FCE.
In documentation, i found the part "items" can use a Query.
But i can not find a way to make it work.
<flux:field.select name="myRecord" items="NOTHING WORKS HERE" label="Choose" maxItems="1" minItems="1" size="5" multiple="false" />
Does anybody know how the items can be filled with table-records ?
If you are trying to get the select box with all items maybe you can then switch to this:
<flux:field.relation size="1" minItems="0" table="tx_{YourExtensionName}_domain_model_{YourObjectName}" maxItems="1" name="package">
</flux:field.relation>
Of corse, you can use any table from the DB, like "pages"..
Hope it helps!

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

Why can't I pass a form field of type file to a CFFUNCTION using structure syntax?

I'm trying to pass a form field of type "file" to a CFFUNCTION. The argument type is "any". Here is the syntax I am trying to use (pseudocode):
<cfloop from="1" to="5" index="i">
<cfset fieldname = "attachment" & i />
<cfinvoke component="myComponent" method="attachFile">
<cfinvokeargument name="attachment" value="#FORM[fieldname]#" />
</cfinvoke>
</cfloop>
The loop is being done because there are five form fields named "attachment1", "attachment2", et al.
This throws an exception in the function:
coldfusion.tagext.io.FileTag$FormFileNotFoundException: The form field C:\ColdFusion8\...\neotmp25080.tmp did not contain a file.
However, this syntax DOES work:
<cfloop from="1" to="5" index="i">
<cfinvoke component="myComponent" method="attachFile">
<cfinvokeargument name="attachment" value="FORM.attachment#i#" />
</cfinvoke>
</cfloop>
I don't like writing code like that in the second example. It just seems like bad practice to me.
So, can anyone tell me how to use structure syntax to properly pass a file type form field to a CFFUNCTION??
In your first codesnippet the value #FORM[fieldname]# evaluates to the name of the file uploaded. So you are sending the filename to your function instead of the name of the field containing the filename.
If you want to stick with the structure notation you might use
<cfinvokeargument name="attachment" value="FORM['#fieldname#']" />
or
<cfinvokeargument name="attachment" value="FORM.#fieldname#" />
instead. I also don't think that there is anything wrong with your (working) second code example.
Edit:
It seems as if <cffile> can not evaluate the filefield if you pass the field using the struct notation, due to some auto evaluation magic of the parameter. After some further investigations I found out that passing only the name of the formfield without the form prefix would also work.
<cfinvokeargument name="attachment" value="#fieldname#" />
The filefield parameter is documented as string, containing the name of the formfield without prefix. My last approach seems more "right" to me. It would even hide the implementation a little bit more. I'm also not so much about composing scope/struct var/keys outside of a component or function and then passing it into the function. This should better be done in the function itself.