Feature: Improve automatic column name mapping using camel case conversion

968 views
Skip to first unread message

Nicky Ramone

unread,
Jun 12, 2010, 10:26:49 PM6/12/10
to mybatis-user
Hi

I'm new to MyBatis. I just started using it today because it fits my
needs, and so far I think it's great.
I wanted to suggest a new feature (I tried adding this in the issue
tracker, but I already messed up since I found no way of indicating
it's a feature request rather than a bug).

Anyway, I was thinking that It would be nice that, when auto-mapping
column names to pojo properties, column names using underscores map
according to camel case conventions.
For example, the column name book_name should be mapped to the pojo
property 'bookName'.

Another option is allowing for a custom implementation of column to
property matching. But whatever the solution is, I think it's quite
useful to reduce SQL queries by convention over configuration.

I'd appreciate your thoughts.

Cheers.

stephen friedrich

unread,
Jun 16, 2010, 5:27:53 AM6/16/10
to mybatis-user
This is definitely a pain point when using MyBatis.
AFAIK there is no way to customize the framework from the outside to
do this.

I am running on a slightly modified version with one class modified to
get
auto-mapping for camel case Java properties.
In case you are interested:

org.apache.ibatis.reflection.Reflector:

New field (only for performance reasons):
private Map<String, String> propertyMap = new HashMap<String,
String>();

Then I replaced these line in the constructor
for (String propName : readablePropertyNames) {

caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH),
propName);
}
for (String propName : writeablePropertyNames) {

caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH),
propName);
}
by
putAll(readablePropertyNames);
putAll(writeablePropertyNames);

and added the putAll method:
private void putAll(String[] propertyNames) {
for (String propName : propertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(),
propName);

caseInsensitivePropertyMap.put(Stringz.camelCase2underScore(propName).toUpperCase(),
propName);
}
}

Then replaced findPropertyName with this
public String findPropertyName(String name) {
String propName = propertyMap.get(name);
if (propName == null) {
propName =
caseInsensitivePropertyMap.get(name.toUpperCase());
if (propName != null) {
propertyMap.put(name, propName);
}
}
return propName;
}

Finally the code to convert a string from camel case to underscore:
(currently in a utility class named Stringz):
/**
* <p>Converts a string in "camel case" to its underscored, lower-
case equivalent, e.g. "minimumFieldWidth" => "minimum_field_width".</
p>
* <p>The algorithm determines each character's type as one of
lower-case-character, upper-case-character, digit or other
(punctuation, ...).
* An underscore is inserted in between each change of character
type, except for the change from upper- to lower-case characters.</p>
* @param text a text in camel case form, e.g. "columnKey2value"
* @return the lower-cased, underscored form, e.g.
"column_key_2_value"
*/
@NotNull
public static String camelCase2underScore(@NotNull String text) {
StringBuilder builder = new StringBuilder(text.length() + 5);
CharType lastCharType = null;
for(int i = 0, count = text.length(); i < count; ++i) {
char c = text.charAt(i);
CharType charType = CharType.get(c);
//noinspection OverlyComplexBooleanExpression
if (lastCharType != null
&& lastCharType != charType
&& !(lastCharType == CharType.UPPER && charType ==
CharType.LOWER)) {
builder.append('_');
}
builder.append(Character.toLowerCase(c));
lastCharType = charType;
}
return builder.toString();
}

/**
* Represents the classification of a character into lower-case-
character, upper-case-character, digit or other.
* Uses the unicode character types (so that for example "ä"
counts as a lower-case-character).
*/
private enum CharType {
LOWER, UPPER, DIGIT, OTHER;

@NotNull
public static CharType get(char c) {
if(Character.isLowerCase(c)) {
return LOWER;
}
if (Character.isUpperCase(c)) {
return UPPER;
}
if (Character.isDigit(c)) {
return DIGIT;
}
return OTHER;

Nicolas ANTONIAZZI

unread,
Jun 16, 2010, 5:31:43 AM6/16/10
to mybati...@googlegroups.com
I totally agree that it would be a nice improvement

+1 for this new feature

And thank you for your code stephen

2010/6/16 stephen friedrich <stephen....@googlemail.com>

Nicky Ramone

unread,
Jun 16, 2010, 10:17:59 AM6/16/10
to mybatis-user
Thanks guys for the replies.
when I posted this, I also submitted this issue:
http://code.google.com/p/mybatis/issues/detail?id=43&colspec=ID%20Type%20Component%20Status%20Priority%20Version%20Target%20Summary%20Reporter
where I provide the entry point for the modification (like stephen
posted).

I wouldn't like to use a forked version, so that's why I am interested
in the guys to accept this request.
I can provide the code, but still got no replies.
Let's hope they take it into account.


On Jun 16, 6:31 am, Nicolas ANTONIAZZI <nicolas.antonia...@gmail.com>
wrote:
> I totally agree that it would be a nice improvement
>
> +1 for this new feature
>
> And thank you for your code stephen
>
> 2010/6/16 stephen friedrich <stephen.friedr...@googlemail.com>

Nicky Ramone

unread,
Jun 16, 2010, 3:59:52 PM6/16/10
to mybatis-user
by the way, does anyone know if it's possible to submit a diff of the
code for that feature?
I don't see any place where I can upload. I guess they have to approve
it somehow?

On Jun 16, 11:17 am, Nicky Ramone <nixe...@gmail.com> wrote:
> Thanks guys for the replies.
> when I posted this, I also submitted this issue:http://code.google.com/p/mybatis/issues/detail?id=43&colspec=ID%20Typ...

Eduardo

unread,
Jun 16, 2010, 4:56:15 PM6/16/10
to mybatis-user
+1

That will be great.

It would be good to have an attribute somethere in the mapper xml file
to set automaping or "camel case mapping" so "select * from table"
works directly. Or maybe as you pointed to be able to set a custom
column to bean mapper.

This will be time saving and really useful.

Clinton Begin

unread,
Jun 16, 2010, 6:29:37 PM6/16/10
to mybati...@googlegroups.com
Somewhere in the ibatis 2 codebase I have a bayesian-ish matching
algorithm for columns, but decided it wasn't predictable enough for
production mappings. :-)

But yes, I'll have to have a look at this, as I believe I put a hook
in there somewhere for this. It was one of the goals on the old
version 3 whiteboard.

Did any of you create the Feature Request in the issue tracker?

Cheers,
clinton

Nicky Ramone

unread,
Jun 22, 2010, 8:30:58 PM6/22/10
to mybatis-user
Yes, I did here: http://code.google.com/p/mybatis/issues/detail?id=43
I reported as a "defect" becasue I couldn't find where to add a
"feature" request.

I think at first it should be faily simple if you change the
findPropertyName() in the Reflector class.
I wouldn't try to be too ambicious in column mapping but something
like the underscores could work pretty well.
Maybe it should try to find the property by default as it is now, and
if it fails, try to apply search with the "underscores" conventions.
Let me know if you would like a patch.

Cheers.

sskillcorn

unread,
Jul 14, 2010, 11:45:06 AM7/14/10
to mybatis-user
As Nicky says above, would a simple change be appropriate to tackle
just the underscore issue?

The field_name > fieldName pattern is a pretty common one in matching
DB fields to Java properties.

I had the exact same issue and made a simple change to
Reflector.findPropertyName() to match underscoreless field names if it
doesn't find an immediate match:

public String findPropertyName(String name) {
String upperName = name.toUpperCase(Locale.ENGLISH);
return caseInsensitivePropertyMap.containsKey(upperName) ?
caseInsensitivePropertyMap.get(upperName) :
caseInsensitivePropertyMap.get(upperName.replaceAll("_",
""));
}

Stephen, thanks for posting your code, that made my change super
simple!

Steve

Nicky Ramone

unread,
Jul 21, 2010, 10:14:41 PM7/21/10
to mybatis-user
I think that, ideally, it should let you inject a custom strategy for
resolving names, so that you can add your own custom one if ever
needed.
Would be a nice and non-intrusive change, but for that, Reflector
should probably be redesigned.
> > > Did any of you create theFeatureRequest in the issue tracker?
>
> > > Cheers,
> > > clinton
>
> > > On Wed, Jun 16, 2010 at 2:56 PM, Eduardo <eduardo.macar...@gmail.com> wrote:
> > > > +1
>
> > > > That will be great.
>
> > > > It would be good to have an attribute somethere in the mapper xml file
> > > > to set automaping or "camel case mapping" so "select * from table"
> > > > works directly. Or maybe as you pointed to be able to set a custom
> > > > column to bean mapper.
>
> > > > This will be time saving and really useful.
>
> > > > On 13 jun, 04:26, Nicky Ramone <nixe...@gmail.com> wrote:
> > > >> Hi
>
> > > >> I'm new to MyBatis. I just started using it today because it fits my
> > > >> needs, and so far I think it's great.
> > > >> I wanted to suggest a newfeature(I tried adding this in the issue
> > > >> tracker, but I already messed up since I found no way of indicating
> > > >> it's afeaturerequest rather than a bug).
Reply all
Reply to author
Forward
0 new messages