How can I query with association or collection object in MyBatis - java

Here's my situation.
I have two class, say they are class A and class B. Class A has a reference with B, so when I try to get all columns in class A and class B, I'm using the association tag in MyBatis configuration, which looks like this.
<association property="b" column="id"
javaType="com.xx.B"
select="getBinfo"></association>
The configuration for getting the info for A:
<select id="list" parameterType="Map" resultMap="A">
select * from A
</select>
The one for getting B info:
<select id="getBinfo" parameterType="Map" resultMap="B">
select * from B where id = #{id}
</select>
It works fine getting all the columns into my bean, namely A here, with B info filled out correctly.
My question is, I can add where condition for id="list" to filter the result with the properties in A, what if I want to filter it with properties in B? How can I do it?
I'm trying to add where condition for id="list" with the columns in B, and passed the parameter with a Map. But the exception shows that the property is not set.
It's grateful if anyone can help.

you can use
<association property="b" resultMap="bReusltMap" />
and join table b and add your where condition.

Related

Is it possible to use Aggregate functions in MyBatis

I am a newbie to MyBatis. As per my knowledge every column is mapped to a property value while retrieving data from the database. So is it possible to use aggregate functions using MyBatis. To what property we will map the results? I have searched it everywhere But can't find any details about the usage of aggregate functions with MyBatis. If anyone can help, please.
Every column of the result set is mapped to a property. Therefore, it's just a matter of producing a SQL statement that does that.
For example:
<resultMap id="regionProfit" type="app.RegionProfitVO">
<result property="region" column="region" />
<result property="cnt" column="cnt" />
<result property="profit" column="profit" />
</resultMap>
<select id="getRegionProfit" resultMap="regionProfit">
select
region,
count(sales) as cnt,
sum(revenue) - sum(expenses) as profit
from sales
group by region
</select>
Now, in the Java code you could do something like:
List<RegionProfitVO> rp = sqlSession.selectList("getRegionProfit");
If you don't want to create another POJO, you can always retrieve the result as a list of java.util.Map<String, Object> but use resultType="hashmap" in mapper.xml file for that select query

How to map a DB column name that is a Java reserved word to a Java model object field using MyBatis?

So I have a table CoolTable with 2 columns: something and short.
And my model class named CoolClass reflects it with 2 fields: something and _short. short is a Java reserved keyword, so the field had to be prefixed with the underscore.
Now my mybatis mapper XML uses CoolClass like so:
<select id="getStuff" resultType="CoolClass">
SELECT * FROM CoolTable
</select>
<insert id = "insertStuff" parameterType = "CoolClass">
INSERT INTO CoolTable (something, short)
VALUES (#{something}, #{short})
</insert>
Now, when I getStuff and insertStuff only something column gets retrieved and inserted. short is always null.
I searched through all the MyBatis documentation but couldn't find anything relevant to help with this case.
How can I go about mapping short column to _short field of class?
Fixed it by using a ResultMap like so:
<resultMap id="coolResultMap" type="CoolClass">
<result property="_short" column="short"/>
</resultMap>
<select id="getStuff" resultMap="coolResultMap">
SELECT * FROM CoolTable
</select>

Mybatis inline type handler for select statements

there is simple select
<select id = "getWidgetConfig" resultType="com.comp.entity.widgets.cnfg.Widget">
SELECT `json_config`
FROM `widget_config`
WHERE `widget_id` = #{widgetId}
</select>
where json_config is string value.
Can i bind type/result handler(inline) to convert json_config value into my entity Widget using my handler?
I can do this inline for update/insert statements
INSERT INTO `widget_config`
(
`json_config`
)
VALUES
(
#{widget,typeHandler=com.comp.mybatis.handlers.widget.WidgetTypeHandler}
)
How can i do similar for select statements?
Thanks
I don't think it is possible to use type handlers on the top level in select. But you can approach this a bit by using result map with constructor:
<resultMap id="widgetMap" type="Widget">
<constructor>
<arg column="json_config" javaType="string"/>
</constructor>
</resultMap>
<select id="getWidgetConfig" resultMap="widgetMap">
SELECT `json_config`
FROM `widget_config`
WHERE `widget_id` = #{widgetId}
</select>
The potential downside is that you need to have parsing logic in constructor of Widget. If this not an option one way to overcome this is to create copy constructor for Widget and change mapping like this:
<resultMap id="widgetMap" type="Widget">
<constructor>
<arg column="json_config" javaHandler="com.comp.mybatis.handlers.widget.WidgetTypeHandler"/>
</constructor>
</resultMap>
You need make sure Widget has appropriate copy constructor that takes value of type Widget and creates a copy (it may probably reuse internals of the passed Widget if they are immutable).

Fetching a LONGBLOB as a byte array using MyBatis

I am using MyBatis in a project, where I have a query that gets data from a LONGBLOB field in a MySQL database. I wish to get the result as a byte array (byte[]), so I try this:
<select id="fetchData" resultType="_byte[]" parameterType="_long">
select blobData from Table where id = #{id}
</select>
This does not work, however. I get the following error:
java.lang.ClassCastException: [B cannot be cast to [Ljava.lang.Object;
at org.apache.ibatis.binding.MapperMethod.convertToArray(MapperMethod.java:146)
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:129)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:90)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:40)
Something tells me I should specify a type handler (BlobTypeHandler?), or something? But where and how?
I should mention that I have no problem getting around this problem by creating a wrapper class for the byte array and using a result map:
<resultMap type="BlobData" id="BlobDataMap">
<constructor>
<idArg javaType="_long" column="id" />
<arg javaType="_byte[]" column="blobData" />
</constructor>
</resultMap>
Still I am wondering if there's a more elegant way that does not involve creating a wrapper class.
in my project we use blobs this way:
we define a result map for our used class:
<resultMap class="SomeClass" id="SomeClassResultMap">
<result property="classByteAttribute" column="blobData" />
<result property="classIdAttribute" column="id" />
</resultMap>
and in the select statement we use this result map
<select id="selectStatement" resultMap="SomeClassResultMap" parameterClass="Integer">
SELECT * FROM EXAMPLETABLE where id=#id#
</select>
after the execution the blob is in the byte array.
As suggested by duffy the only way to get the result as a byte array is:
Mybatis Version 3.1.+
define a resultMap
<resultMap class="MyClass" id="MyClassMap">
<result property="myByteArray" column="MYBINARY " />
</resultMap>
<select id="selectStatement" resultMap="MyClassMap">
SELECT MYBINARY FROM EXAMPLETABLE
</select>
but,
Mybatis Version 3.0.5
<select id="selectStatement" resultType="_byte[]">
SELECT MYBINARY FROM EXAMPLETABLE
</select>
it is a strange regression since mybatis is not able to apply the correct (BlobTypeHandler) TypeHandler, and is not possible to specify the TypeHandler on select tag.

MyBatis: Controlling Object Creation vs. Referencing Existing Objects

This is my first question here, so please let me know if there's anything I can provide to make my question clearer. Thanks!
Is there anyway to tweak MyBatis (3.0.4) so that when it's running through the nested result maps and creating objects based on the results, it doesn't create "duplicate" objects and instead references the object that has already been created? To clarify, let's say I've got an object that consists of the following (simplified) info:
Public class PrimaryObject {
Private Contact role1;
Private Contact role2;
Private List<OtherObject> otherObjects;
}
Public class OtherObject {
Private String otherProperty;
Private Contact otherRole;
}
And the result maps/SQL (assume type aliases):
<mapper namespace="PrimaryObject">
<resultMap id="primaryObject" type="primaryObject">
<association property="role1" column="ROLE_1_ID"
select="Contact.findContactByPK" />
<association property="role2" column="ROLE_2_ID"
select="Contact.findContactByPK" />
<collection property="otherObjects" column="PRIMARY_OBJECT_ID"
javaType="List" ofType="OtherObject"
select="OtherObject.findOtherObjectsByPrimaryObjectPK" />
</resultMap>
<select id="selectPrimaryObjectByPK" parameterType="..."
resultMap="primaryObject">
SELECT * FROM PRIMARY_OBJECT_TABLE
WHERE PRIMARY_OBJECT_ID = #{value}
</select>
...
<mapper namespace="OtherObject">
<resultMap id="otherObject" type="otherObject">
<result property="otherProperty" column="OTHER_PROPERTY" />
<association property="otherRole" column="OTHER_ROLE_ID"
select="Contact.findContactByPK" />
</resultMap>
<select id="findOtherObjectsByPrimaryObjectPK" parameterType="..."
resultMap="otherObject">
SELECT OTHER_OBJECT.OTHER_PROPERTY, OTHER_OBJECT.OTHER_ROLE_ID
FROM OTHER_OBJECT JOIN PRIMARY_OBJECT
ON PRIMARY_OBJECT.PRIMARY_OBJECT_ID = OTHER_OBJECT.PRIMARY_OBJECT_ID
</select>
...
<mapper namespace="Contact">
<resultMap id="contact" type="Contact">
<result property...
...
</resultMap>
<select id="findContactByPK" parameterType="..." resultMap="contact">
SELECT * FROM CONTACT...
</select>
Now let's say a contact is acting as both role1 and role2 on primaryObject. From what I've seen, MyBatis creates the contact object referenced in role1, and when it hits role2, it simply references the object it created for role1. That's great!
However, let's say that same contact is acting as the otherRole in otherObject. MyBatis now creates an identical contact object, as opposed to just referencing the original contact created/referenced when making the primaryObject. Is there any way I can prevent this from happening, and instead just store a reference in my otherObject that references that same contact that the two different fields in primaryObject are pointing to? I've been looking at a custom ResultHandler, but the example's complexity coupled with my lack of experience is preventing me from understanding if it even addresses the problem I'm trying to solve in the first place. Thanks in advance for your help and patience!
OK, I think your question might be a duplicate of How to map a myBatis result to multiple objects?. So, no MyBatis does not have native support for this problem, if I'm understanding it correctly.
You should be careful using nested selects, as they execute addition select statements on the database. I suggest checking out the MyBatis 3 User Guide, particularly around page 35 and up. MyBatis has very powerful ways to avoid the n+1 selects problem. What is SELECT N+1?

Categories