iBatis unable to read property from Map when using isEqual - ibatis

I'm seeing a very bizarre issue with iBatis when trying to read a property from a Java map using isEqual, but not with other iBatis operators. For example it is able to read the map properties fine when using isNotNull and iterate. The xml:
<isNotNull property="filterCriteria.account">
AND
id
<isEqual property="filterCriteria.account.meetsCriteria" compareValue="false">
NOT
</isEqual>
IN
(SELECT DISTINCT id
FROM account
WHERE some other criteria....
)
</isNotNull>
The 2 java classes we're using here:
public class SearchProfile {
private Map<String, SearchProfileCriteria> filterCriteria;
public SAOSearchProfile() {
filterCriteria = new HashMap<>();
}
public Map<String, SAOSearchProfileCriteria> getFilterCriteria() {
return filterCriteria;
}
public void setFilterCriteria(Map<String, SAOSearchProfileCriteriaBase> filterCriteria) {
this.filterCriteria = filterCriteria;
}
}
Above is the container object that is passed to iBatis for the querying, and below is the criteria object that will be the value of the map. In this example it is keyed with the String "account"
public class SearchProfileCriteria {
boolean meetsCriteria;
public String getCriteriaAsString() {
return StringUtils.getStringValueFromBoolean(meetsCriteria);
}
public boolean isMeetsCriteria() {
return meetsCriteria;
}
public void setMeetsCriteria(boolean meetsCriteria) {
this.meetsCriteria = meetsCriteria;
}
public String getSQLString(){
return meetsCriteria ? "" : "NOT";
}
}
And the exception:
Cause: com.ibatis.common.beans.ProbeException: There is no READABLE property named 'account' in class 'java.util.Map'; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException:
The getSQLString() method was my half baked attempt at a work around, the String gets escaped in the query and throws a syntax error.
When I remove the <isEqual> block the query executes find, which indicates it is able to read the "account" key when checking the to see if it is null. As I mentioned above, we're also able to use the map keys in <iterate> tags without issue. It seems <isEqual> and <isNotEqual> are the only tags causing issues. Does anyone have experience with this or know what may be going on?

Beware: Using isNotNull, isEqual, iterate is iBatis, they don't exist anymore in Mybatis, so referencing to Mybatis indifferently is confusing.
Reference documentation.
For your issue, how does it behave if replacing Map with a class (property will be known at compile time)?
Or try using <isPropertyAvailable>.
The work around could work with right syntax: $ instead of #: $filterCriteria.account.SQLString$ instead of #filterCriteria.account.SQLString#, then the value is just concatenated instead of bound as parameter.

Related

Retrieve executionId inside CommandInterceptor

I am implementing my own Activiti command intereceptor like this :
public class ActivitiCommandInterceptor extends AbstractCommandInterceptor {
private RuntimeService runtimeService;
private CommandInterceptor delegate;
public ActivitiSpringTxCommandInterceptor(RuntimeService runtimeService, CommandInterceptor delegate) {
this.runtimeService = runtimeService;
this.delegate=delegate;
}
#Override
public <T> T execute(CommandConfig config, Command<T> command) {
String myVariable = runtimeService.getVariable(<missingExecutionId>, "myVariableName");
...
}
}
Inside the execute() method I need to retrieve a variable from the execution context related to this command.
To do that, I need to have the executionId, but I can't find a way to retrieve it.
How can I get my variable from this interceptor?
Thanks
You can create a nativeExecutionQuery
This allows us to use SQL to perform operations directly on DB.
For your case, just find all the execution IDs that contains your variables, and filter them according to your need.

Dapper Extension Get & Update returns errors

I tried to play with Dapper Extension & MS Access and succeeded up to certain extent. My code is listed below. All the functions works properly (Insert/Count/GetList/Delete) except Get & Update. I have summarised the code below. If anybody wants I can paste all the code here
My Product class
public class Products
{
public string ProductNumber { get; set; }
public string Description { get; set; }
}
And in my main class. I tried to get the product and update it as below. con.Get<Products> function returns an exception with "Sequence contains more than one element" message and con.Update<Products> returns an exception with "At least one Key column must be defined".
using (var con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb"))
{
string ProductNumber = "12";
var product4 = con.Get<Products>(ProductNumber);
product4.ProductNumber = "Baz";
con.Update<Products>(product4);
Console.ReadLine();
}
Even though con.Get<Products> fails con.GetList<Products>(predicate) works perfectly. I did follow this link for setup
If DapperExtensions can't infer a key property called ID from your class, you'll need to explicitly specify one via a class mapper. Assuming the ProductNumber is the primary key in the database, the following example class mapper should set the ProductNumber to be the primary key for DapperExtensions.
using Dapper;
using DapperExtensions;
using DapperExtensions.Mapper;
public class ProductsMapper : ClassMapper<Products>
{
public ProductsMapper()
{
Map(x => x.ProductNumber).Key(KeyType.Assigned);
AutoMap();
}
}
This class can sit somewhere within the same assembly as the rest of your code. Dapper Extensions will automatically pick it up. If you have your classes and Dapper code in separate assemblies, you can point it to your mapper with the following line:
DapperExtensions.DapperExtensions.SetMappingAssemblies({ typeof(ProductsMapper).Assembly })

Spring data mongo ConditionalGenericConverter empty TypeDescriptor

I'm trying to implement a somewhat general converter, which transforms the data based on a given annotation. Say I want to transform these annotated strings in any matter.
All is well, until the code hits my converter's "matches" method. The "sourceType" I'm getting is always stripped out of all of the useful information. Has anyone had any success with such a setup, or am I missing something?
public class TestStringWriteConverter implements ConditionalGenericConverter {
#Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
if (sourceType.hasAnnotation(GivenAnnotation.class)) {
//->never gets executed, because sourceType is being stripped out of it's useful infos
}
I followed the problem to MappingMongoConverter from this package org.springframework.data.mongodb.core.convert
protected void writeInternal(Object obj, final DBObject dbo, MongoPersistentEntity<?> entity) {
//...
if (null != propertyObj) {
if (!conversions.isSimpleType(propertyObj.getClass())) {
writePropertyInternal(propertyObj, dbo, prop);
} else {
// I always end up here, which is correct but the whole prop object is being omitted in favor of the getFieldName() property
writeSimpleInternal(propertyObj, dbo, prop.getFieldName());
}
}
}
The spring versions I'm using:
<spring.version>3.2.5.RELEASE</spring.version>
<spring.data.version>1.3.2.RELEASE</spring.data.version>
Any help is much appreciated.
I think you misunderstand what sourceType.hasAnnotation(…) actually returns. As the name suggests, it inspects the type for annotations. So for a given type like this:
#MyAnnotation
class Foo { }
it would allow you to find #MyAnnotation. However you are writing about "annotated strings". I assume you mean something like:
class Bar {
#MyAnnotation
String property;
}
This is not a type annotation and the Converter API is not meant to cover such cases. If you think supporting such scenarios would be worthfile, please file a JIRA ticket.

Getting Dapper to return an empty string instead of a null string

I know it's kind of the wrong thing to do, but I'm dealing with a legacy codebase that has NULLS when it means empty strings and vice versa.
I can't immediately see how it is possible, but is it possible to get (or modifiy dapper so it will) return an empty string instead of a null string when mapping back from the database.
Dapper doesn't call any setter when it sees a null, so options might include:
set the default value to "" in the constructor
check for null in the accessor
So:
public class SomeDto
{
public SomeDto()
{
Name = "";
}
public string Name {get;set;}
}
or:
public class SomeDto
{
private string name;
public string Name { get {return name ?? "";} set {name = value;} }
}
However, this only applies to reading values; I can't think of a nice way to get dapper to turn "" into null when passing the dto in as the parameter object; options include:
creating an anon-type, substituting "" to null (perhaps write a string NullIfBlank(this string s) extension method)
having a shim property on the type that returns null in place of "", and have your database query bind to #NameOrNull rather than #Name
You can control this with your queries, for example:
public class Data
{
public string Foo { get; set; }
}
var result = conn.Query<Data>("select Foo = coalesce(Foo, '') from MyTable");
So in the above example, coalesce will return an empty string when Foo is null.
In short: depending how you load the data to the dapper you may get two different scenarios.
First: Turn up your data provider layer, for example like in this post - How to return null from a Dapper query rather than default(T)?.
Second way to try: you may modify your GetTypeDeserializer like in the following post - Change Dapper so that it maps a database null value to double.NaN
Third and the last: it is my friendly advice to work on your previous questions acceptance rate. In this way you may increase chances of replies for your questions.
Hope all this will help.
I tend to use a global extension method on string called ConvertNull() which converts any null values to an empty string. You can then call this anywhere without your code looking cluttered. If you're using this directly on an aspx page, just make sure you've imported the namespace of the extension methods and then the method will be available to you:
namespace ExtensionMethods
{
using System;
public static class StringExtensionsClass
{
/// <summary>Converts null strings to empty strings</summary>
/// <param name="s">Input string</param>
/// <returns>Original string, or empty string if original string was null</returns>
public static string ConvertNull(this string s)
{
return s ?? "";
}
}
}
Then call this on an instance of a string.
Usage:
myStringInstance.ConvertNull().Replace("\r\n", "<br />");

Parameter must be an entity type exposed by the DomainService?

Trying to implement a domain service in a SL app and getting the following error:
Parameter 'spFolderCreate' of domain method 'CreateSharePointFolder' must be an entity type exposed by the DomainService.
[EnableClientAccess()]
public class FileUploadService : DomainService
{
public void CreateSharePointFolder(SharePointFolderCreate spFolderCreate)
{
SharePointFolder spf = new SharePointFolder();
spf.CreateFolder_ClientOM(spFolderCreate.listName, spFolderCreate.fileName);
}
[OperationContract]
void CreateSharePointFolder(SharePointFolderCreate spFolderCreate);
[DataContract]
public class SharePointFolderCreate
{
private string m_listName;
private string m_fileName;
[DataMember]
public string listName
{
get { return m_listName; }
set { m_listName = value; }
}
[DataMember]
public string fileName
{
get { return m_fileName; }
set { m_fileName = value; }
}
}
So am I missing something simple here to make this all work?
It may be that the framework is inferring the intended operation because you have the word "Create" prefixing the function name (CreateSharePointFolder). Details of this behaviour can be found here
Although that is all fine for DomainServices and EntityFramework, following the information in that article, it can be inferred that methods beginning "Delete" will be performing a delete of an entity, so must accept an entity as a parameter. The same is true for "Create" or "Insert" prefixed methods. Only "Get" or "Select" methods can take non-entity parameters, making it possible to pass a numeric id (for example) to a "Get" method.
Try changing your method name temporarily to "BlahSharePointFolder" to see if it is this convention of inferrance that's causing your problem.
Also, as there is no metadata defined for your SharePointFolderCreate DC, you might need to decorate the class (in addition to the [DataContract] attribute) with the [MetadataType] attribute. You will see how to implement this if you used the DomainServiceClass wizard and point to an EF model. There is a checkbox at the bottom for generating metadata. Somewhere in your solution.Web project you should find a domainservice.metadata.cs file. In this file, you will find examples of how to use the [MetadataType] attribute.
For the RIA WCF service to work correctly with your own methods, you need to ensure that all entities existing on the parameter list have at least one member with a [Key] attribute defined in their metadata class, and that the entity is returned somewhere on your DomainService in a "Get" method.
HTH
Lee

Resources