package org.ovirt.engine.core.dal.dbbroker; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.ovirt.engine.core.dao.BaseDAODbFacade; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils; import org.springframework.jdbc.core.simple.ParameterizedRowMapper; import org.springframework.jdbc.core.simple.SimpleJdbcCall; public class SimpleJdbcCallsHandler { private static SimpleJdbcCallsHandler instance = new SimpleJdbcCallsHandler(); private ConcurrentMap<String, SimpleJdbcCall> callsMap = new ConcurrentHashMap<String, SimpleJdbcCall>(); private DbEngineDialect dialect; private JdbcTemplate template; public void setDbEngineDialect(DbEngineDialect dialect) { this.dialect = dialect; } public void setJdbcTemplate(JdbcTemplate template) { this.template = template; } private interface CallCreator { SimpleJdbcCall createCall(); } public Map<String, Object> executeModification(final String procedureName, final MapSqlParameterSource paramSource) { return executeImpl(procedureName, paramSource, createCallForModification(procedureName)); } public <T> T executeRead(final String procedureName, final ParameterizedRowMapper<T> mapper, final MapSqlParameterSource parameterSource) { List<T> results = executeReadList(procedureName, mapper, parameterSource); return results.isEmpty() ? null : results.get(0); } @SuppressWarnings("unchecked") public <T> List<T> executeReadList(final String procedureName, final ParameterizedRowMapper<T> mapper, final MapSqlParameterSource parameterSource) { Map<String, Object> resultsMap = executeReadAndReturnMap(procedureName, mapper, parameterSource); List<?> resultList = (List<?>) (resultsMap .get(BaseDAODbFacade.RETURN_VALUE_PARAMETER)); return (resultList != null) ? (List<T>) resultList : null; } public <T> Map<String, Object> executeReadAndReturnMap(final String procedureName, final ParameterizedRowMapper<T> mapper, final MapSqlParameterSource parameterSource) { Map<String, Object> resultsMap = executeImpl(procedureName, parameterSource, createCallForRead(procedureName, mapper, parameterSource)); return resultsMap; } private CallCreator createCallForRead(final String procedureName, final ParameterizedRowMapper<?> mapper, final MapSqlParameterSource parameterSource) { return new CallCreator() { @Override public SimpleJdbcCall createCall() { SimpleJdbcCall call = (SimpleJdbcCall) dialect.createJdbcCallForQuery(template).withProcedureName(procedureName); call.returningResultSet(BaseDAODbFacade.RETURN_VALUE_PARAMETER, mapper); // Pass mapper information (only parameter names) in order to supply all the needed // metadata information for compilation. call.getInParameterNames().addAll( SqlParameterSourceUtils.extractCaseInsensitiveParameterNames(parameterSource).keySet()); return call; } }; } private CallCreator createCallForModification(final String procedureName) { return new CallCreator() { @Override public SimpleJdbcCall createCall() { return new SimpleJdbcCall(template).withProcedureName(procedureName); } }; } private Map<String, Object> executeImpl(String procedureName, MapSqlParameterSource paramsSource, CallCreator callCreator) { SimpleJdbcCall call = callsMap.get(procedureName); if (call == null) { // Creates a simple jdbc call object, and // compile its metadata. // The if block is not atomic - Worst case a few // information schema calls will be made. // Metada compilation is done here, in order to save it // the first time we actually use the stored procedure as // it may yield some concurrency issues. call = callCreator.createCall(); call.compile(); callsMap.putIfAbsent(procedureName, call); } return call.execute(paramsSource); } }