/*
* JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jef.database;
import java.lang.reflect.Type;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.List;
import javax.persistence.PersistenceException;
import jef.common.log.LogUtil;
import jef.database.Session.PopulateStrategy;
import jef.database.jdbc.result.ResultSetImpl;
import jef.database.query.OutParam;
import jef.database.wrapper.populator.Transformer;
/**
* 存储过程调用对象
*
* @author Jiyi
*
*/
public class NativeCall {
private static final int ORACLE_CURSOR = -10;
/**
* 数据库
*/
private OperateTarget db;
/**
* 结果拼装策略
*/
private PopulateStrategy[] strategies = { PopulateStrategy.PLAIN_MODE };
/**
* 存储过程名称
*/
private String name;
private CallableStatement st;
private String sql;
private Type[] params;
private Object[] outParamCache;
private boolean anonymous;
/**
* 是否为匿名存储过程
*
* @return true if the procedure is anonymous.
*/
public boolean isAnonymousProcedure() {
return anonymous;
}
/*
* 构造
*/
NativeCall(OperateTarget db, String name, Type[] params, boolean anonymous){
this.db = db;
this.name = name;
this.params = params;
this.anonymous = anonymous;
if (anonymous) {
sql = name;
} else {
StringBuilder sb = new StringBuilder();
sb.append("{call ").append(name).append("(");
if (params.length > 0) {
sb.append('?');
for (int i = 1; i < params.length; i++) {
sb.append(",?");
}
}
sb.append(")}");
sql = sb.toString();
}
try{
init();
}catch(SQLException e){
throw new PersistenceException(e);
}
}
private void init() throws SQLException {
st = db.prepareCall(sql);
// 定义出参
for (int i = 0; i < params.length; i++) {
if (params[i] instanceof OutParam) {
OutParam OutParam = (OutParam) params[i];
if (OutParam.isList()) {// 游标
st.registerOutParameter(i + 1, ORACLE_CURSOR);
} else {// 常量
st.registerOutParameter(i + 1, getOracleTypeByJava(OutParam.getType()));
}
}
}
}
// 根据入参的java类返回出参的定义类型
private int getOracleTypeByJava(Class<?> type) {
if (Number.class.isAssignableFrom(type)) {
return Types.DECIMAL;
} else if (CharSequence.class.isAssignableFrom(type)) {
return Types.VARCHAR;
} else if (type == java.sql.Time.class) {
return Types.TIME;
} else if (type == java.sql.Timestamp.class || type == java.util.Date.class) {
return Types.TIMESTAMP;
} else if (type == java.sql.Date.class) {
return Types.DATE;
} else if (type == java.sql.Blob.class) {
return Types.BLOB;
} else if (type == java.sql.Clob.class) {
return Types.CLOB;
} else if (type == Boolean.class) {
return Types.BOOLEAN;
} else {
return Types.VARCHAR;
}
}
/**
* 判断对象是否已经关闭
* @return true if closed
*/
public boolean isClosed(){
return st==null;
}
private void assertOpen(boolean reopen) {
if (st == null) {
if(reopen){
try{
init();
}catch(SQLException e){
throw new PersistenceException(e);
}
}else{
throw new RuntimeException("The call is closed!");
}
}
}
/**
* 获得调用的存储过程名称,如果是匿名块直接返回SQL语句
* @return
*/
public String getName() {
return name;
}
/**
* 执行
* @param 存储过程调用参数。此处不指定也可。
* @return <code>true</code> if the first result is a <code>ResultSet</code>
* object; <code>false</code> if the first result is an update count
* or there is no result
*/
public boolean execute(Object... objs) {
assertOpen(true);
for (int i = 0; i < objs.length; i++) {
Object value = objs[i];
innerSet(i + 1, value);
}
if(ORMConfig.getInstance().isDebugMode()){
LogUtil.show("Exccuting:"+sql+Arrays.toString(objs));
}
this.outParamCache=null;
try {
boolean result = st.execute();
cacheOutParams(true);// If there is no cursor open, automatically close this call.
return result;
} catch (SQLException e) {
close();
throw new PersistenceException(e.getMessage() + " " + e.getSQLState(), e);
}
}
/**
* 获得目前设置的结果转换策略
* @return
*/
public PopulateStrategy[] getStrategies() {
return strategies;
}
/**
* 设置结果拼装策略,可以同时使用多个选项策略
*
* @param strategies
* 拼装策略
*
* @see jef.database.Session.PopulateStrategy
*/
public void setStrategies(PopulateStrategy... strategies) {
this.strategies = strategies;
}
/**
* 关闭结果集(游标等)
*/
public void close() {
if (st != null) {
try {
st.close();
} catch (SQLException e) {
LogUtil.exception(e);
}
db.releaseConnection();
st = null;
}
}
/*
* 检查并转换参数类型
*/
private Object checkAndConvert(Class<?> clz, Object value) {
return value;
}
private Object checkAndAdjustOut(OutParam outType, Object value) {
if (outType.isList()) {
Class<?> clz = outType.getType();
ResultSet rs = (ResultSet) value;
try {
return db.populateResultSet(new ResultSetImpl(rs, db.getProfile()), null, new Transformer(clz, strategies));
} catch (SQLException e) {
throw new PersistenceException(e.getMessage() + " " + e.getSQLState(), e);
} finally {
try {
rs.close();
} catch (Exception e) {
LogUtil.exception(e);
}
}
} else {//FIXME 非列表的场合没有返回值啊
// Class clz=outType.getType();
return value;
}
}
private void innerSet(int i, Object obj) {
Type t = params[i - 1];
if (t instanceof Class) {
try {
Object value = checkAndConvert((Class<?>) t, obj);
st.setObject(i, value);
} catch (SQLException e) {
throw new PersistenceException(e.getMessage() + " " + e.getSQLState(), e);
}
} else {
throw new RuntimeException("The param of index " + i + " is not a input param!");
}
}
// /**
// * 传入第n个参数,n从1开始
// *
// * @param index 序号,从1开始
// * @param obj 参数值
// */
// public void setParameter(int i, Object obj) {
// assertOpen(false);
// if (params.length < i) {
// throw new RuntimeException("the index " + i + " is exceed the max param number!");
// }
// innerSet(i, obj);
// }
/**
* 设置多个参数,第一个参数对应1,第二个对应2,以此类推
*
* @param objs
*/
public void setParameters(Object... objs) {
assertOpen(false);
for (int i = 0; i < objs.length; i++) {
Object value = objs[i];
innerSet(i + 1, value);
}
}
/**
* 得到出参,并且转换为指定类型的List,该类型必须和NativeCall对象构造时的类型一致
*
* @param i 参数序号,从1开始
* @param clz 需要转换的类型
* @return 存储过程传出参数(列表)
*/
@SuppressWarnings("unchecked")
public <T> List<T> getOutParameterAsList(int i, Class<T> clz) {
Object obj = getOutParameter(i);
if (obj instanceof List) {
return (List<T>) obj;
} else {
throw new ClassCastException("The output parameter is not a list type!");
}
}
/**
* 得到出参,并且转换为指定类型
*
* @param i 参数序号,从1开始
* @param clz 需要转换的类型,该类型必须和NativeCall对象构造时的类型一致
* @return 存储过程传出参数
*/
@SuppressWarnings("unchecked")
public <T> T getOutParameterAs(int i, Class<T> clz) {
Object obj = getOutParameter(i);
return (T) obj;
}
/**
* 获取存储过程的传出参数,
*
* @param i
* 参数序号,从1开始
* @return
* 存储过程的传出参数,参数类型在构造时确定。
*/
public Object getOutParameter(int i) {
if (params.length < i) {
throw new RuntimeException("the index " + i + " is exceed the max param number!");
}
Type t = params[i - 1];
if (t instanceof OutParam) {
if (outParamCache != null) {
return checkAndAdjustOut((OutParam) t, outParamCache[i - 1]);
}
assertOpen(false);
try {
Object value = st.getObject(i);
return checkAndAdjustOut((OutParam) t, value);
} catch (SQLException e) {
throw new PersistenceException(e.getMessage() + " " + e.getSQLState(), e);
}
} else {
throw new RuntimeException("The param of index " + i + " is not a output param!");
}
}
private void cacheOutParams(boolean close) throws SQLException {
if (params.length > 0) {
Object[] cache = new Object[params.length];
for (int i = 0; i < params.length; i++) {
Type t = params[i];
if (t instanceof OutParam) {
if (((OutParam) t).isList()) {
outParamCache = null;
return;
} else {
cache[i] = st.getObject(i + 1);
}
}
}
this.outParamCache = cache;
}
if(close)
close();
}
}