001: package org.hibernate.engine.query;
002:
003: import java.io.Serializable;
004: import java.sql.PreparedStatement;
005: import java.sql.SQLException;
006: import java.util.Iterator;
007: import java.util.List;
008: import java.util.Map;
009:
010: import org.apache.commons.logging.Log;
011: import org.apache.commons.logging.LogFactory;
012: import org.hibernate.HibernateException;
013: import org.hibernate.QueryException;
014: import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
015: import org.hibernate.action.BulkOperationCleanupAction;
016: import org.hibernate.engine.QueryParameters;
017: import org.hibernate.engine.SessionFactoryImplementor;
018: import org.hibernate.engine.SessionImplementor;
019: import org.hibernate.engine.TypedValue;
020: import org.hibernate.event.EventSource;
021: import org.hibernate.exception.JDBCExceptionHelper;
022: import org.hibernate.loader.custom.sql.SQLCustomQuery;
023: import org.hibernate.type.Type;
024: import org.hibernate.util.ArrayHelper;
025:
026: /**
027: * Defines a query execution plan for a native-SQL query.
028: *
029: * @author Steve Ebersole
030: */
031: public class NativeSQLQueryPlan implements Serializable {
032: private final String sourceQuery;
033:
034: private final SQLCustomQuery customQuery;
035:
036: private static final Log log = LogFactory
037: .getLog(NativeSQLQueryPlan.class);
038:
039: public NativeSQLQueryPlan(
040: NativeSQLQuerySpecification specification,
041: SessionFactoryImplementor factory) {
042: this .sourceQuery = specification.getQueryString();
043:
044: customQuery = new SQLCustomQuery(
045: specification.getQueryString(), specification
046: .getQueryReturns(), specification
047: .getQuerySpaces(), factory);
048: }
049:
050: public String getSourceQuery() {
051: return sourceQuery;
052: }
053:
054: public SQLCustomQuery getCustomQuery() {
055: return customQuery;
056: }
057:
058: private int[] getNamedParameterLocs(String name)
059: throws QueryException {
060: Object loc = customQuery.getNamedParameterBindPoints()
061: .get(name);
062: if (loc == null) {
063: throw new QueryException(
064: "Named parameter does not appear in Query: " + name,
065: customQuery.getSQL());
066: }
067: if (loc instanceof Integer) {
068: return new int[] { ((Integer) loc).intValue() };
069: } else {
070: return ArrayHelper.toIntArray((List) loc);
071: }
072: }
073:
074: /**
075: * Bind positional parameter values to the <tt>PreparedStatement</tt>
076: * (these are parameters specified by a JDBC-style ?).
077: */
078: private int bindPositionalParameters(final PreparedStatement st,
079: final QueryParameters queryParameters, final int start,
080: final SessionImplementor session) throws SQLException,
081: HibernateException {
082:
083: final Object[] values = queryParameters
084: .getFilteredPositionalParameterValues();
085: final Type[] types = queryParameters
086: .getFilteredPositionalParameterTypes();
087: int span = 0;
088: for (int i = 0; i < values.length; i++) {
089: types[i].nullSafeSet(st, values[i], start + span, session);
090: span += types[i].getColumnSpan(session.getFactory());
091: }
092: return span;
093: }
094:
095: /**
096: * Bind named parameters to the <tt>PreparedStatement</tt>. This has an
097: * empty implementation on this superclass and should be implemented by
098: * subclasses (queries) which allow named parameters.
099: */
100: private int bindNamedParameters(final PreparedStatement ps,
101: final Map namedParams, final int start,
102: final SessionImplementor session) throws SQLException,
103: HibernateException {
104:
105: if (namedParams != null) {
106: // assumes that types are all of span 1
107: Iterator iter = namedParams.entrySet().iterator();
108: int result = 0;
109: while (iter.hasNext()) {
110: Map.Entry e = (Map.Entry) iter.next();
111: String name = (String) e.getKey();
112: TypedValue typedval = (TypedValue) e.getValue();
113: int[] locs = getNamedParameterLocs(name);
114: for (int i = 0; i < locs.length; i++) {
115: if (log.isDebugEnabled()) {
116: log.debug("bindNamedParameters() "
117: + typedval.getValue() + " -> " + name
118: + " [" + (locs[i] + start) + "]");
119: }
120: typedval.getType().nullSafeSet(ps,
121: typedval.getValue(), locs[i] + start,
122: session);
123: }
124: result += locs.length;
125: }
126: return result;
127: } else {
128: return 0;
129: }
130: }
131:
132: protected void coordinateSharedCacheCleanup(
133: SessionImplementor session) {
134: BulkOperationCleanupAction action = new BulkOperationCleanupAction(
135: session, getCustomQuery().getQuerySpaces());
136:
137: action.init();
138:
139: if (session.isEventSource()) {
140: ((EventSource) session).getActionQueue().addAction(action);
141: }
142: }
143:
144: public int performExecuteUpdate(QueryParameters queryParameters,
145: SessionImplementor session) throws HibernateException {
146:
147: coordinateSharedCacheCleanup(session);
148:
149: if (queryParameters.isCallable()) {
150: throw new IllegalArgumentException(
151: "callable not yet supported for native queries");
152: }
153:
154: int result = 0;
155: PreparedStatement ps;
156: try {
157: queryParameters.processFilters(this .customQuery.getSQL(),
158: session);
159: String sql = queryParameters.getFilteredSQL();
160:
161: ps = session.getBatcher().prepareStatement(sql);
162:
163: try {
164: int col = 1;
165: col += bindPositionalParameters(ps, queryParameters,
166: col, session);
167: col += bindNamedParameters(ps, queryParameters
168: .getNamedParameters(), col, session);
169: result = ps.executeUpdate();
170: } finally {
171: if (ps != null) {
172: session.getBatcher().closeStatement(ps);
173: }
174: }
175: } catch (SQLException sqle) {
176: throw JDBCExceptionHelper.convert(session.getFactory()
177: .getSQLExceptionConverter(), sqle,
178: "could not execute native bulk manipulation query",
179: this.sourceQuery);
180: }
181:
182: return result;
183: }
184:
185: }
|