Calling Oracle stored procedures with MyBatis

2019-04-08 15:16发布

问题:

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'

回答1:

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"));
}


回答2:

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>


回答3:

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.



回答4:

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