Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.java.ao.EntityProxy Maven / Gradle / Ivy
/*
* Copyright 2007 Daniel Spiewak
*
* Licensed 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 net.java.ao;
import net.java.ao.cache.CacheLayer;
import net.java.ao.schema.FieldNameConverter;
import net.java.ao.schema.NotNull;
import net.java.ao.schema.TableNameConverter;
import net.java.ao.sql.SqlUtils;
import net.java.ao.types.TypeInfo;
import net.java.ao.types.TypeManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Matcher;
import net.java.ao.types.LogicalType;
import static net.java.ao.Common.*;
import static net.java.ao.sql.SqlUtils.closeQuietly;
/**
* @author Daniel Spiewak
*/
public class EntityProxy, K> implements InvocationHandler
{
static boolean ignorePreload = false; // hack for testing
private final K key;
private final Method pkAccessor;
private final String pkFieldName;
private final Class type;
private final EntityManager manager;
private CacheLayer layer;
private Map locks;
private final ReadWriteLock locksLock = new ReentrantReadWriteLock();
private ImplementationWrapper implementation;
private List listeners;
public EntityProxy(EntityManager manager, Class type, K key) {
this.key = key;
this.type = type;
this.manager = manager;
pkAccessor = Common.getPrimaryKeyAccessor(type);
pkFieldName = Common.getPrimaryKeyField(type, getFieldNameConverter());
locks = new HashMap();
listeners = new LinkedList();
}
private FieldNameConverter getFieldNameConverter()
{
return this.manager.getNameConverters().getFieldNameConverter();
}
@SuppressWarnings("unchecked")
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("getEntityProxy")) {
return this;
}
if (method.getName().equals("getEntityType")) {
return type;
}
if (implementation == null) {
implementation = new ImplementationWrapper();
implementation.init((T) proxy);
}
MethodImplWrapper methodImpl = implementation.getMethod(method.getName(), method.getParameterTypes());
if (methodImpl != null) {
final Class> declaringClass = methodImpl.getMethod().getDeclaringClass();
if (!Object.class.equals(declaringClass)) {
// We don't want to return get the class Class using Class.forName as this doesn't play well
// with multiple ClassLoaders (if the AO library has a separate class loader to the AO client)
// Instead just compare the classNames
final String callingClassName = Common.getCallingClassName(1);
if (callingClassName == null || !callingClassName.equals(declaringClass.getName())) {
return methodImpl.getMethod().invoke(methodImpl.getInstance(), args);
}
}
}
if (method.getName().equals(pkAccessor.getName())) {
return getKey();
} else if (method.getName().equals("save")) {
save((RawEntity) proxy);
return Void.TYPE;
} else if (method.getName().equals("getEntityManager")) {
return manager;
} else if (method.getName().equals("addPropertyChangeListener")) {
addPropertyChangeListener((PropertyChangeListener) args[0]);
return null;
} else if (method.getName().equals("removePropertyChangeListener")) {
removePropertyChangeListener((PropertyChangeListener) args[0]);
return null;
} else if (method.getName().equals("hashCode")) {
return hashCodeImpl();
} else if (method.getName().equals("equals")) {
return equalsImpl((RawEntity) proxy, args[0]);
} else if (method.getName().equals("toString")) {
return toStringImpl();
} else if (method.getName().equals("init")) {
return null;
}
checkConstraints(method, args);
String tableName = getTableNameConverter().getName(type);
Class> attributeType = Common.getAttributeTypeFromMethod(method);
String polyFieldName = null;
if (attributeType != null) {
polyFieldName = (attributeType.getAnnotation(Polymorphic.class) == null ? null :
getFieldNameConverter().getPolyTypeName(method));
}
Mutator mutatorAnnotation = method.getAnnotation(Mutator.class);
Accessor accessorAnnotation = method.getAnnotation(Accessor.class);
OneToOne oneToOneAnnotation = method.getAnnotation(OneToOne.class);
OneToMany oneToManyAnnotation = method.getAnnotation(OneToMany.class);
ManyToMany manyToManyAnnotation = method.getAnnotation(ManyToMany.class);
AnnotationDelegate annotations = Common.getAnnotationDelegate(getFieldNameConverter(), method);
Transient transientAnnotation = annotations.getAnnotation(Transient.class);
// check annotations first, they trump all
if (oneToOneAnnotation != null && Common.interfaceInheritsFrom(method.getReturnType(), RawEntity.class)) {
Class extends RawEntity>> type = (Class extends RawEntity>>) method.getReturnType();
Object[] back = retrieveRelations((RawEntity) proxy, new String[0],
new String[] { Common.getPrimaryKeyField(type, getFieldNameConverter()) },
(Class extends RawEntity>) type, Common.where(oneToOneAnnotation, getFieldNameConverter()),
Common.getPolymorphicFieldNames(getFieldNameConverter(), type, this.type));
return back.length == 0 ? null : back[0];
} else if (oneToManyAnnotation != null && method.getReturnType().isArray()
&& Common.interfaceInheritsFrom(method.getReturnType().getComponentType(), RawEntity.class)) {
Class extends RawEntity>> type = (Class extends RawEntity>>) method.getReturnType().getComponentType();
return retrieveRelations((RawEntity) proxy, new String[0],
new String[] { Common.getPrimaryKeyField(type, getFieldNameConverter()) },
(Class extends RawEntity>) type, where(oneToManyAnnotation, getFieldNameConverter()),
Common.getPolymorphicFieldNames(getFieldNameConverter(), type, this.type));
} else if (manyToManyAnnotation != null && method.getReturnType().isArray()
&& Common.interfaceInheritsFrom(method.getReturnType().getComponentType(), RawEntity.class)) {
Class extends RawEntity>> throughType = manyToManyAnnotation.value();
Class extends RawEntity>> type = (Class extends RawEntity>>) method.getReturnType().getComponentType();
return retrieveRelations((RawEntity) proxy, null,
Common.getMappingFields(getFieldNameConverter(),
throughType, type), throughType, (Class extends RawEntity>) type,
Common.where(manyToManyAnnotation, getFieldNameConverter()),
Common.getPolymorphicFieldNames(getFieldNameConverter(), throughType, this.type),
Common.getPolymorphicFieldNames(getFieldNameConverter(), throughType, type));
} else if (Common.isAccessor(method)) {
return invokeGetter((RawEntity>) proxy, getKey(), tableName, getFieldNameConverter().getName(method),
polyFieldName, method.getReturnType(), transientAnnotation == null);
} else if (Common.isMutator(method)) {
invokeSetter((T) proxy, getFieldNameConverter().getName(method), args[0], polyFieldName);
return Void.TYPE;
}
throw new RuntimeException("Cannot handle method with signature: " + method.toString());
}
private TableNameConverter getTableNameConverter()
{
return manager.getNameConverters().getTableNameConverter();
}
public K getKey() {
return key;
}
@SuppressWarnings("unchecked")
public void save(RawEntity entity) throws SQLException {
CacheLayer cacheLayer = getCacheLayer(entity);
String[] dirtyFields = cacheLayer.getDirtyFields();
if (dirtyFields.length == 0) {
return;
}
String table = getTableNameConverter().getName(type);
final DatabaseProvider provider = this.manager.getProvider();
final TypeManager typeManager = provider.getTypeManager();
Connection conn = null;
PreparedStatement stmt = null;
try
{
conn = provider.getConnection();
StringBuilder sql = new StringBuilder("UPDATE " + provider.withSchema(table) + " SET ");
for (String field : dirtyFields) {
sql.append(provider.processID(field));
if (cacheLayer.contains(field)) {
sql.append(" = ?,");
} else {
sql.append(" = NULL,");
}
}
if (sql.charAt(sql.length() - 1) == ',') {
sql.setLength(sql.length() - 1);
}
sql.append(" WHERE ").append(provider.processID(pkFieldName)).append(" = ?");
stmt = provider.preparedStatement(conn, sql);
List events = new LinkedList();
int index = 1;
for (String field : dirtyFields) {
if (!cacheLayer.contains(field)) {
continue;
}
Object value = cacheLayer.get(field);
events.add(new PropertyChangeEvent(entity, field, null, value));
if (value == null) {
this.manager.getProvider().putNull(stmt, index++);
} else {
Class javaType = value.getClass();
if (value instanceof RawEntity) {
javaType = ((RawEntity) value).getEntityType();
}
TypeInfo dbType = typeManager.getType(javaType);
dbType.getLogicalType().validate(value);
dbType.getLogicalType().putToDatabase(this.manager, stmt, index++, value, dbType.getJdbcWriteType());
if (!dbType.getLogicalType().shouldCache(javaType)) {
cacheLayer.remove(field);
}
}
}
TypeInfo pkType = Common.getPrimaryKeyType(provider.getTypeManager(), type);
pkType.getLogicalType().putToDatabase(this.manager, stmt, index++, key, pkType.getJdbcWriteType());
this.manager.getRelationsCache().remove(cacheLayer.getToFlush());
cacheLayer.clearFlush();
this.manager.getRelationsCache().remove(entity, dirtyFields);
stmt.executeUpdate();
for (PropertyChangeListener l : listeners) {
for (PropertyChangeEvent evt : events) {
l.propertyChange(evt);
}
}
cacheLayer.clearDirty();
}
finally
{
closeQuietly(stmt);
closeQuietly(conn);
}
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
listeners.add(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
listeners.remove(listener);
}
public int hashCodeImpl() {
return (key.hashCode() + type.hashCode()) % (2 << 15);
}
public boolean equalsImpl(RawEntity proxy, Object obj) {
if (proxy == obj) {
return true;
}
if (obj instanceof RawEntity>) {
RawEntity> entity = (RawEntity>) obj;
String ourTableName = getTableNameConverter().getName(proxy.getEntityType());
String theirTableName = getTableNameConverter().getName(entity.getEntityType());
return Common.getPrimaryKeyValue(entity).equals(key) && theirTableName.equals(ourTableName);
}
return false;
}
public String toStringImpl() {
return getTableNameConverter().getName(type) + " {" + pkFieldName + " = " + key.toString() + "}";
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof EntityProxy, ?>) {
EntityProxy, ?> proxy = (EntityProxy, ?>) obj;
if (proxy.type.equals(type) && proxy.key.equals(key)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return hashCodeImpl();
}
CacheLayer getCacheLayer(RawEntity> entity) {
// not atomic, but throughput is more important in this case
if (layer == null) {
layer = manager.getCache().createCacheLayer(entity);
}
return layer;
}
Class getType() {
return type;
}
// any dirty fields are kept in the cache, since they have yet to be saved
void flushCache(RawEntity> entity) {
getCacheLayer(entity).clear();
}
private ReadWriteLock getLock(String field) {
locksLock.writeLock().lock();
try {
if (locks.containsKey(field)) {
return locks.get(field);
}
ReentrantReadWriteLock back = new ReentrantReadWriteLock();
locks.put(field, back);
return back;
} finally {
locksLock.writeLock().unlock();
}
}
private V invokeGetter(RawEntity> entity, K key, String table, String name, String polyName, Class type, boolean shouldCache) throws Throwable {
V back = null;
CacheLayer cacheLayer = getCacheLayer(entity);
shouldCache = shouldCache && getTypeManager().getType(type).getLogicalType().shouldCache(type);
getLock(name).writeLock().lock();
try {
if (!shouldCache && cacheLayer.dirtyContains(name)) {
return handleNullReturn(null, type);
} else if (shouldCache && cacheLayer.contains(name)) {
Object value = cacheLayer.get(name);
if (instanceOf(value, type)) {
return handleNullReturn((V) value, type);
} else if (isBigDecimal(value, type)) { // Oracle for example returns BigDecimal when we expect doubles
return (V) handleBigDecimal(value, type);
} else if (Common.interfaceInheritsFrom(type, RawEntity.class)
&& instanceOf(value, Common.getPrimaryKeyClassType((Class extends RawEntity>) type))) {
value = manager.peer((Class extends RawEntity>) type, value);
cacheLayer.put(name, value);
return handleNullReturn((V) value, type);
} else {
cacheLayer.remove(name); // invalid cached value
}
}
final DatabaseProvider provider = manager.getProvider();
Connection conn = null;
PreparedStatement stmt = null;
ResultSet res = null;
try {
conn = provider.getConnection();
StringBuilder sql = new StringBuilder("SELECT ");
sql.append(provider.processID(name));
if (polyName != null) {
sql.append(',').append(provider.processID(polyName));
}
sql.append(" FROM ").append(provider.withSchema(table)).append(" WHERE ");
sql.append(provider.processID(pkFieldName)).append(" = ?");
stmt = provider.preparedStatement(conn, sql);
TypeInfo pkType = Common.getPrimaryKeyType(provider.getTypeManager(), this.type);
pkType.getLogicalType().putToDatabase(manager, stmt, 1, key, pkType.getJdbcWriteType());
res = stmt.executeQuery();
if (res.next()) {
back = convertValue(res, provider.shorten(name), provider.shorten(polyName), type);
}
} finally {
closeQuietly(res, stmt, conn);
}
if (shouldCache) {
cacheLayer.put(name, back);
}
return handleNullReturn(back, type);
} finally {
getLock(name).writeLock().unlock();
}
}
private V handleNullReturn(V back, Class type) {
if (back != null) {
return back;
}
if (type.isPrimitive()) {
if (type.equals(boolean.class)) {
return (V) new Boolean(false);
} else if (type.equals(char.class)) {
return (V) new Character(' ');
} else if (type.equals(int.class)) {
return (V) new Integer(0);
} else if (type.equals(short.class)) {
return (V) new Short("0");
} else if (type.equals(long.class)) {
return (V) new Long("0");
} else if (type.equals(float.class)) {
return (V) new Float("0");
} else if (type.equals(double.class)) {
return (V) new Double("0");
} else if (type.equals(byte.class)) {
return (V) new Byte("0");
}
}
return null;
}
private void invokeSetter(T entity, String name, Object value, String polyName) throws Throwable {
CacheLayer cacheLayer = getCacheLayer(entity);
getLock(name).writeLock().lock();
try {
if (value instanceof RawEntity>) {
cacheLayer.markToFlush(((RawEntity>) value).getEntityType());
cacheLayer.markToFlush(entity.getEntityType());
}
cacheLayer.markDirty(name);
cacheLayer.put(name, value);
if (polyName != null) {
String strValue = null;
if (value != null) {
strValue = manager.getPolymorphicTypeMapper().convert(((RawEntity>) value).getEntityType());
}
cacheLayer.markDirty(polyName);
cacheLayer.put(polyName, strValue);
}
} finally {
getLock(name).writeLock().unlock();
}
}
private > V[] retrieveRelations(RawEntity entity, String[] inMapFields,
String[] outMapFields, Class type, String where, String[] thisPolyNames) throws SQLException {
return retrieveRelations(entity, inMapFields, outMapFields, type, type, where, thisPolyNames, null);
}
private > V[] retrieveRelations(RawEntity entity,
String[] inMapFields,
String[] outMapFields,
Class extends RawEntity>> type,
Class finalType,
String where,
String[] thisPolyNames,
String[] thatPolyNames) throws SQLException
{
if (inMapFields == null || inMapFields.length == 0) {
inMapFields = Common.getMappingFields(getFieldNameConverter(), type, this.type);
}
final String[] fields = getFields(Common.getPrimaryKeyField(finalType, getFieldNameConverter()),
inMapFields, outMapFields, where);
V[] cached = manager.getRelationsCache().get(entity, finalType, type, fields);
if (cached != null) {
return cached;
}
List back = new ArrayList();
List> throughValues = new ArrayList>();
List resPolyNames = new ArrayList(thatPolyNames == null ? 0 : thatPolyNames.length);
String table = getTableNameConverter().getName(type);
boolean oneToMany = type.equals(finalType);
final Preload preloadAnnotation = finalType.getAnnotation(Preload.class);
final DatabaseProvider provider = manager.getProvider();
Connection conn = null;
PreparedStatement stmt = null;
ResultSet res = null;
try {
conn = provider.getConnection();
StringBuilder sql = new StringBuilder();
String returnField;
String throughField = null;
int numParams = 0;
Set selectFields = new LinkedHashSet();
if (oneToMany && inMapFields.length == 1 && outMapFields.length == 1
&& preloadAnnotation != null && !ignorePreload) {
sql.append("SELECT "); // one-to-many preload
selectFields.add(outMapFields[0]);
selectFields.addAll(preloadValue(preloadAnnotation, getFieldNameConverter()));
if (selectFields.contains(Preload.ALL)) {
sql.append(Preload.ALL);
} else {
for (String field : selectFields) {
sql.append(provider.processID(field)).append(',');
}
sql.setLength(sql.length() - 1);
}
sql.append(" FROM ").append(provider.withSchema(table));
sql.append(" WHERE ").append(provider.processID(inMapFields[0])).append(" = ?");
if (!where.trim().equals("")) {
sql.append(" AND (").append(manager.getProvider().processWhereClause(where)).append(")");
}
if (thisPolyNames != null) {
for (String name : thisPolyNames) {
sql.append(" AND ").append(provider.processID(name)).append(" = ?");
}
}
numParams++;
returnField = outMapFields[0];
} else if (!oneToMany && inMapFields.length == 1 && outMapFields.length == 1
&& preloadAnnotation != null && !ignorePreload) {
String finalTable = getTableNameConverter().getName(finalType); // many-to-many preload
returnField = manager.getProvider().shorten(finalTable + "__aointernal__id");
throughField = manager.getProvider().shorten(table + "__aointernal__id");
sql.append("SELECT ");
String finalPKField = Common.getPrimaryKeyField(finalType, getFieldNameConverter());
selectFields.add(finalPKField);
selectFields.addAll(preloadValue(preloadAnnotation, getFieldNameConverter()));
if (selectFields.contains(Preload.ALL))
{
selectFields.remove(Preload.ALL);
selectFields.addAll(Common.getValueFieldsNames(finalType, getFieldNameConverter()));
}
sql.append(provider.withSchema(finalTable)).append('.').append(provider.processID(finalPKField));
sql.append(" AS ").append(provider.quote(returnField)).append(',');
selectFields.remove(finalPKField);
sql.append(provider.withSchema(table)).append('.').append(provider.processID(Common.getPrimaryKeyField(type, getFieldNameConverter())));
sql.append(" AS ").append(provider.quote(throughField)).append(',');
for (String field : selectFields) {
sql.append(provider.withSchema(finalTable)).append('.').append(provider.processID(field)).append(',');
}
sql.setLength(sql.length() - 1);
if (thatPolyNames != null) {
for (String name : thatPolyNames) {
String toAppend = table + '.' + name;
resPolyNames.add(toAppend);
sql.append(',').append(provider.processID(toAppend));
}
}
sql.append(" FROM ").append(provider.withSchema(table)).append(" INNER JOIN ");
sql.append(provider.withSchema(finalTable)).append(" ON ");
sql.append(provider.withSchema(table)).append('.').append(provider.processID(outMapFields[0]));
sql.append(" = ").append(provider.withSchema(finalTable)).append('.').append(provider.processID(finalPKField));
sql.append(" WHERE ").append(provider.withSchema(table)).append('.').append(
provider.processID(inMapFields[0])).append(" = ?");
if (!where.trim().equals("")) {
sql.append(" AND (").append(manager.getProvider().processWhereClause(where)).append(")");
}
if (thisPolyNames != null) {
for (String name : thisPolyNames) {
sql.append(" AND ").append(provider.processID(name)).append(" = ?");
}
}
numParams++;
} else if (inMapFields.length == 1 && outMapFields.length == 1) { // 99% case (1-* & *-*)
sql.append("SELECT ").append(provider.processID(outMapFields[0]));
selectFields.add(outMapFields[0]);
if (!oneToMany) {
throughField = Common.getPrimaryKeyField(type, getFieldNameConverter());
sql.append(',').append(provider.processID(throughField));
selectFields.add(throughField);
}
if (thatPolyNames != null) {
for (String name : thatPolyNames) {
resPolyNames.add(name);
sql.append(',').append(provider.processID(name));
selectFields.add(name);
}
}
sql.append(" FROM ").append(provider.withSchema(table));
sql.append(" WHERE ").append(provider.processID(inMapFields[0])).append(" = ?");
if (!where.trim().equals("")) {
sql.append(" AND (").append(manager.getProvider().processWhereClause(where)).append(")");
}
if (thisPolyNames != null) {
for (String name : thisPolyNames) {
sql.append(" AND ").append(provider.processID(name)).append(" = ?");
}
}
numParams++;
returnField = outMapFields[0];
} else {
sql.append("SELECT DISTINCT a.outMap AS outMap");
selectFields.add("outMap");
if (thatPolyNames != null) {
for (String name : thatPolyNames) {
resPolyNames.add(name);
sql.append(',').append("a.").append(provider.processID(name)).append(" AS ").append(
provider.processID(name));
selectFields.add(name);
}
}
sql.append(" FROM (");
returnField = "outMap";
for (String outMap : outMapFields) {
for (String inMap : inMapFields) {
sql.append("SELECT ");
sql.append(provider.processID(outMap));
sql.append(" AS outMap,");
sql.append(provider.processID(inMap));
sql.append(" AS inMap");
if (thatPolyNames != null) {
for (String name : thatPolyNames) {
sql.append(',').append(provider.processID(name));
}
}
if (thisPolyNames != null) {
for (String name : thisPolyNames) {
sql.append(',').append(provider.processID(name));
}
}
sql.append(" FROM ").append(provider.withSchema(table));
sql.append(" WHERE ");
sql.append(provider.processID(inMap)).append(" = ?");
if (!where.trim().equals("")) {
sql.append(" AND (").append(manager.getProvider().processWhereClause(where)).append(")");
}
sql.append(" UNION ");
numParams++;
}
}
sql.setLength(sql.length() - " UNION ".length());
sql.append(") a");
if (thatPolyNames != null) {
if (thatPolyNames.length > 0) {
sql.append(" WHERE (");
}
for (String name : thatPolyNames) {
sql.append("a.").append(provider.processID(name)).append(" = ?").append(" OR ");
}
if (thatPolyNames.length > 0) {
sql.setLength(sql.length() - " OR ".length());
sql.append(')');
}
}
if (thisPolyNames != null) {
if (thisPolyNames.length > 0) {
if (thatPolyNames == null) {
sql.append(" WHERE (");
} else {
sql.append(" AND (");
}
}
for (String name : thisPolyNames) {
sql.append("a.").append(provider.processID(name)).append(" = ?").append(" OR ");
}
if (thisPolyNames.length > 0) {
sql.setLength(sql.length() - " OR ".length());
sql.append(')');
}
}
}
stmt = provider.preparedStatement(conn, sql);
TypeInfo dbType = getTypeManager().getType(getClass(key));
int index = 0;
for (; index < numParams; index++) {
dbType.getLogicalType().putToDatabase(manager, stmt, index + 1, key, dbType.getJdbcWriteType());
}
int newLength = numParams + (thisPolyNames == null ? 0 : thisPolyNames.length);
String typeValue = manager.getPolymorphicTypeMapper().convert(this.type);
for (; index < newLength; index++) {
stmt.setString(index + 1, typeValue);
}
dbType = Common.getPrimaryKeyType(provider.getTypeManager(), finalType);
final TypeInfo throughDBType = Common.getPrimaryKeyType(provider.getTypeManager(), (Class extends RawEntity>) type);
res = stmt.executeQuery();
while (res.next()) {
K returnValue = dbType.getLogicalType().pullFromDatabase(manager, res, (Class) type, returnField);
Class backType = finalType;
for (String polyName : resPolyNames) {
if ((typeValue = res.getString(polyName)) != null) {
backType = (Class) manager.getPolymorphicTypeMapper().invert(finalType, typeValue);
break;
}
}
if (backType.equals(this.type) && returnValue.equals(key)) {
continue;
}
if (throughField != null) {
LogicalType logicalType = throughDBType.getLogicalType();
throughValues.add(manager.peer((Class extends RawEntity>) type,
logicalType.pullFromDatabase(manager, res, type, throughField)));
}
V returnValueEntity = manager.peer(backType, returnValue);
CacheLayer returnLayer = manager.getProxyForEntity(returnValueEntity).getCacheLayer(returnValueEntity);
if (selectFields.contains(Preload.ALL))
{
selectFields.remove(Preload.ALL);
selectFields.addAll(Common.getValueFieldsNames(finalType, getFieldNameConverter()));
}
for (String field : selectFields) {
if (!resPolyNames.contains(field)) {
returnLayer.put(field, res.getObject(field));
}
}
back.add(returnValueEntity);
}
} finally {
closeQuietly(res, stmt, conn);
}
cached = back.toArray((V[]) Array.newInstance(finalType, back.size()));
manager.getRelationsCache().put(entity,
(throughValues.size() > 0 ? throughValues.toArray(new RawEntity[throughValues.size()]) : cached),
type, cached, finalType, fields);
return cached;
}
private TypeManager getTypeManager()
{
return manager.getProvider().getTypeManager();
}
/**
* Gets the generic class of the given type
* @param object the type for which to get the class
* @return the generic class
*/
@SuppressWarnings("unchecked")
private static Class getClass(O object)
{
return (Class) object.getClass();
}
private String[] getFields(String pkField, String[] inMapFields, String[] outMapFields, String where) {
List back = new ArrayList();
back.addAll(Arrays.asList(outMapFields));
if (inMapFields != null && inMapFields.length > 0) {
if (!inMapFields[0].trim().equalsIgnoreCase(pkField)) {
back.addAll(Arrays.asList(inMapFields));
}
}
Matcher matcher = SqlUtils.WHERE_CLAUSE.matcher(where);
while (matcher.find()) {
back.add(matcher.group(1));
}
return back.toArray(new String[back.size()]);
}
private V convertValue(ResultSet res, String field, String polyName, Class type) throws SQLException
{
if (isNull(res, field))
{
return null;
}
if (polyName != null) {
Class extends RawEntity>> entityType = (Class extends RawEntity>>) type;
entityType = manager.getPolymorphicTypeMapper().invert(entityType, res.getString(polyName));
type = (Class) entityType; // avoiding Java cast oddities with generics
}
final TypeInfo databaseType = getTypeManager().getType(type);
if (databaseType == null) {
throw new RuntimeException("UnrecognizedType: " + type.toString());
}
return databaseType.getLogicalType().pullFromDatabase(this.manager, res, type, field);
}
private boolean isNull(ResultSet res, String field) throws SQLException
{
res.getObject(field);
return res.wasNull();
}
private boolean instanceOf(Object value, Class> type) {
if (value == null) {
return true;
}
if (type.isPrimitive()) {
if (type.equals(boolean.class)) {
return instanceOf(value, Boolean.class);
} else if (type.equals(char.class)) {
return instanceOf(value, Character.class);
} else if (type.equals(byte.class)) {
return instanceOf(value, Byte.class);
} else if (type.equals(short.class)) {
return instanceOf(value, Short.class);
} else if (type.equals(int.class)) {
return instanceOf(value, Integer.class);
} else if (type.equals(long.class)) {
return instanceOf(value, Long.class);
} else if (type.equals(float.class)) {
return instanceOf(value, Float.class);
} else if (type.equals(double.class)) {
return instanceOf(value, Double.class);
}
} else {
return type.isInstance(value);
}
return false;
}
/**
* Some DB (Oracle) return BigDecimal for about any number
*/
private boolean isBigDecimal(Object value, Class> type)
{
if (!(value instanceof BigDecimal))
{
return false;
}
return isInteger(type) || isLong(type) || isFloat(type) || isDouble(type);
}
private Object handleBigDecimal(Object value, Class> type)
{
final BigDecimal bd = (BigDecimal) value;
if (isInteger(type))
{
return bd.intValue();
}
else if (isLong(type))
{
return bd.longValue();
}
else if (isFloat(type))
{
return bd.floatValue();
}
else if (isDouble(type))
{
return bd.doubleValue();
}
else
{
throw new RuntimeException("Could not resolve actual type for object :" + value + ", expected type is " + type);
}
}
private boolean isDouble(Class> type)
{
return type.equals(double.class) || type.equals(Double.class);
}
private boolean isFloat(Class> type)
{
return type.equals(float.class) || type.equals(Float.class);
}
private boolean isLong(Class> type)
{
return type.equals(long.class) || type.equals(Long.class);
}
private boolean isInteger(Class> type)
{
return type.equals(int.class) || type.equals(Integer.class);
}
private void checkConstraints(Method method, Object[] args) {
AnnotationDelegate annotations = Common.getAnnotationDelegate(getFieldNameConverter(), method);
NotNull notNullAnnotation = annotations.getAnnotation(NotNull.class);
if (notNullAnnotation != null && args != null && args.length > 0) {
if (args[0] == null) {
String name = getFieldNameConverter().getName(method);
throw new IllegalArgumentException("Field '" + name + "' does not accept null values");
}
}
}
}