1 package org.hibernate.engine.query;
2
3 import java.io.Serializable;
4 import java.sql.Connection;
5 import java.sql.PreparedStatement;
6 import java.sql.SQLException;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.Map;
10
11 import org.apache.commons.logging.Log;
12 import org.apache.commons.logging.LogFactory;
13 import org.hibernate.HibernateException;
14 import org.hibernate.QueryException;
15 import org.hibernate.action.BulkOperationCleanupAction;
16 import org.hibernate.engine.QueryParameters;
17 import org.hibernate.engine.SessionFactoryImplementor;
18 import org.hibernate.engine.SessionImplementor;
19 import org.hibernate.engine.TypedValue;
20 import org.hibernate.event.EventSource;
21 import org.hibernate.exception.JDBCExceptionHelper;
22 import org.hibernate.loader.custom.SQLCustomQuery;
23 import org.hibernate.pretty.Formatter;
24 import org.hibernate.type.Type;
25 import org.hibernate.util.ArrayHelper;
26
27 /**
28 * Defines a query execution plan for a native-SQL query.
29 *
30 * @author <a href="mailto:steve@hibernate.org">Steve Ebersole </a>
31 */
32 public class NativeSQLQueryPlan implements Serializable {
33 private final String sourceQuery;
34
35 private final SQLCustomQuery customQuery;
36
37 private static final Log log = LogFactory.getLog(NativeSQLQueryPlan.class);
38
39 public NativeSQLQueryPlan(NativeSQLQuerySpecification specification,
40 SessionFactoryImplementor factory) {
41 this.sourceQuery = specification.getQueryString();
42
43 customQuery = new SQLCustomQuery( specification.getSqlQueryReturns(),
44 specification.getSqlQueryScalarReturns(), specification
45 .getQueryString(), specification.getQuerySpaces(),
46 factory );
47 }
48
49 public String getSourceQuery() {
50 return sourceQuery;
51 }
52
53 public SQLCustomQuery getCustomQuery() {
54 return customQuery;
55 }
56
57 private int[] getNamedParameterLocs(String name) throws QueryException {
58 Object loc = customQuery.getNamedParameterBindPoints().get( name );
59 if ( loc == null ) {
60 throw new QueryException(
61 "Named parameter does not appear in Query: " + name,
62 customQuery.getSQL() );
63 }
64 if ( loc instanceof Integer ) {
65 return new int[] { ((Integer) loc ).intValue() };
66 }
67 else {
68 return ArrayHelper.toIntArray( (List) loc );
69 }
70 }
71
72 /**
73 * Bind positional parameter values to the <tt>PreparedStatement</tt>
74 * (these are parameters specified by a JDBC-style ?).
75 */
76 private int bindPositionalParameters(final PreparedStatement st,
77 final QueryParameters queryParameters, final int start,
78 final SessionImplementor session) throws SQLException,
79 HibernateException {
80
81 final Object[] values = queryParameters
82 .getFilteredPositionalParameterValues();
83 final Type[] types = queryParameters
84 .getFilteredPositionalParameterTypes();
85 int span = 0;
86 for (int i = 0; i < values.length; i++) {
87 types[i].nullSafeSet( st, values[i], start + span, session );
88 span += types[i].getColumnSpan( session.getFactory() );
89 }
90 return span;
91 }
92
93 /**
94 * Bind named parameters to the <tt>PreparedStatement</tt>. This has an
95 * empty implementation on this superclass and should be implemented by
96 * subclasses (queries) which allow named parameters.
97 */
98 private int bindNamedParameters(final PreparedStatement ps,
99 final Map namedParams, final int start,
100 final SessionImplementor session) throws SQLException,
101 HibernateException {
102
103 if ( namedParams != null ) {
104 // assumes that types are all of span 1
105 Iterator iter = namedParams.entrySet().iterator();
106 int result = 0;
107 while ( iter.hasNext() ) {
108 Map.Entry e = (Map.Entry) iter.next();
109 String name = (String) e.getKey();
110 TypedValue typedval = (TypedValue) e.getValue();
111 int[] locs = getNamedParameterLocs( name );
112 for (int i = 0; i < locs.length; i++) {
113 if ( log.isDebugEnabled() ) {
114 log.debug( "bindNamedParameters() "
115 + typedval.getValue() + " -> " + name + " ["
116 + (locs[i] + start ) + "]" );
117 }
118 typedval.getType().nullSafeSet( ps, typedval.getValue(),
119 locs[i] + start, session );
120 }
121 result += locs.length;
122 }
123 return result;
124 }
125 else {
126 return 0;
127 }
128 }
129
130 protected void coordinateSharedCacheCleanup(SessionImplementor session) {
131 BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, getCustomQuery().getQuerySpaces() );
132
133 action.init();
134
135 if ( session.isEventSource() ) {
136 ( ( EventSource ) session ).getActionQueue().addAction( action );
137 }
138 }
139
140 public int performExecuteUpdate(QueryParameters queryParameters,
141 SessionImplementor session) throws HibernateException {
142
143 coordinateSharedCacheCleanup( session );
144
145 if(queryParameters.isCallable()) {
146 throw new IllegalArgumentException("callable not yet supported for native queries");
147 }
148
149 int result = 0;
150 PreparedStatement ps = null;
151 try {
152 queryParameters.processFilters( this.customQuery.getSQL(),
153 session );
154 String sql = queryParameters.getFilteredSQL();
155
156 ps = session.getBatcher().prepareStatement( sql );
157
158 try {
159 int col = 1;
160 col += bindPositionalParameters( ps, queryParameters, col,
161 session );
162 col += bindNamedParameters( ps, queryParameters
163 .getNamedParameters(), col, session );
164 result = ps.executeUpdate();
165 }
166 finally {
167 if ( ps != null ) {
168 session.getBatcher().closeStatement( ps );
169 }
170 }
171 }
172 catch (SQLException sqle) {
173 throw JDBCExceptionHelper.convert( session.getFactory()
174 .getSQLExceptionConverter(), sqle,
175 "could not execute native bulk manipulation query", this.sourceQuery );
176 }
177
178 return result;
179 }
180
181 }