| /* |
| * Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| // 02/08/2012-2.4 Guy Pelletier |
| // - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls |
| package org.eclipse.persistence.queries; |
| |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import org.eclipse.persistence.exceptions.QueryException; |
| import org.eclipse.persistence.internal.helper.DatabaseField; |
| import org.eclipse.persistence.internal.localization.ExceptionLocalization; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.security.PrivilegedClassForName; |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.exceptions.ValidationException; |
| import org.eclipse.persistence.mappings.OneToOneMapping; |
| import org.eclipse.persistence.sessions.DatabaseRecord; |
| |
| /** |
| * <p><b>Purpose</b>: |
| * Concrete class to represent the EntityResult structure as defined by |
| * the EJB 3.0 Persistence specification. This class is a subcomponent of the |
| * SQLResultSetMapping |
| * |
| * @see SQLResultSetMapping |
| * @author Gordon Yorke |
| * @since TopLink Java Essentials |
| */ |
| public class EntityResult extends SQLResult { |
| /** Stores the class name of result */ |
| protected String entityClassName; |
| protected transient Class entityClass; |
| |
| /** Stores the list of FieldResult */ |
| protected Map fieldResults; |
| |
| /** Stores the column that will contain the value to determine the correct subclass |
| * to create if applicable. |
| */ |
| protected DatabaseField discriminatorColumn; |
| |
| public EntityResult(Class entityClass){ |
| this.entityClass = entityClass; |
| if (this.entityClass == null){ |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage("null_value_for_entity_result")); |
| } |
| this.entityClassName = entityClass.getName(); |
| } |
| |
| public EntityResult(String entityClassName){ |
| this.entityClassName = entityClassName; |
| if (this.entityClassName == null){ |
| throw new IllegalArgumentException(ExceptionLocalization.buildMessage("null_value_for_entity_result")); |
| } |
| } |
| |
| public void addFieldResult(FieldResult fieldResult){ |
| if (fieldResult == null || fieldResult.getAttributeName() == null){ |
| return; |
| } |
| FieldResult existingFieldResult = (FieldResult)getFieldResults().get(fieldResult.getAttributeName()); |
| if (existingFieldResult==null){ |
| getFieldResults().put(fieldResult.getAttributeName(), fieldResult); |
| }else{ |
| existingFieldResult.add(fieldResult); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Convert all the class-name-based settings in this query to actual class-based |
| * settings. This method is used when converting a project that has been built |
| * with class names to a project with classes. |
| * @param classLoader |
| */ |
| @Override |
| public void convertClassNamesToClasses(ClassLoader classLoader){ |
| super.convertClassNamesToClasses(classLoader); |
| Class entityClass = null; |
| try{ |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| entityClass = AccessController.doPrivileged(new PrivilegedClassForName(entityClassName, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(entityClassName, exception.getException()); |
| } |
| } else { |
| entityClass = PrivilegedAccessHelper.getClassForName(entityClassName, true, classLoader); |
| } |
| } catch (ClassNotFoundException exc){ |
| throw ValidationException.classNotFoundWhileConvertingClassNames(entityClassName, exc); |
| } |
| this.entityClass = entityClass; |
| } |
| |
| /** |
| * Accessor for the internally stored list of FieldResult. Calling this |
| * method will result in a collection being created to store the FieldResult |
| */ |
| public Map getFieldResults(){ |
| if (this.fieldResults == null){ |
| this.fieldResults = new HashMap(); |
| } |
| return this.fieldResults; |
| } |
| |
| /** |
| * Returns the column name for the column that will store the value used to |
| * determine the subclass type if applicable. |
| */ |
| public DatabaseField getDiscriminatorColumn(){ |
| return this.discriminatorColumn; |
| } |
| |
| /** |
| * Sets the column name for the column that will store the value used to |
| * determine the subclass type if applicable. |
| */ |
| public void setDiscriminatorColumn(String column){ |
| if (column == null){ |
| return; |
| } |
| this.discriminatorColumn = new DatabaseField(column); |
| } |
| |
| public void setDiscriminatorColumn(DatabaseField column){ |
| if (column == null){ |
| return; |
| } |
| this.discriminatorColumn = column; |
| } |
| |
| /** |
| * INTERNAL: |
| * This method is a convenience method for extracting values from Results |
| */ |
| @Override |
| public Object getValueFromRecord(DatabaseRecord record, ResultSetMappingQuery query){ |
| // From the row data build result entity. |
| // To do this let's collect the column based data for this entity from |
| // the results and call build object with this new row. |
| ClassDescriptor descriptor = query.getSession().getDescriptor(this.entityClass); |
| if (descriptor == null) { |
| throw new IllegalArgumentException("@EntityResult: entityClass points to unknown entity: " |
| + (this.entityClass != null ? this.entityClass.getName() : "null")); |
| } |
| DatabaseRecord entityRecord = new DatabaseRecord(descriptor.getFields().size()); |
| if (descriptor.hasInheritance()) { |
| Object value = null; |
| if (this.discriminatorColumn == null) { |
| value = record.get(descriptor.getInheritancePolicy().getClassIndicatorField()); |
| } else { |
| value = record.getIndicatingNoEntry(this.discriminatorColumn); |
| if (value == AbstractRecord.noEntry){ |
| throw QueryException.discriminatorColumnNotSelected(this.discriminatorColumn.getName(), getSQLResultMapping().getName()); |
| } |
| } |
| entityRecord.put(descriptor.getInheritancePolicy().getClassIndicatorField(), value); |
| |
| // if multiple types may have been read get the correct descriptor. |
| if ( descriptor.getInheritancePolicy().shouldReadSubclasses()) { |
| Class classValue = descriptor.getInheritancePolicy().classFromRow(entityRecord, query.getSession()); |
| descriptor = query.getSession().getDescriptor(classValue); |
| } |
| } |
| for (Iterator mappings = descriptor.getMappings().iterator(); mappings.hasNext();) { |
| DatabaseMapping mapping = (DatabaseMapping)mappings.next(); |
| FieldResult fieldResult = (FieldResult)this.getFieldResults().get(mapping.getAttributeName()); |
| if (fieldResult != null){ |
| if (mapping.getFields().size() == 1 ) { |
| entityRecord.put(mapping.getFields().firstElement(), record.get(fieldResult.getColumn())); |
| } else if (mapping.getFields().size() >1){ |
| getValueFromRecordForMapping(entityRecord,mapping,fieldResult,record); |
| } |
| } else { |
| for (Iterator fields = mapping.getFields().iterator(); fields.hasNext();) { |
| DatabaseField field = (DatabaseField)fields.next(); |
| entityRecord.put(field, record.get(field)); |
| } |
| } |
| } |
| query.setReferenceClass(this.entityClass); |
| query.setDescriptor(descriptor); |
| //TODO : support prefetchedCacheKeys in ResultSetMappingQuery |
| return descriptor.getObjectBuilder().buildObject(query, entityRecord, null); |
| } |
| |
| @Override |
| public boolean isEntityResult(){ |
| return true; |
| } |
| |
| /** |
| * INTERNAL: |
| * This method is for processing all FieldResults for a mapping. Adds DatabaseFields to the passed in entityRecord |
| */ |
| public void getValueFromRecordForMapping(DatabaseRecord entityRecord,DatabaseMapping mapping, FieldResult fieldResult, DatabaseRecord databaseRecord){ |
| ClassDescriptor currentDescriptor = mapping.getReferenceDescriptor(); |
| /** check if this FieldResult contains any other FieldResults, process it if it doesn't */ |
| if (fieldResult.getFieldResults()==null){ |
| DatabaseField dbfield = processValueFromRecordForMapping(currentDescriptor,fieldResult.getMultipleFieldIdentifiers(),1); |
| /** If it is a 1:1 mapping we need to do the target to source field conversion. If it is an aggregate, it is fine as it is*/ |
| if (mapping.isOneToOneMapping()){ |
| dbfield = (((OneToOneMapping)mapping).getTargetToSourceKeyFields().get(dbfield)); |
| } |
| entityRecord.put(dbfield, databaseRecord.get(fieldResult.getColumn())); |
| return; |
| } |
| /** This processes each FieldResult stored in the collection of FieldResults individually */ |
| Iterator fieldResults = fieldResult.getFieldResults().iterator(); |
| while (fieldResults.hasNext()){ |
| FieldResult tempFieldResult = ((FieldResult)fieldResults.next()); |
| DatabaseField dbfield = processValueFromRecordForMapping(currentDescriptor,tempFieldResult.getMultipleFieldIdentifiers(),1); |
| if (mapping.isOneToOneMapping()){ |
| dbfield = (((OneToOneMapping)mapping).getTargetToSourceKeyFields().get(dbfield)); |
| } |
| entityRecord.put(dbfield, databaseRecord.get(tempFieldResult.getColumn())); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * This method is for processing a single FieldResult, returning the DatabaseField it refers to. |
| */ |
| public DatabaseField processValueFromRecordForMapping(ClassDescriptor descriptor, String[] attributeNames, int currentLoc){ |
| DatabaseMapping mapping = descriptor.getObjectBuilder().getMappingForAttributeName(attributeNames[currentLoc]); |
| if (mapping==null){throw QueryException.mappingForFieldResultNotFound(attributeNames,currentLoc);} |
| currentLoc++; |
| if (attributeNames.length!=currentLoc){ |
| ClassDescriptor currentDescriptor = mapping.getReferenceDescriptor(); |
| DatabaseField df= processValueFromRecordForMapping(currentDescriptor, attributeNames, currentLoc); |
| if (mapping.isOneToOneMapping()){ |
| return (((OneToOneMapping)mapping).getTargetToSourceKeyFields().get(df)); |
| } |
| return df; |
| }else{ |
| //this is it.. return this mapping's field |
| return mapping.getFields().firstElement(); |
| } |
| } |
| |
| } |