Recently I have been working on a project to change web-base system from old-fashion JDBC to myBatis. MyBatis is great but because I have to keep all the sql the same as before to minimize the change I have to make.
Then I have something like this: pojo.setFieldA(rs.getInt("FieldA")==1?"Y":"N");
As in the table 'FieldA' allows null, so when the result is null then I will have 'N' in fieldA in the pojo.
So, I have created a custom type handler and do something like this: public String getNullableResult(ResultSet paramResultSet, String paramString) throws SQLException { int ret = paramResultSet.getInt(paramString); return ret==1?"Y":"N";
}
But finally it does not work because in BaseTypeHandler, I found something like this:
public T getResult(ResultSet rs, String columnName) throws SQLException { Object result = getNullableResult(rs, columnName); if (rs.wasNull()) { return null; } return result; }
So, as the result returned by the query is null, then it return null directly instead of the value returned by the custom type handler.
I have done something other to achieve my goal... but I am quite curious about why it has to check rs.wasNull() in BaseTypeHandler... or I have seen in a wrong place???
Sorry, I meant unless "we" find, not me :) I do not know how to solve it.
BTW, hibernate fails when mapping a null to a primitive. That is
indeed a consistent solution but I am fairly sure that bypassing nulls
will create less issues.
One possible solution is letting the user choose if he wants nulls to
be set or not with a new global configuration property like
"callSettersOnNulls". In that case it will call always to the setter
wether it is a primitive or not.
Not sure it is needed. You can already replace null by another value
using current getResult method from TypeHandler interface or overiding
it from BaseTypeHandler.
In fact, that is what Chan Wai Lun should do to fix the issue with current code.
eduardo.macar...@gmail.com> wrote:
> Not sure it is needed. You can already replace null by another value
> using current getResult method from TypeHandler interface or overiding
> it from BaseTypeHandler.
> In fact, that is what Chan Wai Lun should do to fix the issue with current
> code.
Thank you very much, now I can understand the reason of doing so.
Yes I agree that is what I should do it... but why I asked this question was because in my mind, when I read the documentation and examples, extending baseTypeHandler and implement the getNullableResult should let me have the control on how the result should be return to myBatis... just like, if in some case I have to write some code to turn the result from query to something other before it send to myBatis for further process... I did not expect myBatis will check the original value that the query returned instead of checking the value returned by the custom type handler... maybe i missed it somewhere in the documentation... haha.
> Not sure it is needed. You can already replace null by another value > using current getResult method from TypeHandler interface or overiding > it from BaseTypeHandler.
> In fact, that is what Chan Wai Lun should do to fix the issue with current > code.
Can anyone tell how to use ibatis framework in java with relation to
creating a connection pool in jboss server. I am not getting any suitable
example. If anyone can tell me how to configure and how i can use it java.
Waiting for your repsonse..
On Tue, Jun 19, 2012 at 8:35 AM, Chan Wai Lun <fatlun1...@gmail.com> wrote:
> Thank you very much, now I can understand the reason of doing so.
> Yes I agree that is what I should do it... but why I asked this question
> was because in my mind, when I read the documentation and examples,
> extending baseTypeHandler and implement the getNullableResult should let me
> have the control on how the result should be return to myBatis... just
> like, if in some case I have to write some code to turn the result from
> query to something other before it send to myBatis for further process... I
> did not expect myBatis will check the original value that the query
> returned instead of checking the value returned by the custom type
> handler... maybe i missed it somewhere in the documentation... haha.
> Thanks and Regards,
> Wai Lun
> Eduardo於 2012年6月18日星期一UTC+8下午10時46分55秒寫道:
>> Not sure it is needed. You can already replace null by another value
>> using current getResult method from TypeHandler interface or overiding
>> it from BaseTypeHandler.
>> In fact, that is what Chan Wai Lun should do to fix the issue with
>> current code.
> Can anyone tell how to use ibatis framework in java with relation to
> creating a connection pool in jboss server. I am not getting any suitable
> example. If anyone can tell me how to configure and how i can use it java.
> Waiting for your repsonse..
> On Tue, Jun 19, 2012 at 8:35 AM, Chan Wai Lun <fatlun1...@gmail.com> wrote:
>> Hi Edurado,
>> Thank you very much, now I can understand the reason of doing so.
>> Yes I agree that is what I should do it... but why I asked this question
>> was because in my mind, when I read the documentation and examples,
>> extending baseTypeHandler and implement the getNullableResult should let me
>> have the control on how the result should be return to myBatis... just like,
>> if in some case I have to write some code to turn the result from query to
>> something other before it send to myBatis for further process... I did not
>> expect myBatis will check the original value that the query returned instead
>> of checking the value returned by the custom type handler... maybe i missed
>> it somewhere in the documentation... haha.
>> Thanks and Regards,
>> Wai Lun
>> Eduardo於 2012年6月18日星期一UTC+8下午10時46分55秒寫道:
>>> Not sure it is needed. You can already replace null by another value
>>> using current getResult method from TypeHandler interface or overiding
>>> it from BaseTypeHandler.
>>> In fact, that is what Chan Wai Lun should do to fix the issue with
>>> current code.
I think this behavior is a Bad Thing: It violates the principle of least surprise.
When I read an object the natural assumption is that it holds exactly the values returned from the mapped statement. When the object has primitive fields, but null is returned from the db, then a natural way to handle it would be to throw an exception. It's clearly a programmer error to use a primitive field for a nullable column.
This probably cannot be changed in a minor version, but a configuration option would be very welcome! IMHO for the next major version this natural behavior should be the default.
For motivation here's a real scenario that just happened to me. I had a lot of "fun" debugging this very strange and surprising error: We got a complaint that a user was not able to log-in. He had just changed his password, but was pretty sure that the entered password was correct. When debugging this I noticed that my "user" DTO read with mybatis _always_ contained a salt value for the password hash, even if it was null in the db. (Happens for users that were created before we added password salting). I asked a colleague to come over and help me debug this under the assumption that there is a dumb error in the code that I just don't notice, because I am too familiar with the code. But we both were stumped when we looked at the object returned from mybatis versus the same query executed directly on the database. We checked if we were looking at the same db for both versions, that all transactions were commited and just looked clueless at the code. The solution to the riddle was that somebody noticed code duplication and refactored it: Everywhere a "user" dto was created a random salt was set afterwards. So a better, more DRY way seemed to move the salt initialization to the field of the user dto. :-(
> I think this behavior is a Bad Thing: It violates the principle of least
> surprise.
> When I read an object the natural assumption is that it holds exactly the
> values returned from the mapped statement.
> When the object has primitive fields, but null is returned from the db,
> then a natural way to handle it would be to throw an exception. It's
> clearly a programmer error to use a primitive field for a nullable column.
> This probably cannot be changed in a minor version, but a configuration
> option would be very welcome!
> IMHO for the next major version this natural behavior should be the
> default.
> For motivation here's a real scenario that just happened to me.
> I had a lot of "fun" debugging this very strange and surprising error:
> We got a complaint that a user was not able to log-in. He had just changed
> his password, but was pretty sure that the entered password was correct.
> When debugging this I noticed that my "user" DTO read with mybatis
> _always_ contained a salt value for the password hash, even if it was null
> in the db. (Happens for users that were created before we added password
> salting).
> I asked a colleague to come over and help me debug this under the
> assumption that there is a dumb error in the code that I just don't notice,
> because I am too familiar with the code.
> But we both were stumped when we looked at the object returned from
> mybatis versus the same query executed directly on the database.
> We checked if we were looking at the same db for both versions, that all
> transactions were commited and just looked clueless at the code.
> The solution to the riddle was that somebody noticed code duplication and
> refactored it: Everywhere a "user" dto was created a random salt was set
> afterwards. So a better, more DRY way seemed to move the salt
> initialization to the field of the user dto. :-(
> When I read an object the natural assumption is that it holds exactly
> the values returned from the mapped statement.
> When the object has primitive fields, but null is returned from the db,
> then a natural way to handle it would be to throw an exception. It's
> clearly a programmer error to use a primitive field for a nullable column.
I don't agree with the last statement. One of the strong points of MyBatis (the reason we adopted it) was it functions well in the presence of "impedance" mismatch. We have many legacy databases that are not well normalized and missing a lot of referential integrity checks. MyBatis continues to provide a useful mapping in this environment, while a more strict solution like Hibernate would simply not work.
Having said all that, you obviously can't stuff a null into an int. So, the problem is not using a primitive field for a nullable column, but failing to account for it in the SQL.