Is it possible to use Aggregate functions in MyBatis - java

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

Related

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>

Mybatis2 to MyBatis3 Conversion - Results with Same Property

I am working on converting a large MyBatis2 map to a MyBatis3 map, and have run into a bit of an issue where I have a resultMap with multiple result elements using the same property attribute (and the class is generated from a WSDL outside of my control):
<resultMap id="blah" class="someWsdlGeneratedClass">
<result property="addressLine"
resultMap="addressLineOneListMap"
javaType="java.util.List" />
<result property="addressLine"
resultMap="addressLineTwoListMap"
javaType="java.util.List" />
</resultMap>
<resultMap id="addressLineXListMap" class="string">
<!-- Result maps are the same except for column -->
<result property="addressLine" column="COLUMN_X" />
</resultMap>
Notice that both properties are "addressLine".
This works fine for Mybatis2. However, if I try to use the same pattern for MyBatis3, I get an IllegalArgumentException: Result Maps collection already contains value for Mapper.mapper_resultMap[blah]_collection[addressLine]
<resultMap id="blah" type="someWsdlGeneratedClass">
<collection property="addressLine"
resultMap="addressLineOneListMap"
javaType="java.util.List" />
<collection property="addressLine"
resultMap="addressLineTwoListMap"
javaType="java.util.List" />
</resultMap>
I'd like to avoid writing a wrapper around the generated class in a Dto object, if possible, as that would result in a major refactoring effort in the project. Is there something I can add in the map itself?
You can add a 2nd setter (setAddressLine2) to your generated dto.
In which your code for it can just add to addressLine. ex:
void setAddressLine2(final List<Address> addressLine2) {
address.addAll(addressLine2);
}
If that's not possible you can try changing your query to return a union of the 2 columns
Without knowing your exact query it would look something like:
SELECT foo, addressLine1 as Address
FROM bar
UNION
SELECT foo, addressLine2 as Address
FROM bar
If that isn't possible then you need to create a test project and create an issue on https://github.com/mybatis/mybatis-3 and request a feature.
That option is probably best anyways as I'm not sure you are using it correctly. It seems that your 2nd example (using collection) is correct (conceptually at least. you still can't map to the same property using it) but the first won't behave as you explained it?

Multiple ids in MyBatis resultMap - performance benefits?

I am using MyBatis with Java. Can I use multiple ids in the result map?
Code
My Result Java class looks like this:
public class MyClass {
private int id1;
private int id2;
private int total;
// getters & setters
}
Currently, my MyBatis result map looks like this:
<resultMap id="MyResult" type="MyClass">
<result property="id1" column="id1" />
<result property="id2" column="id2" />
<result property="total" column="total" />
</resultMap>
This result map is used in this query:
<select id="mySelect" resultMap="MyResult" parameterType="Map">
select
id1,
id2,
sum(total) as total
from
myTable
group by
id1,id2;
</select>
Everything works fine, but according to MyBatis documentation I should use id to gain some efficiency. I want to change the MyBatis result map to:
<resultMap id="MyResult" type="MyClass">
<id property="id1" column="id1" />
<id property="id2" column="id2" />
<result property="total" column="total" />
</resultMap>
Questions
Will it work the same as before?
[Later Edit]: tested, it seems that it does not screw up the groupings and the rows, so I would say that it works as before and as expected.
Will it bring the MyBatis performance benefits by using id?
Where did I look, but could not find an answer
MyBatis documentation - they do not say or give example of a similar situation.
Composite Key - however, I do not have a composite key, and I would rather not modify MyClass to create a pseudoclass ID that has id1, id2.
Yes, it will work the same as before, and according to documentation, it will gain some efficiency, that you will appreciate when you work with massive rows. Anyway, my recommendation is to use the second result map, in order to be the most accurate in the definition of your resultmap according to your database estructure.

Is it possible to map POJO fields with names different from the table columns?

I am relatively new to ibatis. I know it is already upgraded to mybatis, but for some reason I have to use ibatis. My question is that "Is it possible to map POJO fields with names different from the table columns?"
I have a table, the mapping file, and the POJO class. I can successfully read data if my POJO class has names exactly the same as the columns in the table, but if I name the field something else, it does not work. I changed the corresponding getter and defined a resultMap in the mapping file like the following
<resultMap id="result" class="Subscriber">
<result column="AdvisorId" property="id" jdbcType="INTEGER"/>
<result column="FirstName" property="FirstName" jdbcType="VARCHAR"/>
<result column="LastName" property="LastName" jdbcType="VARCHAR"/>
<result column="EmailId" property="EmailId222" jdbcType="VARCHAR"/>
</resultMap>
<select id="getAll" resultMap="result">
SELECT AdvisorId,FirstName,LastName,EmailId FROM communication
</select>
Here as an example, I am trying to rename EmailId in my POJO field to EmailId222
In the Subscriber class you have to define getter and setter methods for the field. Be careful to use capital letters if necessary. Then you have to restart your application to make the changes work. In your case, you should implement:
getEmailId222(){...}
setEmailId222(String EmailId222){...}

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