/******************************************************************************* | |
* Copyright (c) 2011, 2013 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 v1.0 and Eclipse Distribution License v. 1.0 | |
* which accompanies this distribution. | |
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html | |
* and the Eclipse Distribution License is available at | |
* http://www.eclipse.org/org/documents/edl-v10.php. | |
* | |
* Contributors: | |
* dclarke/tware - initial | |
******************************************************************************/ | |
package org.eclipse.persistence.jpa.rs.util; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.StringTokenizer; | |
import org.eclipse.persistence.descriptors.ClassDescriptor; | |
import org.eclipse.persistence.dynamic.DynamicEntity; | |
import org.eclipse.persistence.internal.descriptors.PersistenceEntity; | |
import org.eclipse.persistence.internal.dynamic.DynamicEntityImpl; | |
import org.eclipse.persistence.internal.helper.DatabaseField; | |
import org.eclipse.persistence.internal.jpa.CMP3Policy; | |
import org.eclipse.persistence.internal.sessions.AbstractSession; | |
import org.eclipse.persistence.jpa.JpaHelper; | |
import org.eclipse.persistence.jpa.rs.PersistenceContext; | |
import org.eclipse.persistence.mappings.DatabaseMapping; | |
import org.eclipse.persistence.mappings.OneToOneMapping; | |
import org.eclipse.persistence.queries.FetchGroupTracker; | |
import org.eclipse.persistence.sessions.DatabaseSession; | |
/** | |
* EclipseLink helper class used for converting composite key values passed into | |
* JAX-RS calls as query or matrix parameters into a value that can be used in a | |
* find. | |
* | |
* @author dclarke | |
* @since EclipseLink 2.4.0 | |
*/ | |
public class IdHelper { | |
private static final String SEPARATOR_STRING = "+"; | |
@SuppressWarnings("rawtypes") | |
public static Object buildId(PersistenceContext app, String entityName, String idString) { | |
DatabaseSession session = app.getJpaSession(); | |
ClassDescriptor descriptor = app.getDescriptor(entityName); | |
List<DatabaseMapping> pkMappings = descriptor.getObjectBuilder().getPrimaryKeyMappings(); | |
List<SortableKey> pkIndices = new ArrayList<SortableKey>(); | |
int index = 0; | |
int multitenantPKMappings = 0; | |
for (DatabaseMapping mapping: pkMappings){ | |
if (mapping.isMultitenantPrimaryKeyMapping()){ | |
multitenantPKMappings++; | |
} else { | |
pkIndices.add(new SortableKey(mapping, index)); | |
index++; | |
} | |
} | |
Collections.sort(pkIndices); | |
// Handle composite key in map | |
Object[] keyElements = new Object[pkMappings.size() - multitenantPKMappings]; | |
StringTokenizer tokenizer = new StringTokenizer(idString, SEPARATOR_STRING); | |
int tokens = tokenizer.countTokens(); | |
if (tokens + multitenantPKMappings != pkMappings.size()){ | |
throw new RuntimeException("Failed, incorrect number of keys values"); | |
} | |
index = 0; | |
Iterator<SortableKey> iterator = pkIndices.iterator(); | |
while (tokenizer.hasMoreTokens()){ | |
SortableKey key = iterator.next(); | |
String token = tokenizer.nextToken(); | |
DatabaseMapping mapping = key.getMapping(); | |
Class attributeClasification = mapping.getAttributeClassification(); | |
if (attributeClasification == null) { | |
if ((mapping.getFields() != null) && (!mapping.getFields().isEmpty())) { | |
attributeClasification = mapping.getFields().get(0).getType(); | |
} | |
} | |
Object idValue = session.getDatasourcePlatform().getConversionManager().convertObject(token, attributeClasification); | |
keyElements[key.getIndex()] = idValue; | |
index++; | |
} | |
if (descriptor.hasCMPPolicy()) { | |
CMP3Policy policy = (CMP3Policy) descriptor.getCMPPolicy(); | |
return policy.createPrimaryKeyInstanceFromPrimaryKeyValues((AbstractSession) session, new int[]{0}, keyElements); | |
} | |
if (keyElements.length == 1) { | |
return keyElements[0]; | |
} | |
return keyElements; | |
} | |
public static String stringifyId(Object entity, String typeName, PersistenceContext app) { | |
ClassDescriptor descriptor = app.getDescriptor(typeName); | |
List<DatabaseMapping> pkMappings = descriptor.getObjectBuilder().getPrimaryKeyMappings(); | |
if (pkMappings.isEmpty()) { | |
return ""; | |
} | |
List<SortableKey> pkIndices = new ArrayList<SortableKey>(); | |
int index = 0; | |
for (DatabaseMapping mapping : pkMappings) { | |
pkIndices.add(new SortableKey(mapping, index)); | |
index++; | |
} | |
Collections.sort(pkIndices); | |
StringBuffer key = new StringBuffer(); | |
Iterator<SortableKey> sortableKeys = pkIndices.iterator(); | |
List<DatabaseField> refObjectdbFields = null; | |
while (sortableKeys.hasNext()) { | |
DatabaseMapping mapping = sortableKeys.next().getMapping(); | |
ClassDescriptor refDesc = mapping.getReferenceDescriptor(); | |
List<DatabaseField> dbFields = mapping.getDescriptor().getPrimaryKeyFields(); | |
if (refDesc != null) { | |
refObjectdbFields = refDesc.getFields(); | |
} | |
if ((refObjectdbFields != null) && (!refObjectdbFields.isEmpty())) { | |
for (DatabaseField dbField : dbFields) { | |
String dbFieldName = dbField.getName(); | |
String refObjectDbFieldName = null; | |
if (refDesc != null) { | |
for (DatabaseField refObjectDbField : refObjectdbFields) { | |
refObjectDbFieldName = refObjectDbField.getName(); | |
if ((refObjectDbFieldName != null) && (dbFieldName != null)) { | |
if (dbFieldName.equals(refObjectDbFieldName)) { | |
List<DatabaseMapping> refMappings = refDesc.getMappings(); | |
for (DatabaseMapping refMapping : refMappings) {// | |
DatabaseField field = refMapping.getField(); | |
if (field != null) { | |
String fieldName = field.getName(); | |
if (mapping instanceof OneToOneMapping) { | |
Map<DatabaseField, DatabaseField> targetToSourceKeyFields = ((OneToOneMapping) mapping).getTargetToSourceKeyFields(); | |
Map<DatabaseField, DatabaseField> sourceToTargetFields = ((OneToOneMapping) mapping).getTargetToSourceKeyFields(); | |
if ((targetToSourceKeyFields != null) && (!targetToSourceKeyFields.isEmpty())) { | |
if (targetToSourceKeyFields.containsKey(refObjectDbField)) { | |
if ((sourceToTargetFields != null) && (!sourceToTargetFields.isEmpty())) { | |
if (sourceToTargetFields.containsKey(field)) { | |
if ((fieldName != null) && (dbFieldName.equals(fieldName))) { | |
Object value = descriptor.getObjectBuilder().getBaseValueForField(dbField, entity); | |
Object realAttributeValue = refMapping.getRealAttributeValueFromAttribute(refMapping.getAttributeValueFromObject(value), value, (AbstractSession) app.getJpaSession()); | |
key.append(realAttributeValue); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} else { | |
Object part = mapping.getAttributeValueFromObject(entity); | |
key.append(part); | |
} | |
if (sortableKeys.hasNext()) { | |
key.append(SEPARATOR_STRING); | |
refObjectdbFields = null; | |
} | |
} | |
return key.toString(); | |
} | |
/** | |
* build a shell of an object based on a primary key. The object shell will be an instance of the object with | |
* only primary key populated | |
* @param context | |
* @param entityType | |
* @param id | |
* @return | |
*/ | |
public static Object buildObjectShell(PersistenceContext context, String entityType, Object id) { | |
ClassDescriptor descriptor = context.getDescriptor(entityType); | |
List<DatabaseMapping> pkMappings = descriptor.getObjectBuilder().getPrimaryKeyMappings(); | |
Object entity = null; | |
if (descriptor.hasCMPPolicy()) { | |
CMP3Policy policy = (CMP3Policy) descriptor.getCMPPolicy(); | |
entity = policy.createBeanUsingKey(id, (AbstractSession) context.getJpaSession()); | |
} else if (entity instanceof DynamicEntity) { | |
DynamicEntityImpl dynamicEntity = (DynamicEntityImpl) context.newEntity(entityType); | |
// if there is only one PK mapping, we assume the id object | |
// represents the value of that mapping | |
if (pkMappings.size() == 1) { | |
dynamicEntity.set(pkMappings.get(0).getAttributeName(), id); | |
} else { | |
// If there are more that one PK, we assume an array as produced | |
// by buildId() above with the keys | |
// based on a sorted order of PK fields | |
List<SortableKey> pkIndices = new ArrayList<SortableKey>(); | |
int index = 0; | |
for (DatabaseMapping mapping : pkMappings) { | |
pkIndices.add(new SortableKey(mapping, index)); | |
index++; | |
} | |
Collections.sort(pkIndices); | |
Object[] keyElements = (Object[]) id; | |
for (SortableKey key : pkIndices) { | |
dynamicEntity.set(key.getMapping().getAttributeName(), keyElements[key.getIndex()]); | |
} | |
} | |
entity = dynamicEntity; | |
} else { | |
throw new RuntimeException("Could not create shell for entity."); | |
} | |
if (entity instanceof PersistenceEntity) { | |
((PersistenceEntity) entity)._persistence_setId(id); | |
} | |
if (entity instanceof FetchGroupTracker) { | |
((FetchGroupTracker) entity)._persistence_setSession(JpaHelper.getDatabaseSession(context.getEmf())); | |
} | |
return entity; | |
} | |
private static class SortableKey implements Comparable<SortableKey>{ | |
private DatabaseMapping mapping; | |
private int index; | |
public SortableKey(DatabaseMapping mapping, int index){ | |
this.mapping = mapping; | |
this.index = index; | |
} | |
public int compareTo(SortableKey o){ | |
return mapping.getAttributeName().compareTo(o.getMapping().getAttributeName()); | |
} | |
public DatabaseMapping getMapping(){ | |
return mapping; | |
} | |
public int getIndex(){ | |
return index; | |
} | |
} | |
} |