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 :)
Related
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.
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 dont know why but when I import this project to Eclipse. This work well.
So, I think this is problem of eclipse project when import to InteliJ IDEA
This not easy such my imagine.
I have class Setting and Setting.hbm.xml for mapping hibernate.
In this class:
<hibernate-mapping>
<class name="Setting" table="setting" lazy="false">
<id name="id" column="id" type="integer">
<generator class="increment" />
</id>
.....
</class>
<query name="select.setting">
from Setting as s where s.id = ? order by s.name
</query>
Now, when I call function
this.getHibernateTemplate().findByNamedQuery("select.setting", params);
This return error
org.springframework.orm.hibernate4.HibernateSystemException: Named query not known: select.setting; nested exception is org.hibernate.MappingException: Named query not known: select.setting
at org.springframework.orm.hibernate4.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:218) ~[spring-orm-4.1.6.RELEASE.jar:4.1.6.RELEASE]
at org.springframework.orm.hibernate4.HibernateTemplate.doExecute(HibernateTemplate.java:343) ~[spring-orm-4.1.6.RELEASE.jar:4.1.6.RELEASE]
at org.springframework.orm.hibernate4.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:308) ~[spring-orm-4.1.6.RELEASE.jar:4.1.6.RELEASE]
at org.springframework.orm.hibernate4.HibernateTemplate.findByNamedQuery(HibernateTemplate.java:933) ~[spring-orm-4.1.6.RELEASE.jar:4.1.6.RELEASE]
Please give advice about it.
You can give a try with this.
<query name="select.setting">
<![CDATA[from Setting as s where s.id = ? order by s.name]]>
</query>
The XML parser gets confused of you are not using CDATA tag.
CDATA is way of telling the framework that its a data which should not be interpreted as a markup.
Hence as #Lovababu mentioned, include the query inside CDATA tags:
<query name="select.setting">
<![CDATA[from Setting as s where s.id = ? order by s.name]]>
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 in the process of moving our database over to Oracle from SQL Server 2008 but cannot get MyBatis to work.
Given the following example:
UserMapper.xml (example)
<resultMap type="User" id="UserResult">
<id property="userId" column="userId"/>
<result property="firstName" column="firstName"/>
<result property="lastName" column="lastName"/>
</resultMap>
<select id="getUsers" statementType="CALLABLE" resultMap="UserResult">
{CALL GetUsers()}
</select>
UserDAO.java
public interface UserDAO {
public List<User> getUsers();
}
SQL Server procedure
CREATE PROCEDURE [dbo].[GetUsers]
AS
BEGIN
SET NOCOUNT ON;
SELECT userId, firstName, lastName
FROM Users
END
...works in SQL Server 2008. Can someone please explain to me how to call the Oracle procedure (that has the same name and columns as the SQL Server procedure above) from the UserMapper.xml and populate my User class with an Oracle cursor?
This is what I tried:
<resultMap type="User" id="UserResult">
<id property="userId" column="userId"/>
<result property="firstName" column="firstName"/>
<result property="lastName" column="lastName"/>
</resultMap>
<select id="getUsers" statementType="CALLABLE" resultMap="UserResult">
{CALL GetUsers(#{resultSet,mode=OUT,jdbcType=CURSOR,resultMap=UserResult})}
</select>
and I get this error:
Caused by: org.apache.ibatis.reflection.ReflectionException:
Could not set property 'resultSet' of 'class java.lang.Class'
with value 'oracle.jdbc.driver.OracleResultSetImpl#476d05dc'
Cause: org.apache.ibatis.reflection.ReflectionException:
There is no setter for property named 'resultSet' in 'class java.lang.Class'
Result map looks like this:
<resultMap id="UserResult" type="User">
<id property="userId" column="userId"/>
<result property="firstName" column="firstName"/>
<result property="lastName" column="lastName"/>
</resultMap>
In your select statement, change the parameter type to java.util.Map.
<select id="getUsers" statementType="CALLABLE" parameterType="java.util.Map">
{call GetUsers(#{users, jdbcType=CURSOR, javaType=java.sql.ResultSet, mode=OUT, resultMap=UserResult})}
</select>
Your mapper interface looks like this, it looks like you are currently calling this the DAO. The way I've done it in the past is to make a mapper interface that gets injected into the DAO and the DAO is what calls the methods on the mapper. Here's an example mapper interface:
public interface UserMapper {
public Object getUsers(Map<String, Object> params);
}
That mapper class would then get injected into a DAO class and make the call like this:
public List<User> getUsers() {
Map<String, Object> params = new HashMap<String, Object>();
ResultSet rs = null;
params.put("users", rs);
userMapper.getUsers(params);
return ((ArrayList<User>)params.get("users"));
}
Getting a resultset from Oracle 11 using MyBatis/iBATIS 3 is a real oddball process. It makes no sense to me, but it worked. My example is different, but you'll get the idea:
create or replace
PROCEDURE SP_GET_ALL_STORED_PROC (l_cursor out SYS_REFCURSOR) IS
BEGIN
open l_cursor for select account_id, totalLegs, born, weight, mammal, animal from copybittest;
END SP_GET_ALL_STORED_PROC;
Map map = new HashMap();
session.selectList("ibatis_3_test.selectProductAllOracleStoredProc", map);
List productList = (List) map.get("key");
<resultMap id="productResultMap" type="test.Product">
</resultMap>
<select id="selectProductAllOracleStoredProc" parameterType="java.util.Map" statementType="CALLABLE">
{call SP_GET_ALL_STORED_PROC(#{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet,resultMap=productResultMap})}
</select>
Just an addition to the clav's comment, Snoozy, you need to remove resultSet from
<select id="getUsers" statementType="CALLABLE" resultMap="UserResult">
{CALL GetUsers(# {resultSet,mode=OUT,jdbcType=CURSOR,resultMap=UserResult})}
and change that to "key" as in:
<select id="getUsers" statementType="CALLABLE" resultMap="UserResult">
{CALL GetUsers(#{key,mode=OUT,jdbcType=CURSOR,resultMap=UserResult})}
</select>
I hope that was helpful.
I also got the same error.
Caused by: org.apache.ibatis.reflection.ReflectionException: There is
no setter for property named 'columnNames' in 'class java.lang.Class'
in mapper.java getSearchResult(searchCriteriaVO vo)
in mapper.xml
#{userId, mode=IN, jdbcType=VARCHAR},
#{resultList, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=inquiryResult},
where inquiryResult is defined as
<resultMap type="java.util.LinkedHashMap" id="inquiryResult">
<result property="name" jdbcType="VARCHAR" javaType="String" column="name"/>
Struggled for a day
But when I debugged, it's a simple mistake that I made. My Value Object was passing as a null to my mapper class. The query was getting executed even my VO was null because mybatis passing values as null. But when mybatis was trying to set the result to my VO, since it is null, it was throwing the above error.
Hope this is useful info