org.apache.cayenne.DataObjectUtils Maven / Gradle / Ivy
Show all versions of cayenne-client-nodeps
/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
****************************************************************/
package org.apache.cayenne;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.query.ObjectIdQuery;
import org.apache.cayenne.query.Query;
/**
* A collection of utility methods to work with DataObjects.
*
* DataObjects and Primary Keys: All methods that allow to extract primary key values
* or use primary keys to find objects are provided for convenience. Still the author's
* belief is that integer sequential primary keys are meaningless in the object model and
* are pure database artifacts. Therefore relying heavily on direct access to PK provided
* via this class (or other such Cayenne API) is not a clean design practice in many
* cases, and sometimes may actually lead to security issues.
*
*
* @since 1.1
* @author Andrei Adamchik
*/
public final class DataObjectUtils {
/**
* Returns an int primary key value for a DataObject. Only works for single column
* numeric primary keys. If a DataObjects is transient or has an ObjectId that can not
* be converted to an int PK, an exception is thrown.
*/
public static int intPKForObject(Persistent dataObject) {
Object value = pkForObject(dataObject);
if (!(value instanceof Number)) {
throw new CayenneRuntimeException("PK is not a number: "
+ dataObject.getObjectId());
}
return ((Number) value).intValue();
}
/**
* Returns a primary key value for a DataObject. Only works for single column primary
* keys. If a DataObjects is transient or has a compound ObjectId, an exception is
* thrown.
*/
public static Object pkForObject(Persistent dataObject) {
Map pk = extractObjectId(dataObject);
if (pk.size() != 1) {
throw new CayenneRuntimeException("Expected single column PK, got "
+ pk.size()
+ " columns, ID: "
+ pk);
}
Map.Entry pkEntry = (Map.Entry) pk.entrySet().iterator().next();
return pkEntry.getValue();
}
/**
* Returns a primary key map for a DataObject. This method is the most generic out of
* all methods for primary key retrieval. It will work for all possible types of
* primary keys. If a DataObjects is transient, an exception is thrown.
*/
public static Map compoundPKForObject(Persistent dataObject) {
return Collections.unmodifiableMap(extractObjectId(dataObject));
}
static Map extractObjectId(Persistent dataObject) {
if (dataObject == null) {
throw new IllegalArgumentException("Null DataObject");
}
ObjectId id = dataObject.getObjectId();
if (!id.isTemporary()) {
return id.getIdSnapshot();
}
// replacement ID is more tricky... do some sanity check...
if (id.isReplacementIdAttached()) {
DbEntity entity = dataObject
.getObjectContext()
.getEntityResolver()
.lookupDbEntity(dataObject);
if (entity != null && entity.isFullReplacementIdAttached(id)) {
return id.getReplacementIdMap();
}
}
throw new CayenneRuntimeException("Can't get primary key from temporary id.");
}
/**
* Returns an object matching an int primary key. If the object is mapped to use
* non-integer PK or a compound PK, CayenneRuntimeException is thrown.
*
* If this object is already cached in the ObjectStore, it is returned without a
* query. Otherwise a query is built and executed against the database.
*
*
* @see #objectForPK(ObjectContext, ObjectId)
*/
public static DataObject objectForPK(
ObjectContext context,
Class dataObjectClass,
int pk) {
return objectForPK(context, buildId(context, dataObjectClass, new Integer(pk)));
}
/**
* Returns an object matching an Object primary key. If the object is mapped to use a
* compound PK, CayenneRuntimeException is thrown.
*
* If this object is already cached in the ObjectStore, it is returned without a
* query. Otherwise a query is built and executed against the database.
*
*
* @see #objectForPK(ObjectContext, ObjectId)
*/
public static DataObject objectForPK(
ObjectContext context,
Class dataObjectClass,
Object pk) {
return objectForPK(context, buildId(context, dataObjectClass, pk));
}
/**
* Returns an object matching a primary key. PK map parameter should use database PK
* column names as keys.
*
* If this object is already cached in the ObjectStore, it is returned without a
* query. Otherwise a query is built and executed against the database.
*
*
* @see #objectForPK(ObjectContext, ObjectId)
*/
public static DataObject objectForPK(
ObjectContext context,
Class dataObjectClass,
Map pk) {
ObjEntity entity = context.getEntityResolver().lookupObjEntity(dataObjectClass);
if (entity == null) {
throw new CayenneRuntimeException("Non-existent ObjEntity for class: "
+ dataObjectClass);
}
return objectForPK(context, new ObjectId(entity.getName(), pk));
}
/**
* Returns an object matching an int primary key. If the object is mapped to use
* non-integer PK or a compound PK, CayenneRuntimeException is thrown.
*
* If this object is already cached in the ObjectStore, it is returned without a
* query. Otherwise a query is built and executed against the database.
*
*
* @see #objectForPK(ObjectContext, ObjectId)
*/
public static DataObject objectForPK(
ObjectContext context,
String objEntityName,
int pk) {
return objectForPK(context, buildId(context, objEntityName, new Integer(pk)));
}
/**
* Returns an object matching an Object primary key. If the object is mapped to use a
* compound PK, CayenneRuntimeException is thrown.
*
* If this object is already cached in the ObjectStore, it is returned without a
* query. Otherwise a query is built and executed against the database.
*
*
* @see #objectForPK(ObjectContext, ObjectId)
*/
public static DataObject objectForPK(
ObjectContext context,
String objEntityName,
Object pk) {
return objectForPK(context, buildId(context, objEntityName, pk));
}
/**
* Returns an object matching a primary key. PK map parameter should use database PK
* column names as keys.
*
* If this object is already cached in the ObjectStore, it is returned without a
* query. Otherwise a query is built and executed against the database.
*
*
* @see #objectForPK(ObjectContext, ObjectId)
*/
public static DataObject objectForPK(
ObjectContext context,
String objEntityName,
Map pk) {
if (objEntityName == null) {
throw new IllegalArgumentException("Null ObjEntity name.");
}
return objectForPK(context, new ObjectId(objEntityName, pk));
}
/**
* Returns an object matching ObjectId. If this object is already cached in the
* ObjectStore, it is returned without a query. Otherwise a query is built and
* executed against the database.
*
* @return A DataObject that matched the id, null if no matching objects were found
* @throws CayenneRuntimeException if more than one object matched ObjectId.
*/
public static DataObject objectForPK(ObjectContext context, ObjectId id) {
return (DataObject) DataObjectUtils.objectForQuery(context, new ObjectIdQuery(
id,
false,
ObjectIdQuery.CACHE));
}
/**
* Returns a DataObject or a DataRow that is a result of a given query. If query
* returns more than one object, an exception is thrown. If query returns no objects,
* null is returned.
*
* @since 1.2
*/
public static Object objectForQuery(ObjectContext context, Query query) {
List objects = context.performQuery(query);
if (objects.size() == 0) {
return null;
}
else if (objects.size() > 1) {
throw new CayenneRuntimeException(
"Expected zero or one object, instead query matched: "
+ objects.size());
}
return objects.get(0);
}
static ObjectId buildId(ObjectContext context, String objEntityName, Object pk) {
if (pk == null) {
throw new IllegalArgumentException("Null PK");
}
if (objEntityName == null) {
throw new IllegalArgumentException("Null ObjEntity name.");
}
ObjEntity entity = context.getEntityResolver().getObjEntity(objEntityName);
if (entity == null) {
throw new CayenneRuntimeException("Non-existent ObjEntity: " + objEntityName);
}
DbEntity dbEntity = entity.getDbEntity();
if (dbEntity == null) {
throw new CayenneRuntimeException("No DbEntity for ObjEntity: "
+ entity.getName());
}
List pkAttributes = dbEntity.getPrimaryKey();
if (pkAttributes.size() != 1) {
throw new CayenneRuntimeException("PK contains "
+ pkAttributes.size()
+ " columns, expected 1.");
}
DbAttribute attr = (DbAttribute) pkAttributes.get(0);
return new ObjectId(objEntityName, attr.getName(), pk);
}
static ObjectId buildId(ObjectContext context, Class dataObjectClass, Object pk) {
if (pk == null) {
throw new IllegalArgumentException("Null PK");
}
if (dataObjectClass == null) {
throw new IllegalArgumentException("Null DataObject class.");
}
ObjEntity entity = context.getEntityResolver().lookupObjEntity(dataObjectClass);
if (entity == null) {
throw new CayenneRuntimeException("Unmapped DataObject Class: "
+ dataObjectClass.getName());
}
DbEntity dbEntity = entity.getDbEntity();
if (dbEntity == null) {
throw new CayenneRuntimeException("No DbEntity for ObjEntity: "
+ entity.getName());
}
List pkAttributes = dbEntity.getPrimaryKey();
if (pkAttributes.size() != 1) {
throw new CayenneRuntimeException("PK contains "
+ pkAttributes.size()
+ " columns, expected 1.");
}
DbAttribute attr = (DbAttribute) pkAttributes.get(0);
return new ObjectId(entity.getName(), attr.getName(), pk);
}
// not intended for instantiation
private DataObjectUtils() {
}
}