org.eclipse.persistence.mappings.structures.ObjectRelationalDataTypeDescriptor Maven / Gradle / Ivy
Show all versions of org.eclipse.persistence.core Show documentation
/*
* Copyright (c) 1998, 2018 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
// 14/05/2012-2.4 Guy Pelletier
// - 376603: Provide for table per tenant support for multitenant applications
package org.eclipse.persistence.mappings.structures;
import java.sql.Array;
import java.sql.Ref;
import java.sql.Struct;
import java.sql.Types;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.RelationalDescriptor;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor;
import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.queries.ContainerPolicy;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.queries.ReadObjectQuery;
import org.eclipse.persistence.queries.ValueReadQuery;
import org.eclipse.persistence.sessions.DatabaseRecord;
/**
* Purpose:
* Differentiates object-relational descriptors from normal relational descriptors.
* The object-relational descriptor describes a type not a table, (although there
* is normally a table associated with the type, unless it is aggregate).
*/
@SuppressWarnings("unchecked")
public class ObjectRelationalDataTypeDescriptor extends RelationalDescriptor {
protected String structureName;
protected Vector orderedFields;
protected Vector allOrderedFields;
public ObjectRelationalDataTypeDescriptor() {
this.orderedFields = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance();
}
/**
* INTERNAL:
* Auto-Default orderedFields to fields
*/
@Override
public void initialize(AbstractSession session) throws DescriptorException {
super.initialize(session);
if (orderedFields==null || orderedFields.size()==0){
orderedFields=getAllFields();
}
setAllOrderedFields();
}
/**
* PUBLIC:
* Order the fields in a specific
* Add the field ordering, this will order the fields in the order this method is called.
* @param fieldName the name of the field to add ordering on.
*/
public void addFieldOrdering(String fieldName) {
getOrderedFields().addElement(new DatabaseField(fieldName));
}
/**
* INTERNAL:
* Extract the direct values from the specified field value.
* Return them in a vector.
* The field value better be an Array.
*/
@Override
public Vector buildDirectValuesFromFieldValue(Object fieldValue) throws DatabaseException {
if(fieldValue == null) {
return null;
}
return Helper.vectorFromArray((Object[])fieldValue);
}
/**
* INTERNAL:
* Build the appropriate field value for the specified
* set of direct values.
* The database better be expecting an ARRAY.
*/
@Override
public Object buildFieldValueFromDirectValues(Vector directValues, String elementDataTypeName, AbstractSession session) throws DatabaseException {
Object[] fields = Helper.arrayFromVector(directValues);
try {
((DatabaseAccessor)session.getAccessor()).incrementCallCount(session);
java.sql.Connection connection = ((DatabaseAccessor)session.getAccessor()).getConnection();
return session.getPlatform().createArray(elementDataTypeName, fields, session,connection);
} catch (java.sql.SQLException ex) {
throw DatabaseException.sqlException(ex, session, false);
} finally {
((DatabaseAccessor)session.getAccessor()).decrementCallCount();
}
}
/**
* INTERNAL:
* Build and return the field value from the specified nested database row.
* The database better be expecting a Struct.
*/
@Override
public Object buildFieldValueFromNestedRow(AbstractRecord nestedRow, AbstractSession session) throws DatabaseException {
java.sql.Connection connection = ((DatabaseAccessor)session.getAccessor()).getConnection();
return this.buildStructureFromRow(nestedRow, session, connection);
}
/**
* INTERNAL:
* Build and return the appropriate field value for the specified
* set of nested rows.
* The database better be expecting an ARRAY.
* It looks like we can ignore inheritance here....
*/
@Override
public Object buildFieldValueFromNestedRows(Vector nestedRows, String structureName, AbstractSession session) throws DatabaseException {
Object[] fields = new Object[nestedRows.size()];
java.sql.Connection connection = ((DatabaseAccessor)session.getAccessor()).getConnection();
boolean reconnected = false;
try {
if (connection == null) {
((DatabaseAccessor)session.getAccessor()).incrementCallCount(session);
reconnected = true;
connection = ((DatabaseAccessor)session.getAccessor()).getConnection();
}
int i = 0;
for (Enumeration stream = nestedRows.elements(); stream.hasMoreElements();) {
AbstractRecord nestedRow = (AbstractRecord)stream.nextElement();
fields[i++] = this.buildStructureFromRow(nestedRow, session, connection);
}
return session.getPlatform().createArray(structureName, fields, session,connection);
} catch (java.sql.SQLException exception) {
throw DatabaseException.sqlException(exception, session, false);
} finally {
if (reconnected) {
((DatabaseAccessor)session.getAccessor()).decrementCallCount();
}
}
}
/**
* INTERNAL:
* Build and return the nested rows from the specified field value.
* This method allows the field value to be an ARRAY containing other structures
* such as arrays or Struct, or direct values.
*/
static public Object buildContainerFromArray(Array fieldValue, ObjectRelationalDatabaseField arrayField, AbstractSession session) throws DatabaseException {
if (arrayField.getType()==null){
return fieldValue;
}
Object[] objects = null;
try {
objects = (Object[])fieldValue.getArray();
} catch (java.sql.SQLException ex) {
throw DatabaseException.sqlException(ex, session, false);
}
if (objects == null) {
return null;
}
boolean isNestedStructure = false;
ObjectRelationalDataTypeDescriptor ord=null;
DatabaseField nestedType = null;
nestedType = arrayField.getNestedTypeField();
if ((nestedType != null) && nestedType.getSqlType()==Types.STRUCT){
ClassDescriptor descriptor = session.getDescriptor(nestedType.getType());
if ((descriptor != null) && (descriptor.isObjectRelationalDataTypeDescriptor())) {
//this is used to convert non-null objects passed through stored procedures and custom SQL to structs
ord=(ObjectRelationalDataTypeDescriptor)descriptor;
}
} else if ((nestedType != null) && (nestedType instanceof ObjectRelationalDatabaseField) ){
isNestedStructure = true;
}
//handle ARRAY conversions
ReadObjectQuery query = new ReadObjectQuery();
query.setSession(session);
ContainerPolicy cp = ContainerPolicy.buildPolicyFor(arrayField.getType());
Object container = cp.containerInstance(objects.length);
for (int i = 0; i < objects.length; i++) {
Object arrayValue = objects[i];
if (arrayValue == null) {
return null;
}
if (ord!=null){
AbstractRecord nestedRow = ord.buildRowFromStructure( (Struct)arrayValue);
ClassDescriptor descriptor = ord;
if (descriptor.hasInheritance()) {
Class newElementClass = descriptor.getInheritancePolicy().classFromRow(nestedRow, session);
if (!descriptor.getJavaClass().equals(newElementClass)) {
descriptor = session.getDescriptor(newElementClass);
if (descriptor==null){
descriptor=ord;
}
}
}
arrayValue = descriptor.getObjectBuilder().buildNewInstance();
descriptor.getObjectBuilder().buildAttributesIntoObject(arrayValue, null, nestedRow, query, null, null, false, session);
} else if (isNestedStructure && (arrayValue instanceof Array)){
arrayValue = buildContainerFromArray((Array)arrayValue, (ObjectRelationalDatabaseField)nestedType, session);
}
cp.addInto(arrayValue, container, session);
}
return container;
}
/**
* INTERNAL:
* Build and return the nested database row from the specified field value.
* The field value better be an Struct.
*/
@Override
public AbstractRecord buildNestedRowFromFieldValue(Object fieldValue) throws DatabaseException {
AbstractRecord row = new DatabaseRecord();
Object[] attributes = (Object[])fieldValue;
for (int index = 0; index < getAllOrderedFields().size(); index++) {
DatabaseField field = (DatabaseField) getAllOrderedFields().get(index);
row.put(field, attributes[index]);
}
return row;
}
/**
* INTERNAL:
* Creates allOrderedFields Vector, keeping allFields contents ordered.
*/
private void setAllOrderedFields() {
allOrderedFields = new Vector(orderedFields.size());
Vector allFieldsCopy = new Vector(getAllFields());
for (DatabaseField orderedField : (Vector) getOrderedFields()) {
Iterator iterator = allFieldsCopy.iterator();
while (iterator.hasNext()) {
DatabaseField field = iterator.next();
if (orderedField.getName().equalsIgnoreCase(field.getName())) {
allOrderedFields.add(field);
iterator.remove();
}
}
}
}
/**
* INTERNAL:
* Build and return the nested rows from the specified field value.
* The field value better be an ARRAY.
*/
@Override
public Vector buildNestedRowsFromFieldValue(Object fieldValue, AbstractSession session) throws DatabaseException {
if(fieldValue==null){
return null;
}
Object[] structs = (Object[])fieldValue;
Vector nestedRows = new Vector(structs.length);
for (int i = 0; i < structs.length; i++) {
Object[] struct = (Object[])structs[i];
if (struct == null) {
return null;
}
nestedRows.addElement(this.buildNestedRowFromFieldValue(struct));
}
return nestedRows;
}
/**
* INTERNAL:
* Build a row representation from the ADT structure field array.
* TopLink will then build the object from the row.
*/
public AbstractRecord buildRowFromStructure(Struct structure) throws DatabaseException {
Object[] attributes;
try {
attributes = structure.getAttributes();
} catch (java.sql.SQLException exception) {
throw DatabaseException.sqlException(exception);
}
if(attributes!=null){
for(int i=0;i