0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.amber.manager;
0031:
0032: import com.caucho.amber.AmberException;
0033: import com.caucho.amber.cfg.ColumnResultConfig;
0034: import com.caucho.amber.cfg.EntityResultConfig;
0035: import com.caucho.amber.cfg.FieldResultConfig;
0036: import com.caucho.amber.cfg.SqlResultSetMappingConfig;
0037: import com.caucho.amber.entity.Entity;
0038: import com.caucho.amber.entity.EntityItem;
0039: import com.caucho.amber.expr.AmberExpr;
0040: import com.caucho.amber.expr.ArgExpr;
0041: import com.caucho.amber.expr.LoadEntityExpr;
0042: import com.caucho.amber.query.AbstractQuery;
0043: import com.caucho.amber.query.ResultSetImpl;
0044: import com.caucho.amber.query.SelectQuery;
0045: import com.caucho.amber.query.UserQuery;
0046: import com.caucho.amber.type.CalendarType;
0047: import com.caucho.amber.type.EntityType;
0048: import com.caucho.amber.type.UtilDateType;
0049: import com.caucho.ejb.EJBExceptionWrapper;
0050: import com.caucho.util.L10N;
0051:
0052: import javax.persistence.FlushModeType;
0053: import javax.persistence.NonUniqueResultException;
0054: import javax.persistence.NoResultException;
0055: import javax.persistence.Query;
0056: import javax.persistence.TemporalType;
0057: import java.lang.reflect.Constructor;
0058: import java.lang.reflect.Method;
0059: import java.sql.PreparedStatement;
0060: import java.sql.ResultSet;
0061: import java.sql.ResultSetMetaData;
0062: import java.sql.SQLException;
0063: import java.sql.Types;
0064: import java.util.*;
0065: import java.util.logging.Level;
0066: import java.util.logging.Logger;
0067:
0068: /**
0069: * The EJB query
0070: */
0071: public class QueryImpl implements Query {
0072: private static final L10N L = new L10N(QueryImpl.class);
0073: private static final Logger log = Logger.getLogger(QueryImpl.class
0074: .getName());
0075:
0076: private AbstractQuery _query;
0077: private UserQuery _userQuery;
0078:
0079: private AmberConnection _aConn;
0080: private int _firstResult;
0081:
0082: private int _currIndex;
0083:
0084: private FlushModeType _flushMode;
0085:
0086: // Constructor queries.
0087: private Class _primitiveColumns[];
0088:
0089: // Native queries.
0090: private String _nativeSql;
0091: private PreparedStatement _nativeStmt;
0092: private SqlResultSetMappingConfig _sqlResultSetMapping;
0093: private int _currEntityResult;
0094: private int _currColumnResult;
0095:
0096: /**
0097: * Creates a manager instance.
0098: */
0099: QueryImpl(AbstractQuery query, AmberConnection aConn) {
0100: _query = query;
0101: _aConn = aConn;
0102:
0103: _userQuery = new UserQuery(query);
0104: _userQuery.setSession(_aConn);
0105: }
0106:
0107: /**
0108: * Creates a manager instance.
0109: */
0110: QueryImpl(AmberConnection aConn) {
0111: _aConn = aConn;
0112: }
0113:
0114: /**
0115: * Execute the query and return as a List.
0116: */
0117: public List getResultList() {
0118: ResultSet rs = null;
0119:
0120: try {
0121: // jpa/0y1b
0122: if (_aConn.isInTransaction())
0123: _aConn.flush();
0124:
0125: Class constructorClass = null;
0126:
0127: if (isSelectQuery()) {
0128: if (!isNativeQuery()) {
0129: SelectQuery selectQuery = (SelectQuery) _query;
0130: constructorClass = selectQuery
0131: .getConstructorClass();
0132: }
0133: } else
0134: throw new IllegalStateException(
0135: L
0136: .l("javax.persistence.Query.getResultList() can only be applied to a SELECT statement"));
0137:
0138: Constructor constructor = null;
0139:
0140: ArrayList results = new ArrayList();
0141:
0142: rs = executeQuery();
0143:
0144: ResultSetMetaData metaData = null;
0145: int columnCount = -1;
0146:
0147: int n = 0;
0148:
0149: Object row[] = null;
0150:
0151: ArrayList columns = new ArrayList();
0152:
0153: while (rs.next()) {
0154: Object object = null;
0155:
0156: _currIndex = 1;
0157:
0158: if (n == 0) {
0159:
0160: try {
0161:
0162: metaData = rs.getMetaData();
0163:
0164: if (metaData != null)
0165: columnCount = metaData.getColumnCount();
0166:
0167: } catch (Exception ex) {
0168: // Below, we work around if DB is not able
0169: // to retrieve result set meta data. jpa/0t00
0170: metaData = null;
0171: }
0172:
0173: if (columnCount <= 0)
0174: columnCount = 10000;
0175:
0176: for (int i = 1; i <= columnCount; i++) {
0177:
0178: int columnType = -1;
0179:
0180: try {
0181: columnType = metaData.getColumnType(i);
0182: } catch (Exception ex) {
0183: }
0184:
0185: try {
0186:
0187: if (isNativeQuery()) {
0188: ArrayList<EntityResultConfig> entityResults = _sqlResultSetMapping
0189: .getEntityResults();
0190:
0191: if (_currEntityResult == entityResults
0192: .size()) {
0193:
0194: ArrayList<ColumnResultConfig> columnResults = _sqlResultSetMapping
0195: .getColumnResults();
0196:
0197: if (_currColumnResult == columnResults
0198: .size())
0199: break;
0200: }
0201:
0202: object = getInternalNative(rs);
0203: } else {
0204: object = getInternalObject(rs,
0205: columnType);
0206: }
0207:
0208: columns.add(object);
0209:
0210: } catch (IndexOutOfBoundsException ex1) {
0211: break;
0212: }
0213: // catch (Exception ex2) {
0214: // break;
0215: // }
0216: }
0217:
0218: n = columns.size();
0219: row = columns.toArray();
0220:
0221: if (constructorClass != null) {
0222:
0223: _primitiveColumns = new Class[row.length];
0224:
0225: StringBuilder argValues = new StringBuilder();
0226:
0227: try {
0228: // jpa/11a4, jpa/11a5
0229:
0230: boolean isFirst = true;
0231:
0232: for (int i = 0; i < n; i++) {
0233: if (isFirst)
0234: isFirst = false;
0235: else
0236: argValues.append(", ");
0237:
0238: argValues.append(row[i]);
0239: }
0240:
0241: Constructor ctors[] = constructorClass
0242: .getDeclaredConstructors();
0243:
0244: ArrayList<Constructor> validConstructors = new ArrayList<Constructor>();
0245:
0246: for (int j = 0; j < ctors.length; j++) {
0247:
0248: Class paramTypes[] = ctors[j]
0249: .getParameterTypes();
0250:
0251: if (paramTypes.length != row.length)
0252: continue;
0253:
0254: boolean isValid = true;
0255:
0256: for (int k = 0; k < paramTypes.length; k++) {
0257: Class columnClass = row[k]
0258: .getClass();
0259:
0260: if (!paramTypes[k]
0261: .isAssignableFrom(columnClass)) {
0262:
0263: if (!paramTypes[k]
0264: .isPrimitive()) {
0265: isValid = false;
0266: break;
0267: }
0268:
0269: Class primitiveType = (Class) columnClass
0270: .getDeclaredField(
0271: "TYPE").get(
0272: null);
0273:
0274: if (paramTypes[k]
0275: .isAssignableFrom(primitiveType)) {
0276: // jpa/11a5
0277: _primitiveColumns[k] = primitiveType;
0278: } else {
0279: isValid = false;
0280: break;
0281: }
0282: }
0283: }
0284:
0285: if (isValid)
0286: validConstructors.add(ctors[j]);
0287: }
0288:
0289: constructor = validConstructors.get(0);
0290:
0291: } catch (Exception ex) {
0292: throw error(L
0293: .l(
0294: "Unable to find constructor {0}. Make sure there is a public constructor for the given argument values ({1})",
0295: constructorClass.getName(),
0296: argValues));
0297: }
0298: }
0299: } else {
0300:
0301: row = new Object[n];
0302:
0303: for (int i = 0; i < n; i++) {
0304:
0305: int columnType = -1;
0306:
0307: try {
0308: columnType = metaData.getColumnType(i + 1);
0309: } catch (Exception ex) {
0310: }
0311:
0312: if (isNativeQuery())
0313: row[i] = getInternalNative(rs);
0314: else
0315: row[i] = getInternalObject(rs, columnType);
0316: }
0317: }
0318:
0319: if (constructor == null) {
0320: if (n == 1)
0321: results.add(row[0]);
0322: else
0323: results.add(row);
0324: } else {
0325:
0326: try {
0327: for (int i = 0; i < row.length; i++) {
0328: Class primitiveType = _primitiveColumns[i];
0329:
0330: if (primitiveType == null)
0331: continue;
0332:
0333: // jpa/11a5
0334: if (primitiveType == Boolean.TYPE)
0335: row[i] = ((Boolean) row[i])
0336: .booleanValue();
0337: else if (primitiveType == Byte.TYPE)
0338: row[i] = ((Byte) row[i]).byteValue();
0339: else if (primitiveType == Character.TYPE)
0340: row[i] = ((Character) row[i])
0341: .charValue();
0342: else if (primitiveType == Double.TYPE)
0343: row[i] = ((Double) row[i])
0344: .doubleValue();
0345: else if (primitiveType == Float.TYPE)
0346: row[i] = ((Float) row[i]).floatValue();
0347: else if (primitiveType == Integer.TYPE)
0348: row[i] = ((Integer) row[i]).intValue();
0349: else if (primitiveType == Long.TYPE)
0350: row[i] = ((Long) row[i]).longValue();
0351: else if (primitiveType == Short.TYPE)
0352: row[i] = ((Short) row[i]).shortValue();
0353: }
0354:
0355: object = constructor.newInstance(row);
0356: } catch (Exception ex) {
0357:
0358: StringBuilder argTypes = new StringBuilder();
0359:
0360: boolean isFirst = true;
0361:
0362: for (int i = 0; i < row.length; i++) {
0363:
0364: if (isFirst)
0365: isFirst = false;
0366: else
0367: argTypes.append(", ");
0368:
0369: if (row[i] == null)
0370: argTypes.append("null");
0371: else
0372: argTypes.append(row[i].toString()); // .getClass().getName());
0373: }
0374:
0375: throw error(L
0376: .l(
0377: "Unable to instantiate {0} with parameters ({1}).",
0378: constructorClass.getName(),
0379: argTypes));
0380: }
0381:
0382: results.add(object);
0383: }
0384: }
0385:
0386: if (log.isLoggable(Level.FINER)) {
0387: if (results != null) {
0388: log.log(Level.FINER, L.l(
0389: "query result list size: {0}", results
0390: .size()));
0391:
0392: java.util.Iterator it = results.iterator();
0393:
0394: while (it.hasNext()) {
0395: Object o = it.next();
0396:
0397: if (o == null)
0398: log.log(Level.FINER, L
0399: .l(" result entry: null"));
0400: else
0401: log.log(Level.FINER, L.l(
0402: " result entry: {0}", o.getClass()
0403: .getName()));
0404: }
0405: }
0406: }
0407:
0408: // jpa/0h19, jpa/1160
0409: if (!_aConn.isActiveTransaction())
0410: _aConn.detach();
0411:
0412: return results;
0413: } catch (RuntimeException e) {
0414: throw e;
0415: } catch (Exception e) {
0416: throw EJBExceptionWrapper.createRuntime(e);
0417: } finally {
0418: if (rs != null) {
0419: try {
0420: rs.close();
0421: } catch (SQLException e) {
0422: log.log(Level.FINE, e.toString(), e);
0423: }
0424: }
0425: }
0426: }
0427:
0428: /**
0429: * Returns a single result.
0430: */
0431: public Object getSingleResult() {
0432: ResultSet rs = null;
0433:
0434: try {
0435: if (!isSelectQuery())
0436: throw new IllegalStateException(
0437: L
0438: .l("javax.persistence.Query.getSingleResult() can only be applied to a SELECT statement"));
0439:
0440: rs = executeQuery();
0441:
0442: Object value = null;
0443:
0444: if (rs.next())
0445: value = rs.getObject(1);
0446: else
0447: // jpa/1004
0448: throw new NoResultException(
0449: "Query returned no results for getSingleResult()");
0450:
0451: // jpa/1005
0452: if (rs.next())
0453: throw new NonUniqueResultException(
0454: "Query returned more than one result for getSingleResult()");
0455:
0456: return value;
0457: } catch (RuntimeException e) {
0458: throw e;
0459: } catch (Exception e) {
0460: throw EJBExceptionWrapper.createRuntime(e);
0461: } finally {
0462: if (rs != null) {
0463: try {
0464: rs.close();
0465: } catch (SQLException e) {
0466: log.log(Level.FINE, e.toString(), e);
0467: }
0468:
0469: // jpa/0h19
0470: if (!_aConn.isActiveTransaction())
0471: _aConn.detach();
0472: }
0473: }
0474: }
0475:
0476: /**
0477: * Execute an update or delete.
0478: */
0479: public int executeUpdate() {
0480: try {
0481: // jpa/1006
0482: if (isSelectQuery())
0483: throw new IllegalStateException(
0484: L
0485: .l("javax.persistence.Query.executeUpdate() cannot be applied to a SELECT statement"));
0486:
0487: if (_flushMode == FlushModeType.AUTO)
0488: _aConn.flushNoChecks();
0489:
0490: return _userQuery.executeUpdate();
0491: } catch (Exception e) {
0492: throw EJBExceptionWrapper.createRuntime(e);
0493: }
0494: }
0495:
0496: /**
0497: * Executes the query returning a result set.
0498: */
0499: protected ResultSet executeQuery() throws SQLException {
0500: ResultSet rs = null;
0501: PreparedStatement pstmt = null;
0502:
0503: try {
0504: if (_flushMode == FlushModeType.AUTO)
0505: _aConn.flushNoChecks();
0506:
0507: if (_nativeSql == null) {
0508: // JPA query.
0509: rs = _userQuery.executeQuery();
0510: } else {
0511: // Native query stmt is prepared in setNativeSql().
0512: rs = _nativeStmt.executeQuery();
0513: }
0514: } catch (SQLException e) {
0515: if (rs != null)
0516: rs.close();
0517:
0518: if (pstmt != null)
0519: _aConn.closeStatement(_nativeSql);
0520:
0521: throw e;
0522: }
0523:
0524: return rs;
0525: }
0526:
0527: /**
0528: * Sets the maximum result returned.
0529: */
0530: public Query setMaxResults(int maxResults) {
0531: if (maxResults < 0)
0532: throw new IllegalArgumentException(
0533: L
0534: .l(
0535: "setMaxResults() needs a non-negative argument, '{0}' is not allowed",
0536: maxResults));
0537:
0538: _userQuery.setMaxResults(maxResults);
0539:
0540: return this ;
0541: }
0542:
0543: /**
0544: * Sets the position of the first result.
0545: */
0546: public Query setFirstResult(int startPosition) {
0547: if (startPosition < 0)
0548: throw new IllegalArgumentException(
0549: "setFirstResult() requires a non-negative argument");
0550:
0551: _userQuery.setFirstResult(startPosition);
0552:
0553: return this ;
0554: }
0555:
0556: /**
0557: * Sets a hint.
0558: */
0559: public Query setHint(String hintName, Object value) {
0560: return this ;
0561: }
0562:
0563: /**
0564: * Sets a named parameter.
0565: */
0566: public Query setParameter(String name, Object value) {
0567: ArrayList<String> mapping = _query.getPreparedMapping();
0568:
0569: int n = mapping.size();
0570:
0571: boolean found = false;
0572:
0573: for (int i = 0; i < n; i++) {
0574: if (mapping.get(i).equals(name)) {
0575: // jpa/10ee
0576: ArgExpr args[] = _userQuery.getQuery().getArgList();
0577: setInternalParameter(args[i], i + 1, value);
0578: found = true;
0579: }
0580: }
0581:
0582: if (!found)
0583: throw new IllegalArgumentException(L.l(
0584: "Parameter name '{0}' is invalid", name));
0585:
0586: return this ;
0587: }
0588:
0589: /**
0590: * Sets a date parameter.
0591: */
0592: public Query setParameter(String name, Date value, TemporalType type) {
0593: ArrayList<String> mapping = _query.getPreparedMapping();
0594:
0595: int n = mapping.size();
0596:
0597: boolean found = false;
0598:
0599: for (int i = 0; i < n; i++) {
0600: if (mapping.get(i).equals(name)) {
0601: setParameter(i + 1, value, type);
0602: found = true;
0603: }
0604: }
0605:
0606: if (!found)
0607: throw new IllegalArgumentException(L.l(
0608: "Parameter name '{0}' is invalid", name));
0609:
0610: return this ;
0611: }
0612:
0613: /**
0614: * Sets a calendar parameter.
0615: */
0616: public Query setParameter(String name, Calendar value,
0617: TemporalType type) {
0618: ArrayList<String> mapping = _query.getPreparedMapping();
0619:
0620: int n = mapping.size();
0621:
0622: boolean found = false;
0623:
0624: for (int i = 0; i < n; i++) {
0625: if (mapping.get(i).equals(name)) {
0626: setParameter(i + 1, value, type);
0627: found = true;
0628: }
0629: }
0630:
0631: if (!found)
0632: throw new IllegalArgumentException(L.l(
0633: "Parameter name '{0}' is invalid", name));
0634:
0635: return this ;
0636: }
0637:
0638: /**
0639: * Sets an indexed parameter.
0640: */
0641: public Query setParameter(int index, Object value) {
0642: if (_nativeSql == null) {
0643: // jpa/141h
0644: ArgExpr arg = checkParameterIndex(index);
0645:
0646: setInternalParameter(arg, index, value);
0647: } else {
0648: setInternalParameter(_nativeStmt, null, index, value);
0649: }
0650:
0651: return this ;
0652: }
0653:
0654: /**
0655: * Sets a date parameter.
0656: */
0657: public Query setParameter(int index, Date value, TemporalType type) {
0658: try {
0659:
0660: checkParameterIndex(index);
0661:
0662: if (value == null)
0663: _userQuery.setNull(index, Types.JAVA_OBJECT);
0664: else {
0665: switch (type) {
0666: case TIME:
0667: _userQuery.setObject(index, value,
0668: UtilDateType.TEMPORAL_TIME_TYPE);
0669: break;
0670:
0671: case DATE:
0672: _userQuery.setObject(index, value,
0673: UtilDateType.TEMPORAL_DATE_TYPE);
0674: break;
0675:
0676: default:
0677: _userQuery.setObject(index, value,
0678: UtilDateType.TEMPORAL_TIMESTAMP_TYPE);
0679: }
0680: }
0681:
0682: return this ;
0683: } catch (IndexOutOfBoundsException e) {
0684: throw new IllegalArgumentException(
0685: L
0686: .l(
0687: "Parameter index '{0}' is not valid for setParameter()",
0688: index));
0689: }
0690: }
0691:
0692: /**
0693: * Sets a calendar parameter.
0694: */
0695: public Query setParameter(int index, Calendar value,
0696: TemporalType type) {
0697: try {
0698:
0699: checkParameterIndex(index);
0700:
0701: if (value == null)
0702: _userQuery.setNull(index, Types.JAVA_OBJECT);
0703: else {
0704: switch (type) {
0705: case TIME:
0706: _userQuery.setObject(index, value,
0707: CalendarType.TEMPORAL_TIME_TYPE);
0708: break;
0709:
0710: case DATE:
0711: _userQuery.setObject(index, value,
0712: CalendarType.TEMPORAL_DATE_TYPE);
0713: break;
0714:
0715: default:
0716: _userQuery.setObject(index, value,
0717: CalendarType.TEMPORAL_TIMESTAMP_TYPE);
0718: }
0719: }
0720:
0721: return this ;
0722: } catch (IndexOutOfBoundsException e) {
0723: throw new IllegalArgumentException(
0724: L
0725: .l(
0726: "Parameter index '{0}' is not valid for setParameter()",
0727: index));
0728: }
0729: }
0730:
0731: /**
0732: * Sets the flush mode type.
0733: */
0734: public Query setFlushMode(FlushModeType mode) {
0735: _flushMode = mode;
0736:
0737: return this ;
0738: }
0739:
0740: //
0741: // extensions
0742:
0743: /**
0744: * Sets an indexed parameter.
0745: */
0746: public Query setDouble(int index, double value) {
0747: _userQuery.setDouble(index, value);
0748:
0749: return this ;
0750: }
0751:
0752: /**
0753: * Sets the sql for native queries.
0754: */
0755: protected void setNativeSql(String sql) throws SQLException {
0756: _nativeSql = sql;
0757: _nativeStmt = _aConn.prepareStatement(sql);
0758: }
0759:
0760: /**
0761: * Sets the sql result set mapping for native queries.
0762: */
0763: protected void setSqlResultSetMapping(SqlResultSetMappingConfig map) {
0764: _sqlResultSetMapping = map;
0765: }
0766:
0767: /**
0768: * Returns true for SELECT queries.
0769: */
0770: private boolean isSelectQuery() {
0771: if (_query instanceof SelectQuery)
0772: return true;
0773:
0774: if (isNativeQuery()) {
0775: String sql = _nativeSql.trim().toUpperCase();
0776:
0777: if (sql.startsWith("SELECT"))
0778: return true;
0779: }
0780:
0781: return false;
0782: }
0783:
0784: /**
0785: * Returns true for native queries.
0786: */
0787: private boolean isNativeQuery() {
0788: return _nativeSql != null;
0789: }
0790:
0791: /**
0792: * Creates an error.
0793: */
0794: private AmberException error(String msg) {
0795: msg += "\nin \"" + _query.getQueryString() + "\"";
0796:
0797: return new AmberException(msg);
0798: }
0799:
0800: /**
0801: * Native queries. Returns the object using the
0802: * result set mapping.
0803: */
0804: private Object getInternalNative(ResultSet rs) throws Exception {
0805: // jpa/0y1-
0806:
0807: int oldEntityResult = _currEntityResult;
0808:
0809: ArrayList<EntityResultConfig> entityResults = _sqlResultSetMapping
0810: .getEntityResults();
0811:
0812: if (oldEntityResult == entityResults.size()) {
0813:
0814: ArrayList<ColumnResultConfig> columnResults = _sqlResultSetMapping
0815: .getColumnResults();
0816:
0817: int oldColumnResult = _currColumnResult;
0818:
0819: if (_currColumnResult == columnResults.size()) {
0820: _currColumnResult = 0;
0821: }
0822:
0823: // jpa/0y19
0824: if (entityResults.size() == 0
0825: || oldColumnResult < columnResults.size()) {
0826: _currColumnResult++;
0827:
0828: if (columnResults.size() > 0) {
0829: Object object = rs.getObject(_currIndex++);
0830:
0831: return object;
0832: }
0833: }
0834:
0835: oldEntityResult = 0;
0836: _currEntityResult = 0;
0837: }
0838:
0839: _currEntityResult++;
0840:
0841: EntityResultConfig entityResult = entityResults
0842: .get(oldEntityResult);
0843:
0844: String className = entityResult.getEntityClass();
0845:
0846: EntityType entityType = _aConn.getPersistenceUnit()
0847: .getEntityType(className);
0848:
0849: if (entityType == null)
0850: throw new IllegalStateException(L.l(
0851: "Unable to locate entity '{0}' for native query.",
0852: className));
0853:
0854: int oldIndex = _currIndex;
0855:
0856: // jpa/0y15 _currIndex++;
0857:
0858: /* jpa/0y14
0859: EntityItem item = entityType.getHome().findItem(_aConn, rs, oldIndex);
0860:
0861: if (item == null)
0862: return null;
0863:
0864: Entity entity = item.getEntity();
0865: */
0866:
0867: int keyLength = entityType.getId().getKeyCount();
0868:
0869: ArrayList<FieldResultConfig> fieldResults = entityResult
0870: .getFieldResults();
0871:
0872: Entity entity = null;
0873:
0874: int consumed;
0875:
0876: try {
0877: // jpa/0y14
0878: entity = (Entity) _aConn.load(className, rs
0879: .getObject(oldIndex));
0880:
0881: // jpa/0y10
0882: consumed = entity.__caucho_load(_aConn, rs, oldIndex
0883: + keyLength);
0884: } catch (Exception e) {
0885: // jpa/0y1a: invalid query.
0886: throw new IllegalStateException(
0887: L
0888: .l(
0889: "Unable to load an entity of class '{0}' using a native query. When mapped to @EntityResult, a native query should select all fields for the corresponding entity in '{1}'",
0890: className, _nativeSql));
0891: }
0892:
0893: // item.setNumberOfLoadingColumns(consumed);
0894:
0895: _currIndex++;
0896:
0897: // jpa/0y12, jpa/0y15
0898: _currIndex += consumed;
0899:
0900: return entity;
0901: }
0902:
0903: /**
0904: * Returns the object using the correct
0905: * result set getter based on SQL type.
0906: */
0907: private Object getInternalObject(ResultSet rs, int columnType)
0908: throws Exception {
0909: // jpa/110-, jpa/11a4, and jpa/11z1
0910:
0911: int oldIndex = _currIndex;
0912:
0913: _currIndex++;
0914:
0915: Object object = rs.getObject(oldIndex);
0916:
0917: if (object instanceof Entity) {
0918: // _currIndex += ((ResultSetImpl) rs).getNumberOfLoadingColumns();
0919:
0920: ArrayList<AmberExpr> resultList = ((SelectQuery) _query)
0921: .getResultList();
0922:
0923: AmberExpr expr = resultList.get(oldIndex - 1);
0924:
0925: // jpa/1160
0926: if (expr instanceof LoadEntityExpr) {
0927: LoadEntityExpr entityExpr = (LoadEntityExpr) expr;
0928: joinFetch((ResultSetImpl) rs, entityExpr,
0929: (Entity) object);
0930: }
0931:
0932: return object;
0933: }
0934:
0935: if (object == null)
0936: return null;
0937:
0938: switch (columnType) {
0939: case Types.BIT:
0940: case Types.BOOLEAN:
0941: // try {
0942: // object = rs.getInt(oldIndex);
0943: // } catch (Exception ex) {
0944: if (!(object instanceof Boolean))
0945: object = rs.getBoolean(oldIndex);
0946: // }
0947: break;
0948:
0949: case Types.TINYINT:
0950: if (!(object instanceof Number))
0951: object = rs.getByte(oldIndex);
0952: break;
0953:
0954: case Types.SMALLINT:
0955: if (!(object instanceof Number))
0956: object = rs.getShort(oldIndex);
0957: break;
0958:
0959: case Types.INTEGER:
0960: if (!(object instanceof Number))
0961: object = rs.getLong(oldIndex);
0962: break;
0963:
0964: case Types.DECIMAL:
0965: case Types.DOUBLE:
0966: case Types.NUMERIC:
0967: case Types.REAL:
0968: if (!(object instanceof Number))
0969: object = rs.getDouble(oldIndex);
0970: break;
0971:
0972: case Types.FLOAT:
0973: if (!(object instanceof Number))
0974: object = rs.getFloat(oldIndex);
0975: break;
0976:
0977: // It was fetched with getObject (see top).
0978: // default:
0979: // object = rs.getObject(oldIndex);
0980: }
0981:
0982: return object;
0983: }
0984:
0985: private ArgExpr checkParameterIndex(int index) {
0986: // jpa/141h
0987:
0988: ArgExpr args[] = _userQuery.getQuery().getArgList();
0989:
0990: int len = args.length;
0991:
0992: for (int i = 0; i < len; i++) {
0993: if (args[i].getIndex() == index)
0994: return args[i];
0995: }
0996:
0997: throw new IllegalArgumentException(L.l(
0998: "Parameter index '{0}' is invalid for query {1}",
0999: index, _userQuery.getQuery()));
1000: }
1001:
1002: private void joinFetch(ResultSetImpl rs, LoadEntityExpr entityExpr,
1003: Entity entity) {
1004: String property = rs.getJoinFetchMap()
1005: .get(entityExpr.getExpr());
1006:
1007: EntityType entityType = entity.__caucho_getEntityType();
1008:
1009: Iterator eagerFieldsIterator = null;
1010:
1011: HashSet<String> eagerFieldNames = entityType
1012: .getEagerFieldNames();
1013:
1014: if (eagerFieldNames != null)
1015: eagerFieldsIterator = eagerFieldNames.iterator();
1016:
1017: // XXX: needs to handle field-based access
1018: if (!entityType.isFieldAccess()) {
1019: if (property == null)
1020: if ((eagerFieldsIterator != null)
1021: && eagerFieldsIterator.hasNext())
1022: property = (String) eagerFieldsIterator.next();
1023: }
1024:
1025: if (property != null) {
1026: try {
1027: Class cl = entityType.getInstanceClass();
1028:
1029: do {
1030: String methodName = "get"
1031: + Character.toUpperCase(property.charAt(0))
1032: + property.substring(1);
1033:
1034: Method method = cl.getDeclaredMethod(methodName,
1035: null);
1036:
1037: Object field = method.invoke(entity, null);
1038:
1039: // XXX: for now, invoke the toString() method on
1040: // the collection to fetch all the objects (join fetch).
1041:
1042: if (field == null) {
1043: try {
1044: methodName = "__caucho_item_" + methodName;
1045:
1046: method = cl
1047: .getDeclaredMethod(
1048: methodName,
1049: new Class[] { AmberConnection.class });
1050:
1051: field = method.invoke(entity, _aConn);
1052: } catch (Exception ex) {
1053: }
1054: }
1055:
1056: if (field != null) {
1057: Class fieldClass = field.getClass();
1058:
1059: method = fieldClass.getMethod("toString", null);
1060:
1061: method.invoke(field, null);
1062: }
1063:
1064: property = null;
1065:
1066: if ((eagerFieldsIterator != null)
1067: && (eagerFieldsIterator.hasNext()))
1068: property = (String) eagerFieldsIterator.next();
1069: } while (property != null);
1070:
1071: } catch (NoSuchMethodException e) {
1072: log.log(Level.FINER, e.toString(), e);
1073: } catch (IllegalAccessException e) {
1074: log.log(Level.FINER, e.toString(), e);
1075: } catch (java.lang.reflect.InvocationTargetException e) {
1076: log.log(Level.FINER, e.toString(), e);
1077: }
1078: }
1079: }
1080:
1081: /**
1082: * Sets an indexed parameter.
1083: */
1084: private Query setInternalParameter(ArgExpr arg, int index,
1085: Object value) {
1086: try {
1087: if (value == null) {
1088: _userQuery.setNull(index, java.sql.Types.JAVA_OBJECT);
1089: return this ;
1090: }
1091:
1092: // jpa/141i
1093: boolean valueIsNumber = value instanceof Number;
1094:
1095: if (valueIsNumber) {
1096: // jpa/0w20: type is null.
1097: if (arg.getType() != null) {
1098: boolean typeIsNumber = arg.getType().isNumeric();
1099:
1100: if (!typeIsNumber)
1101: throw new IllegalArgumentException(
1102: L
1103: .l(
1104: "Type mismatch for parameter index '{0}'. Value '{1}' for type '{2}' is not valid in query '{3}'",
1105: index, value, arg
1106: .getType()
1107: .getClass()
1108: .getName(),
1109: _userQuery.getQuery()
1110: .getSQL()));
1111: }
1112: }
1113:
1114: setInternalParameter(null, _userQuery, index, value);
1115:
1116: return this ;
1117: } catch (IndexOutOfBoundsException e) {
1118: log.log(Level.FINE, e.toString(), e);
1119:
1120: throw new IllegalArgumentException(
1121: L
1122: .l(
1123: "Parameter index '{0}' is not valid for setParameter()",
1124: index));
1125: }
1126: }
1127:
1128: /**
1129: * Sets an indexed parameter.
1130: */
1131: private static void setInternalParameter(PreparedStatement pstmt,
1132: UserQuery userQuery, int index, Object value) {
1133: try {
1134: if (value instanceof Byte) {
1135: byte arg = ((Byte) value).byteValue();
1136:
1137: if (pstmt == null)
1138: userQuery.setByte(index, arg);
1139: else
1140: pstmt.setByte(index, arg);
1141: } else if (value instanceof Short) {
1142: short arg = ((Short) value).shortValue();
1143:
1144: if (pstmt == null)
1145: userQuery.setShort(index, arg);
1146: else
1147: pstmt.setShort(index, arg);
1148: } else if (value instanceof Integer) {
1149: int arg = ((Integer) value).intValue();
1150:
1151: if (pstmt == null)
1152: userQuery.setInt(index, arg);
1153: else
1154: pstmt.setInt(index, arg);
1155: } else if (value instanceof Long) {
1156: long arg = ((Long) value).longValue();
1157:
1158: if (pstmt == null)
1159: userQuery.setLong(index, arg);
1160: else
1161: pstmt.setLong(index, arg);
1162: } else if (value instanceof Float) {
1163: float arg = ((Float) value).floatValue();
1164:
1165: if (pstmt == null)
1166: userQuery.setFloat(index, arg);
1167: else
1168: pstmt.setFloat(index, arg);
1169: } else if (value instanceof Double) { // jpa/141a
1170: double arg = ((Double) value).doubleValue();
1171:
1172: if (pstmt == null)
1173: userQuery.setDouble(index, arg);
1174: else
1175: pstmt.setDouble(index, arg);
1176: } else if (value instanceof Character) {
1177: if (pstmt == null)
1178: userQuery.setString(index, value.toString());
1179: else
1180: pstmt.setString(index, value.toString());
1181: } else if (value instanceof Entity) {
1182: // XXX: needs to handle Compound PK
1183:
1184: Object pk = ((Entity) value).__caucho_getPrimaryKey();
1185:
1186: if (pstmt == null)
1187: userQuery.setObject(index, pk);
1188: else
1189: pstmt.setObject(index, pk);
1190: } else {
1191: if (pstmt == null)
1192: userQuery.setObject(index, value);
1193: else
1194: pstmt.setObject(index, value);
1195: }
1196: } catch (Exception e) {
1197: log.log(Level.FINE, e.toString(), e);
1198:
1199: throw new IllegalArgumentException(
1200: L
1201: .l(
1202: "Parameter index '{0}' is not valid for setParameter()",
1203: index));
1204: }
1205: }
1206: }
|