Mapping a nested Oracle CURSOR (ResultSet) with myBatis
I made an example to show how it works.
package models contains two classes:
public class Result { public int start_from; public List<Model> models;}public class Model { public int a; public String b;}
stored procedure
CREATE OR REPLACE PROCEDURE get_data( p_start IN NUMBER , p_cur OUT SYS_REFCURSOR)ISBEGIN OPEN p_cur FOR SELECT p_start a,'abc' b FROM dual UNION ALL SELECT p_start + 1,'cde' FROM dual UNION ALL SELECT p_start + 2,'xyz' FROM dual;END;
mybatis-config.xml (you must provide the URL for Database)
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="oracle.jdbc.OracleDriver"/> <property name="url" value="${set_the_url}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mybatis-mapper.xml"/> </mappers></configuration>
mybatis-mapper.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <resultMap id="map_res_4" type="models.Model"> <result property="a" column="a"/> <result property="b" column="b"/> </resultMap> <parameterMap id="map_par_4" type="models.Result"> <parameter property="start_from" jdbcType="INTEGER" mode="IN" /> <parameter property="models" jdbcType="CURSOR" mode="OUT" resultMap="map_res_4" /> </parameterMap> <select id="select_4" parameterMap="map_par_4" statementType="CALLABLE"> {CALL get_data(?, ?)} </select></mapper>
And the sample of calling the procedure get_data
with mybatis:
You are to notice that the selectOne
method returns null, because we execute a callable statement. All interaction with the procedure call goes using the second parameter: we pass start_from
and receive models
as fields of Result object (MyBatis can get and set them through reflection). So the Result object is the same before and after the method call: you can even make the fields private (here I left them public to keep the code shorter).
import models.Result;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;import java.io.Reader;/** * */public class Main { private static SqlSessionFactory sessionFactory = null; private static String CONFIGURATION_FILE = "mybatis-config.xml"; static { try { Reader reader = Resources.getResourceAsReader(CONFIGURATION_FILE); sessionFactory = new SqlSessionFactoryBuilder().build(reader); } catch (IOException e) { e.printStackTrace(); } } public static void main(String... args) { SqlSession session = sessionFactory.openSession(); Result res = new Result(); res.start_from = 5; Object obj = session.selectOne("select_4", res); // `obj` must be NULL // `res` contains all the results of Oracle procedure call }}
from ibatis examples:
<parameter property="result" javaType="java.sql.ResultSet" jdbcType="ORACLECURSOR" mode="OUT"/>
the difference from you code:
- javaType was specified - jdbcType = ORACLECURSOR
see the examples from iBatis Oracle REF CURSOR
<sqlMap namespace="KOMUNIKA_REPORT"> <resultMap id="BaseResultMap" class="javaapplication4.StockAreaAndWarehouse" > <result column="PRODUCT_CODE" property="productCode" /> <result column="PRODUCT_NAME" property="productName" /> <result column="INCOMING" property="incoming" /> <result column="UNIT_SOLD" property="unitSold" /> <result column="TOTAL_STOCK" property="totalStock" /> </resultMap> <parameterMap id="resultMap" class="java.util.Map"> <parameter property="result" javaType="java.sql.ResultSet" jdbcType="ORACLECURSOR" mode="OUT"/> </parameterMap> <procedure id="selectStockAreaAndWarehouse" parameterMap="resultMap" resultMap="BaseResultMap" > { call KOMUNIKA.LP_STOCK_AREA_WAREHOUSE(?) } </procedure> </sqlMap>
<resultMap id="userDataResultMap" type="TestUserData"> <id property="userid" column="userid" /> <result property="firstName" column="firstName"/> <result property="lastName" column="lastName"/> <result property="zip" column="zip"/> <result property="summary" column="summary"/> <result property="specialities" column="specialities"/> <result property="isActive" column="isActive"/> <result property="country" column="country"/> <result property="platform" column="platforms"/> </resultMap><select id="getFullPublicData" statementType="CALLABLE" parameterType="User" >{call p_user_public_data(#{userId},#{userDataList,mode=OUT,jdbcType=CURSOR,javaType=java.sql.ResultSet, resultMap=com.test.data.UserPublicViewMapper.userDataResultMap},#{noOfConnections,mode=OUT,jdbcType=NUMERIC,javaType=int},#{noOfRecommendations,mode=OUT,jdbcType=NUMERIC,javaType=int})}</select>