问题
i have a query like this. i pass the student ID i need some fields from Student as well as their parent as well some fields from the parent->Address[here is the main problem i am facing] i am using AliasToBeanNestedResultTransformer
transformer by Sami Andoni
here is the implementation of it CODE
here is my code.
public List<Student>searchForStudent(Integer studentId)
{
Projection p=Projections.projectionList().create()
.add(Projections.property("name"),"name")//the student name it works O.K
.add(Projections.property("lastname"),"lastname")//the student name it works O.K
.add(Projections.property("age"),"age")//the student AGE it works O.K
.add(Projections.property("p.phone"),"parent.phone")//the parent phone it works O.K
.add(Projections.property("address.state").as("parent.Address.state")); // i need a field from address.state here is the problem...
Session session = ......
Criteria like = session.createCriteria(Student.class).add(prepareForSelect())//some filters..
.createAlias("parent","p")//the parent of the student. a student have one parent
.createAlias("parent.Address","address")//the address of the parent.... a parent have one address.
.setProjection(p)
.setResultTransformer(new AliasToBeanNestedResultTransformer(Student.class));
List<Student>results=like.list();
return results;
}
it throws
Exception in thread "main" org.hibernate.PropertyAccessException: IllegalArgumentException occurred while calling setter of com.generic.model.Parent.Address
FYI is some type mismatch i have done some tracing in SAMI
code and i see this
[MyState]
[Address]
seems that Hibernate is returning a String State
MyState
in this case and the transformer is using a Address Object
and this is the type Mismatch.
is any help is hugely needed it
thanks a lot.
回答1:
I have improved the SamiAndoni class, maybe it solve your issue
package com.alutiiq.develop.promanagesys.core.util;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.property.PropertyAccessor;
import org.hibernate.property.PropertyAccessorFactory;
import org.hibernate.property.Setter;
import org.hibernate.transform.AliasToBeanResultTransformer;
import org.hibernate.transform.AliasedTupleSubsetResultTransformer;
import org.hibernate.transform.ResultTransformer;
/**
* Help to transform alises with nested alises
*
* @author Miguel Resendiz
*
*/
public class AliasToBeanNestedResultTransformer extends
AliasedTupleSubsetResultTransformer {
private static final long serialVersionUID = -8047276133980128266L;
private static final int TUPE_INDEX = 0;
private static final int ALISES_INDEX = 1;
private static final int FIELDNAME_INDEX = 2;
private static final PropertyAccessor accessor = PropertyAccessorFactory
.getPropertyAccessor("property");
private final Class<?> resultClass;
private Object[] entityTuples;
private String[] entityAliases;
private Map<String, Class<?>> fieldToClass = new HashMap<String, Class<?>>();
private Map<String, List<?>> subEntities = new HashMap<String, List<?>>();
private List<String> nestedAliases = new ArrayList<String>();
private Map<String, Class<?>> listFields = new HashMap<String, Class<?>>();
public boolean isTransformedValueATupleElement(String[] aliases,
int tupleLength) {
return false;
}
public AliasToBeanNestedResultTransformer(Class<?> resultClass) {
this.resultClass = resultClass;
}
public Object transformTuple(Object[] tuple, String[] aliases) {
handleSubEntities(tuple, aliases);
cleanParams(tuple, aliases);
ResultTransformer rootTransformer = new AliasToBeanResultTransformer(
resultClass);
Object root = rootTransformer.transformTuple(entityTuples,
entityAliases);
loadSubEntities(root);
cleanMaps();
return root;
}
private void handleSubEntities(Object[] tuple, String[] aliases)
throws HibernateException {
String fieldName = "";
String aliasName = "";
try {
for (int i = 0; i < aliases.length; i++) {
String alias = aliases[i];
if (alias.contains(".")) {
String[] sp = alias.split("\\.");
StringBuilder aliasBuilder = new StringBuilder();
for (int j = 0; j < sp.length; j++) {
if (j == 0) {
fieldName = sp[j];
} else {
aliasBuilder.append(sp[j]);
aliasBuilder.append(".");
}
}
aliasName = aliasBuilder.substring(0,
aliasBuilder.length() - 1);
nestedAliases.add(alias);
manageEntities(fieldName, aliasName, tuple[i]);
}
}
} catch (NoSuchFieldException e) {
throw new HibernateException("Could not instantiate resultclass: "
+ resultClass.getName() + " for field name: " + fieldName
+ " and alias name:" + aliasName);
}
}
private Class<?> findClass(String fieldName) throws NoSuchFieldException,
SecurityException {
if (fieldToClass.containsKey(fieldName)) {
return fieldToClass.get(fieldName);
} else {
Class<?> subclass = resultClass.getDeclaredField(fieldName)
.getType();
if (subclass.equals(List.class) || subclass.equals(Set.class)) {
if (subclass.equals(List.class)) {
listFields.put(fieldName, LinkedList.class);
} else {
listFields.put(fieldName, HashSet.class);
}
Field field = resultClass.getDeclaredField(fieldName);
ParameterizedType genericType = (ParameterizedType) field
.getGenericType();
subclass = (Class<?>) genericType.getActualTypeArguments()[0];
}
fieldToClass.put(fieldName, subclass);
return subclass;
}
}
@SuppressWarnings("unchecked")
private void manageEntities(String fieldName, String aliasName,
Object tupleValue) throws NoSuchFieldException, SecurityException {
Class<?> subclass = findClass(fieldName);
if (!subEntities.containsKey(fieldName)) {
List<Object> list = new ArrayList<Object>();
list.add(new ArrayList<Object>());
list.add(new ArrayList<String>());
list.add(FIELDNAME_INDEX, subclass);
subEntities.put(fieldName, list);
}
((List<Object>) subEntities.get(fieldName).get(TUPE_INDEX))
.add(tupleValue);
((List<String>) subEntities.get(fieldName).get(ALISES_INDEX))
.add(aliasName);
}
private void cleanParams(Object[] tuple, String[] aliases) {
entityTuples = new Object[aliases.length - nestedAliases.size()];
entityAliases = new String[aliases.length - nestedAliases.size()];
for (int j = 0, i = 0; j < aliases.length; j++) {
if (!nestedAliases.contains(aliases[j])) {
entityTuples[i] = tuple[j];
entityAliases[i] = aliases[j];
++i;
}
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void loadSubEntities(Object root) throws HibernateException {
try {
for (String fieldName : subEntities.keySet()) {
Class<?> subclass = (Class<?>) subEntities.get(fieldName).get(
FIELDNAME_INDEX);
ResultTransformer subclassTransformer = new AliasToBeanNestedResultTransformer(
subclass);
Object subObject = subclassTransformer.transformTuple(
((List<Object>) subEntities.get(fieldName).get(0))
.toArray(),
((List<Object>) subEntities.get(fieldName).get(1))
.toArray(new String[0]));
Setter setter = accessor.getSetter(resultClass, fieldName);
if (listFields.containsKey(fieldName)) {
Class<?> collectionClass = listFields.get(fieldName);
Collection subObjectList = (Collection) collectionClass
.newInstance();
subObjectList.add(subObject);
setter.set(root, subObjectList, null);
} else {
setter.set(root, subObject, null);
}
}
} catch (Exception e) {
throw new HibernateException(e);
}
}
private void cleanMaps() {
fieldToClass = new HashMap<String, Class<?>>();
subEntities = new HashMap<String, List<?>>();
nestedAliases = new ArrayList<String>();
listFields = new HashMap<String, Class<?>>();
}
}
I hope it help you.
--------------edit 07/25/15---------------
To group nested list.
public List<? extends Entity<?>> cleanList(
List<? extends Entity<?>> resultList) throws DataException {
List<Entity<?>> entities = new ArrayList<Entity<?>>();
Entity<?> current = null;
try {
for (Entity<?> entity : resultList) {
if (entity.getId() == null) {
continue;
}
if (current == null) {
current = entity;
continue;
}
if (current.getId().equals(entity.getId())) {
append(current, entity);
} else {
entities.add(current);
current = entity;
}
}
if (current != null) {
entities.add(current);
}
cleanSubList(entities);
return entities;
} catch (Exception e) {
throw new DataException(e);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public Set<? extends Entity<?>> cleanList(
Set<? extends Entity<?>> resultList) throws DataException {
List listToClean = new LinkedList();
listToClean.addAll(resultList);
listToClean = cleanList(listToClean);
resultList.clear();
resultList.addAll(listToClean);
return resultList;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void append(Entity<?> current, Entity<?> next)
throws IllegalArgumentException, IllegalAccessException {
Field[] fields = current.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.getType().equals(List.class)) {
field.setAccessible(true);
List valueNext = (List) field.get(next);
List valueCurrent = (List) field.get(current);
if (valueNext != null) {
if (valueCurrent != null) {
valueCurrent.addAll(valueNext);
field.set(current, valueCurrent);
} else {
field.set(current, valueNext);
}
}
} else if (field.getType().equals(Set.class)) {
field.setAccessible(true);
Set valueNext = (Set) field.get(next);
Set valueCurrent = (Set) field.get(current);
if (valueNext != null) {
if (valueCurrent != null) {
valueCurrent.addAll(valueNext);
field.set(current, valueCurrent);
} else {
field.set(current, valueNext);
}
}
}
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void cleanSubList(List<? extends Entity<?>> listToClean)
throws IllegalArgumentException, IllegalAccessException,
DataException {
for (Entity<?> entity : listToClean) {
Field[] fields = entity.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.getType().equals(List.class)) {
field.setAccessible(true);
List valueToClean = (List) field.get(entity);
// Throw a thread
if (valueToClean != null) {
valueToClean = cleanList(valueToClean);
field.set(entity, valueToClean);
}
} else if (field.getType().equals(Set.class)) {
field.setAccessible(true);
Set valueToClean = (Set) field.get(entity);
// Throw a thread
if (valueToClean != null) {
valueToClean = cleanList(valueToClean);
field.set(entity, valueToClean);
}
}
}
}
}
To speed up the process I´m suggesting throw a thread in the main process.
This is my Entity interface:
package com.alutiiq.develop.promanagesys.common.entity;
import java.io.Serializable;
/**
* Entity for Hibernate comunications
*
* @author Miguel Resendiz
*
* @param <I>
* Primary key type
*/
public interface Entity<I extends Serializable> extends Serializable {
/**
* Enable poissibility to write generic queries using primary key
*
* @return primary key value for entity
*/
I getId();
void setId(I id);
void setId(String id);
}
Usage example:
criteria.setResultTransformer(new AliasToBeanNestedResultTransformer(
entityClass));
List<Project> projects = criteria.list();
projects = (List<Project>) cleanList(projects);
来源:https://stackoverflow.com/questions/20331852/java-hibernate-transformer-aliastobeannestedresulttransformer