| /* |
| * Copyright (c) 2013, 2021 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 |
| package org.eclipse.persistence.internal.sessions; |
| |
| import java.sql.ResultSet; |
| import java.sql.ResultSetMetaData; |
| import java.util.Vector; |
| |
| import org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor; |
| import org.eclipse.persistence.internal.helper.DatabaseField; |
| import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform; |
| |
| /** |
| * PERF: Record used by ObjectLevelReadQuery ResultSet optimization. |
| * The record corresponds to a single position (resultSet.next() is never called). |
| * In case the cached object used instead of creating a new one from the record, |
| * the not needed fields' values are never obtained from resultSet |
| * (that's especially important for expensive LOBs). |
| * If alternatively the record is used to populate an object then all |
| * the values obtained from resultSet and resultSet is nullified. |
| * In this case the fields' values not required after object population |
| * (those not involved in indirection) are nullified to save space in the record. |
| */ |
| public class ResultSetRecord extends ArrayRecord { |
| transient protected ResultSet resultSet; |
| transient protected ResultSetMetaData metaData; |
| transient protected DatabaseAccessor accessor; |
| transient protected DatabasePlatform platform; |
| transient protected boolean optimizeData; |
| transient protected AbstractSession session; |
| |
| protected ResultSetRecord() { |
| super(); |
| } |
| |
| public ResultSetRecord(Vector fields, DatabaseField[] fieldsArray, ResultSet resultSet, ResultSetMetaData metaData, DatabaseAccessor accessor, AbstractSession session, DatabasePlatform platform, boolean optimizeData) { |
| super(fields, fieldsArray, new Object[fieldsArray.length]); |
| this.resultSet = resultSet; |
| this.metaData = metaData; |
| this.accessor = accessor; |
| this.platform = platform; |
| this.optimizeData = optimizeData; |
| this.session = session; |
| } |
| |
| /** |
| * Obtains all the value from resultSet and removes it. |
| * resultSet must be non null. |
| */ |
| public void loadAllValuesFromResultSet() { |
| int size = this.valuesArray.length; |
| for (int index = 0; index < size; index++) { |
| if (this.valuesArray[index] == null) { |
| DatabaseField field = this.fieldsArray[index]; |
| // Field can be null for fetch groups. |
| if (field != null) { |
| this.valuesArray[index] = this.accessor.getObject(this.resultSet, field, this.metaData, index + 1, this.platform, this.optimizeData, this.session); |
| } |
| } |
| } |
| this.resultSet = null; |
| this.metaData = null; |
| this.accessor = null; |
| this.platform = null; |
| this.session = null; |
| } |
| |
| /** |
| * Remove values corresponding to all fields not related to indirection. |
| */ |
| public void removeNonIndirectionValues() { |
| if (this.fieldsArray != null) { |
| int size = this.valuesArray.length; |
| for (int index = 0; index < size; index++) { |
| DatabaseField field = this.fieldsArray[index]; |
| // Field can be null for fetch groups. |
| if (field != null) { |
| if (!field.keepInRow) { |
| this.valuesArray[index] = null; |
| } |
| } |
| } |
| } |
| } |
| |
| public void removAllValue() { |
| if (this.valuesArray != null) { |
| int size = this.valuesArray.length; |
| for (int index = 0; index < size; index++) { |
| this.valuesArray[index] = null; |
| } |
| } |
| } |
| |
| /** |
| * Indicates whether resultSet is still here. |
| */ |
| public boolean hasResultSet() { |
| return this.resultSet != null; |
| } |
| |
| public void removeResultSet() { |
| this.resultSet = null; |
| this.metaData = null; |
| this.accessor = null; |
| this.platform = null; |
| this.session = null; |
| } |
| |
| /** |
| * PUBLIC: |
| * Clear the contents of the row. |
| */ |
| @Override |
| public void clear() { |
| removeResultSet(); |
| this.fieldsArray = null; |
| this.valuesArray = null; |
| super.clear(); |
| } |
| |
| /** |
| * Reset the fields and values from the arrays. |
| * This removes the optimization if a non-optimized method is called. |
| */ |
| @Override |
| protected void checkValues() { |
| if (this.resultSet != null) { |
| loadAllValuesFromResultSet(); |
| } |
| super.checkValues(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Check if the value is contained in the row. |
| */ |
| @Override |
| public boolean containsValue(Object value) { |
| if (this.resultSet != null) { |
| loadAllValuesFromResultSet(); |
| } |
| return super.containsValue(value); |
| } |
| |
| /** |
| * INTERNAL: |
| * Retrieve the value for the field. If missing null is returned. |
| */ |
| @Override |
| public Object get(DatabaseField key) { |
| if (this.fieldsArray != null) { |
| // Optimize check. |
| int index = key.index; |
| if ((index < 0) || (index >= this.size)) { |
| index = 0; |
| } |
| DatabaseField field = this.fieldsArray[index]; |
| if ((field != key) && !field.equals(key)) { |
| index = -1; |
| for (int fieldIndex = 0; fieldIndex < this.size; fieldIndex++) { |
| field = this.fieldsArray[fieldIndex]; |
| if ((field == key) || field.equals(key)) { |
| // PERF: If the fields index was not set, then set it. |
| if (key.index == -1) { |
| key.setIndex(fieldIndex); |
| } |
| index = fieldIndex; |
| break; |
| } |
| } |
| if (index < 0) { |
| return null; |
| } |
| } |
| if (this.resultSet != null) { |
| Object value = this.valuesArray[index]; |
| if (value == null) { |
| value = this.accessor.getObject(this.resultSet, field, this.metaData, index + 1, this.platform, this.optimizeData, this.session); |
| this.valuesArray[index] = value; |
| } else { |
| // field's value has been already extracted earlier - the row is used to populate object |
| loadAllValuesFromResultSet(); |
| } |
| return value; |
| } else { |
| return this.valuesArray[index]; |
| } |
| } else { |
| return super.get(key); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Retrieve the value for the field. If missing DatabaseRow.noEntry is returned. |
| * PERF: This method is a clone of get() for performance. |
| */ |
| @Override |
| public Object getIndicatingNoEntry(DatabaseField key) { |
| if (this.fieldsArray != null) { |
| // Optimize check. |
| int index = key.index; |
| if ((index < 0) || (index >= this.size)) { |
| index = 0; |
| } |
| DatabaseField field = this.fieldsArray[index]; |
| if ((field != key) && !field.equals(key)) { |
| index = -1; |
| for (int fieldIndex = 0; fieldIndex < this.size; fieldIndex++) { |
| field = this.fieldsArray[fieldIndex]; |
| if ((field == key) || field.equals(key)) { |
| // PERF: If the fields index was not set, then set it. |
| if (key.index == -1) { |
| key.setIndex(fieldIndex); |
| } |
| index = fieldIndex; |
| break; |
| } |
| } |
| if (index < 0) { |
| return null; |
| } |
| } |
| if (this.resultSet != null) { |
| Object value = this.valuesArray[index]; |
| if (value == null) { |
| value = this.accessor.getObject(this.resultSet, field, this.metaData, index + 1, this.platform, this.optimizeData, this.session); |
| this.valuesArray[index] = value; |
| } else { |
| // field's value has been already extracted earlier - the row is used to populate object |
| loadAllValuesFromResultSet(); |
| } |
| return value; |
| } else { |
| return this.valuesArray[index]; |
| } |
| } else { |
| return super.get(key); |
| } |
| } |
| |
| @Override |
| protected String toStringAditional() { |
| return (this.resultSet != null ? " hasResultSet" : ""); |
| } |
| |
| @Override |
| public void setSopObject(Object sopObject) { |
| super.setSopObject(sopObject); |
| // sopObject is set - the row is used to populate object |
| if (this.resultSet != null) { |
| loadAllValuesFromResultSet(); |
| } |
| } |
| } |