绵弟的SSM项目使用的是Oracle数据库,会有用到PL/SQL中的存储过程和函数。不知道是不是因为Oracle比较难学还是没市场,网上的资料好少。看了几篇博客,我学试着去调用一下,还有有困难的。
- 我的mapper文件是这样的:
<!-- 调用存储过程,插入商品并返回数据库中生成的id -->
<insert id="createGood" parameterType="Good" statementType="CALLABLE">
call proc_add_good(
#{user_id, jdbcType=NUMERIC},
#{name, jdbcType=VARCHAR},
#{description,jdbcType=VARCHAR},
#{depreciation, jdbcType=NUMERIC},
#{price, jdbcType=NUMERIC},
#{status, jdbcType=NUMERIC},
#{photo, jdbcType=NUMERIC},
#{goods_id, mode=OUT, jdbcType=NUMERIC, javaType=Integer}
)
</insert>
- 存储过程:
CREATE OR REPLACE
PROCEDURE proc_add_good (user_id NUMERIC, name VARCHAR, description VARCHAR, depreciation NUMERIC,
price NUMERIC, status NUMERIC, photo VARCHAR, goods_id OUT NUMERIC)
AS
v_inner_date DATE;
v_goods_id NUMERIC;
BEGIN
v_inner_date := CURRENT_DATE;
goods_id:=seq_good.nextval;
INSERT INTO M_GOOD
VALUES (
goods_id,
user_id,
name,
description,
depreciation,
price,
status,
v_inner_date,
photo
);
END;
一开始就出现了错误:
Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: java.sql.SQLException: 索引中丢失 IN 或 OUT 参数:: 8
### The error may exist in cn/jnu/core/dao/GoodDao.xml
### The error may involve cn.jnu.core.dao.GoodDao.createGood-Inline
### The error occurred while setting parameters
### SQL: call proc_add_good( ?, ?, ?, ?, ?, ?, ?, ? )
### Cause: java.sql.SQLException: 索引中丢失 IN 或 OUT 参数:: 8
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:199)
at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:184)
at test.GoodDaoTest.test1(GoodDaoTest.java:78)
at test.GoodDaoTest.main(GoodDaoTest.java:91)
Caused by: java.sql.SQLException: 索引中丢失 IN 或 OUT 参数:: 8
at oracle.jdbc.driver.OraclePreparedStatement.processCompletedBindRow(OraclePreparedStatement.java:1752)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3444)
at oracle.jdbc.driver.OraclePreparedStatement.execute(OraclePreparedStatement.java:3550)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.execute(OraclePreparedStatementWrapper.java:1374)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:47)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:74)
at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:50)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:197)
... 3 more
我是反复对了我的存储过程和调用的的参数类型和顺序,后来才发现是映射文件中statementType="CALLABLE"忘记加了。但是一般出现这个错误的都是参数的类型不对应,又或是顺序搞错了,mybatis还会很人性化的指出是第几个参数错误。
statementType有三种:statement(默认)、prestatement和callable。
callable对应的就是存储过程和函数。
然后就是测试:
public static void test1() {
SqlSession session = MybatisUtils.getSqlSession();
Good good = new Good();
good.setUser_id(14);
good.setName("棒棒");
good.setDescription("棒棒哒!");
good.setPrice(100.0);
good.setDepreciation(99);
good.setStatus(1);
int rows = session.insert("cn.jnu.core.dao.GoodDao.createGood", good);
System.out.println(rows);
System.out.println(good.getGoods_id());
session.commit();
session.close();
}
结果
- 然后我将mapper文件中的<insert>标签改为<select>:
<!-- 调用存储过程,插入商品并返回数据库中生成的id -->
<select id="createGood" parameterType="Good" statementType="CALLABLE">
call proc_add_good(
#{user_id, jdbcType=NUMERIC},
#{name, jdbcType=VARCHAR},
#{description,jdbcType=VARCHAR},
#{depreciation, jdbcType=NUMERIC},
#{price, jdbcType=NUMERIC},
#{status, jdbcType=NUMERIC},
#{photo, jdbcType=NUMERIC},
#{goods_id, mode=OUT, jdbcType=NUMERIC, javaType=Integer}
)
</select>
结果
结果也是可以执行,我又测试了<delete>和<update>也都是OK的。然后我就觉得标签可能是没有影响的。
- 我又尝试改一下SqlSession调用的方法:
public static void test1() {
SqlSession session = MybatisUtils.getSqlSession();
Good good = new Good();
good.setUser_id(14);
good.setName("棒棒");
good.setDescription("棒棒哒!");
good.setPrice(100.0);
good.setDepreciation(99);
good.setStatus(1);
int rows = session.selectOne("cn.jnu.core.dao.GoodDao.createGood", good);
System.out.println(rows);
System.out.println(good.getGoods_id());
session.commit();
session.close();
}
<!-- 调用存储过程,插入商品并返回数据库中生成的id -->
<select id="createGood" parameterType="Good" statementType="CALLABLE">
call proc_add_good(
#{user_id, jdbcType=NUMERIC},
#{name, jdbcType=VARCHAR},
#{description,jdbcType=VARCHAR},
#{depreciation, jdbcType=NUMERIC},
#{price, jdbcType=NUMERIC},
#{status, jdbcType=NUMERIC},
#{photo, jdbcType=NUMERIC},
#{goods_id, mode=OUT, jdbcType=NUMERIC, javaType=Integer}
)
</select>
结果标签中update和delete都OK,只有select不行,就算对应了,也会报出空指针:
结果
- 所以当SSM+Oracle联合测试的时候,调用的方法应该是和标签指定的一样,insert、update和delete调用的时候是返回的是影响的行数,而select不管是selectOne还是selectList都是有结果集返回的,需要涉及到游标,所以使用时就会返回空指针,自动调用时不要用<select>。
- 还有一点要注意的就是调用过程返回值在参数里,方法的返回值是-1,我也不知道为什么,可能以后得看看源码吧。