I have one java class which resembles to
class A {
String a;
B bclass;
}
class B {
String b;
String c;
}
my ibatis query is : Select a,b,c from A_TABLE
and resultmap I want is something like this where I can fill properties of class B (B.b,B.c) as well.
<resultMap class="A" id="resmap">
<result property="a" column="A" jdbcType="VARCHAR"/>
<result property="bclass.b" column="B" jdbcType="VARCHAR"/>
<result property="bclass.c" column="C" jdbcType="VARCHAR"/>
</resultmap>
any idea how I can fill this object A from ibatis query so I have all 3 a,b,c properties filled?
The mapping of inner objects is made with association tag. You need something like this:
<resultMap id="resmap" type="A">
<result property="a" column="a"/>
<association property="b" javaType="B">
<result property="b" column="b"/>
<result property="c" column="c"/>
</association>
</resultMap>
Check documentation as well, it's explained in details.
Related
I have a request, which gives me one object of Advantage class:
<resultMap id="AdvantageResult" type="Advantage">
<id property="id" jdbcType="BIGINT" javaType="java.lang.Long" column="id"/>
<result property="code" column="code"/>
<result property="name" column="name"/>
<result property="description" column="description"/>
<result property="asIs" column="as_is"/>
<result property="toBe" column="to_be"/>
<result property="availableName" column="available_name"/>
<result property="availableNameShort" column="available_name_short"/>
<result property="availableDescription" column="available_description"/>
<result property="availableDescriptionShort" column="available_description_short"/>
<result property="activeName" column="active_name"/>
<result property="activeNameShort" column="active_name_short"/>
<result property="activeDescription" column="active_description"/>
<result property="activeDescriptionShort" column="active_description_short"/>
</resultMap>
Here is my request, where I use the map:
<select id="findAdvantageByLoyaltyAndConfigDetailId" resultMap="AdvantageResult">
select a.id, a.code, a.name, a.description, a.as_is, a.to_be,
a.available_name, a.available_name_short, a.available_description, a.available_description_short,
a.active_name, a.active_name_short, a.active_description, a.active_description_short
from advantage a
left join detail_advantage da on da.advantage_id = a.id
where da.config_detail_id = #{configDetailId}
</select>
I want to get Map <Long, Advantage>, where the long key will be the param #{configDetailId}
How should I rewrite the mapper?
I could think of two approaches.
Convert the returned Advantage to Map in Java code.
Defining a simple default method in the Java mapper interface should be sufficient.
Advantage internalSelect(Long configDetailId);
default Map<Long, Advantage> select(Long configDetailId) {
return Map.of(configDetailId, internalSelect(configDetailId));
}
Add a private field to Advantage that holds configDetailId and use #MapKey.
private Long configDetailId;
Then include the parameter to the column list of the select and the result map.
select ...
, #{configDetailId} as configDetailId
from ...
<result property="configDetailId" column="configDetailId" />
Then add #MapKey to your Java mapper interface.
#MapKey("configDetailId")
Map<Long, Advantage> findAdvantageByLoyaltyAndConfigDetailId(Long configDetailId);
#MapKey is handy when there is an existing property for the 'key'.
Otherwise, I would recommend the first approach.
i m actually testing mybatis. I like really but, i want to go deeper and i have a problem, with resultMap.
Actually i just want to get from database a computer object, which is composed of multiple screens and one tower (other object of my code)
This is my resultMap for computer :
<resultMap type="entity.Computer" id="computer">
<id column="id" property="id"/>
<result column="name" property="name"/>
<association property="tower" column="towerid" resultMap="towerResult" columnPrefix="t_"/>
<collection ofType="entity.Screen" property="screen" javaType="ArrayList" resultMap="screenResult" columnPrefix="s_"/>
</resultMap>
this the request :
<select id="getcomputerById" resultMap="computer">
Select c.id, c.name, c.towerid, s.id as s_id, s.size as s_size, s.type as s_type, s.computer_id as s_computer_id, t.id as t_id, t.ram as t_ram, t.stockage as t_stockage from computer c inner join tower t on t.id = c.towerid left join screen s ON s.computer_id = c.id where c.id=#{computerId}
</select>
With this code everything works fine. BUTTTTTTTT !
What i wanted to do is :
<resultMap type="entity.Computer" id="computer">
<id column="id" property="id"/>
<result column="name" property="name"/>
<association property="tower" column="towerid" select="getTowerbycomputerid"/>
<collection ofType="entity.Screen" property="screen" javaType="ArrayList" resultMap="screenResult" columnPrefix="s_"/>
</resultMap>
The only thing different is : <association property="tower" column="towerid" select="getTowerbycomputerid"/>
Of course i change my request to :
<select id="getcomputerById" resultMap="computer">
Select c.id, c.name, c.towerid, s.id as s_id, s.size as s_size, s.type as s_type, s.computer_id as s_computer_id from computer c inner join tower t on t.id = c.towerid left join screen s ON s.computer_id = c.id where c.id=#{computerId}
</select>
There is the xml match the getTowerbycomputerid :
<select id="getTowerbycomputerid" resultMap="towerResult">
Select t.id, t.ram, t.stockage from tower t inner join computer c on c.towerid=t.id where c.id=#{computerId}
</select>
And the resultMap :
<resultMap id="towerResult" type="entity.Tower">
<id property="id" column="id"/>
<result property="ram" column="ram"/>
<result property="stockage" column="stockage"/>
</resultMap>
I don't understand why the second resultmap don't work.
If i have one-one tower and one-one Screen
I can have a resultmap, with two association and in them a select="getmethod"
And it work perfectly
But when i change my code to have one-one tower and one-many Screen, i can't let select="getmethod" for the last association.
It return null for the one-one, but the one-many work (with the right select statement).
Any idea ?
Maybe it's not possible to do it?
THx :)
i answer my question, #ave put me on the right way.
His comment : It should be possible. The nested select getTowerbycomputerid seems to expect computer id, whereas you specify column="towerid" in the association. Shouldn't it be "id"? If that's not the reason, please consider providing a complete example like these.
It's not exactly this but it helps me to find a solution.
There is my request :
#Select("Select c.id, c.name, c.towerid, s.id as s_id, s.size as s_size, s.type as s_type, s.computer_id as s_computer_id from computer c left join screen s ON s.computer_id = c.id where c.id=#{computerId}")
#ResultMap("ComputerMapper.computer")
public Computer getcomputerById(#Param("computerId") Integer computerId);
This is my resultMap :
<resultMap type="entity.Computer" id="computer">
<id column="id" property="id"/>
<result column="name" property="name"/>
<association property="tower" column="towerid" javaType="entity.Tower" select="getTowerbycomputerid"/>
<collection ofType="entity.Screen" property="screen" javaType="ArrayList" resultMap="screenResult" columnPrefix="s_"/>
</resultMap>
And now my resultMap and the request to get the tower :
<resultMap id="towerResult" type="entity.Tower">
<id property="id" column="id"/>
<result property="ram" column="ram"/>
<result property="stockage" column="stockage"/>
</resultMap>
<select id="getTowerbycomputerid" resultMap="towerResult">
Select t.id, t.ram, t.stockage from tower t where t.id = #{towerid}
</select>
And now everything works fine.
Before i got this to select the tower:
<select id="getTowerbycomputerid" resultMap="towerResult">
Select t.id, t.ram, t.stockage from tower t inner join computer c on c.towerid = t.id where c.id = #{computerId}
</select>
This is the end. Thx #ave :)
I encountered problems when returning a list of Objects inside another Object when using MyBatis. My main object looks like this:
private Long id;
private String symbol;
private List<TypePermission> typePermissions;
and my mapper looks like this
<resultMap type="CalendarType" id="calendarTypeMap">
<result column="id" property="id"/>
<result column="symbol" property="symbol"/>
<collection property="TypePermissions" resultMap="TypePermissions"/>
</resultMap>
<resultMap id="TypePermissions" type="TypePermission">
<result property="roleId" column="roleId"/>
<result property="permissionSymbol" column="permissionSymbol"/>
</resultMap>
My goal is to get an object like this:
content:[
"id":id,
"symbol":symbol,
"TypePermissions":{
"roleId":roleId,
"permissionSymbol":permissionSymbol
}
]
When I execute the sql query I get the following an error cannot find symbol TypePermissions, because the main SELECT tries to select rows such as TYPEPERMISSIONS, ID, SYMBOL
I searched over the internet, but failed to find anything useful. Could you help me and point out what am I doing wrong?
Please post your select snippet, I think this will ok:
<select id="selectCalendarType" parameterType="int" resultMap="calendarTypeMap">
SELECT c.id,
c.symbol
t.roleId,
t.permissionSymbol
FROM CalendarType c
LEFT JOIN TypePermission t ON c.id = t.c_id
WHERE c.id = #{id}
</select>
And I think what you will get is actully something like this:
content:{
"id":id,
"symbol":symbol,
"TypePermissions":[{
"roleId":roleId,
"permissionSymbol":permissionSymbol
}]
}
And more about this you can read this example Nested_Results_for_Collection
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){...}
(Using MyBatis v3.0.4.)
I have a problem that I do not know how to solve. My object model is:
Location.java
public class Location {
// ... other content
private List addresses;
// ... other content
}
Address.java
public class Address {
public enum Type { POSTAL, POBOX, INPUT, CLEANSED }
private Type type;
private String line1;
// ... other content
}
My SQL is:
SELECT
// ... other content
postal_address_line_1,
postal_address_line_2,
postal_address_city,
cleansed_address_line_1,
cleansed_address_line_2,
cleansed_address_city,
// ... other content
How would I construct a resultMap that would plug the appropriate
columns into an address instance of the correct type and added to the
same list in Location.java? I would like to avoid having to add
another instance variable to Location.java just to hold a different
type of address.
Use a discriminator tag in your result map.
Look at the mybatis user guide. Search for "discriminator" you see more informations.
<resultMap id="vehicleResult" type="Vehicle">
<id property=”id” column="id" />
<result property="sharedPropA" column="shared_column"/>
<discriminator javaType="int" column="address_type">
<case value="1" resultMap="postalResultMap"/>
<case value="2" resultMap="inputResultMap"/>
<case value="3" resultMap="cleanResultMap"/>
<case value="4" resultMap="whatIsaCleansedAddressResultMap"/>
</discriminator>
</resultMap>
Addition 1:
You need to select the addresses as different rows.
i.e
select
postal_address_line_1 as line1,
postal_address_line_2 as line2,
postal_address_city as city,
type as 'POSTAL'
....
union
select
postal_address_line_1 as line1,
postal_address_line_2 as line2,
postal_address_city as city,
type as 'CLEANSED'
.....
then the built in enum type handler should set the type correctly.
Along the lines of Andy Pryor's suggestion, I was able to solve the problem by updating my SQL statement to something like the following:
SELECT
// ... other content
'POSTAL' as Postal_Address_Type,
postal_address_line_1,
postal_address_line_2,
postal_address_city,
'CLEANSED' as Cleansed_Address_Type,
cleansed_address_line_1,
cleansed_address_line_2,
cleansed_address_city,
// ... other content
Then update my resultMap to the following:
<resultMap ...>
//... other content
<association property="postalAddress" javaType="com.x.y.z.Address">
<result property="type" column="Postal_Address_Type"/>
<result property="line1" column="Address_Part_1_Name"/>
<result property="line2" column="Address_Part_2_Name"/>
//...other content
</association>
<association property="cleansedAddress" javaType="com.x.y.z.Address">
<result property="type" column="Cleansed_Address_Type"/>
<result property="line1" column="Address_Part_1_Name"/>
<result property="line2" column="Address_Part_2_Name"/>
//...other content
</association>
</resultMap>
Finally, within my Address class I am able to have setType(Type) and the inbuilt enumerated type handler does the magic. Within the Location class I can just have one list of instances of Address and the various setXXXAddress() methods can add to this list appropriately.
It is unfortunate that I cannot plug the columns into some sort of factory class but putting hard-coded types into the SQL statement isn't too dirty, in my opinion. The disadvantage is that I have introduced coupling between the domain model's Address.Type values and the SQL statement but this is kind of already there given that the resultMap SQL XML needs to hold the names of instance variables in the Address class anyway.