avoid stackoverflow while reading and popular relations

2

I'm doing a library that populates a random model with random values, to be used in tests, but it happens that when I have a relation like below, I get a StackOverflowException

Author

@Entity
public class Author implements Serializable {

private static final long serialVersionUID = 1L;

@Id
private Long id;

private String name;

@OneToMany
private List<Book> books = new ArrayList<Book>();

and Book

@Entity
public class Book implements Serializable {


@ManyToOne
private Author author;

The code that reads the fields is like this

public <T> T fakeIt(Class<T> clazz) throws FakerException {
if(clazz.getAnnotation(Entity.class) == null){
    throw new FakerException("The class "+ clazz.getName()+ "is not an entity");
}
try {
    T faked  = clazz.newInstance();
    for(Field f : clazz.getDeclaredFields()){
    if(f.getName().equals("serialVersionUID"))
        continue;

    System.out.println("Genearting value for "+f.getName() + " on " + f.getDeclaringClass());

    f.setAccessible(true);
    f.set(faked, getValueForField(f));
    }
    return faked;
} catch(Exception e){
    throw new FakerException(e);
}
}

private Object getValueForField(Field f) throws Exception {
if(f.isAnnotationPresent(UseGenerator.class)){
    Generator<?> gen = (Generator<?>) f.getAnnotation(UseGenerator.class).generator().newInstance();
    return gen.genearte();

} else if(f.isAnnotationPresent(ManyToOne.class)){
    return fakeIt(f.getType());

} else if(f.isAnnotationPresent(OneToMany.class)){
    Class<?> toFake = extractTypeFromList(f);

    List<Object> fakedObjects = new ArrayList<Object>();
    for(int i = 0; i < 6; i++){
    fakedObjects.add(fakeIt(toFake));
    }

    return fakedObjects;
}
// Other types
String clazzType = f.getType().getSimpleName();
Generator<?> generator = defaultGenerators.get(clazzType.toLowerCase());

if(generator != null)
    return generator.genearte();

return null;
}

private Class<?> extractTypeFromList(Field f) {
ParameterizedType parameterizedType = (ParameterizedType) f.getGenericType();
Class<?> type = (Class<?>) parameterizedType.getActualTypeArguments()[0];

return type;
}

In this case, fakeIt is the function that generates the random values. In this case, it drops in the books field and when it calls the fakeIt again, it will try to create a new Author that will fall in the books field, and so on. What is the best way to avoid this?

    
asked by anonymous 29.07.2015 / 00:15

1 answer

4

Luiz

The cause of StackOverflow is the bidirectional relationship between the Book and Author classes.

When you create a author and try to give values to the attributes, a List<Book> is created, and every element of that list will also create Author , which in turn creates a new list of books and from there follow in loop.

To solve the problem, you'll have to look at bidirectional relationships .

As I said in the comment, you can solve the problem by creating a method that when searching for the value of field you check if the fake object itself is not the field attribute, something like:

private boolean checkIfFieldHasClass(Field f, Class<?> clazz){
   Class<?> fieldClass = f.getClass();

   for (Field declaredField : fieldClass.getDeclaredFields()) {
        if (declaredField.getClass().equals(clazz)) {
            return true;
        }
    }

    return false;
}

If field has an attribute of type clazz , then set the instance faked in this attribute, otherwise ask to generate this value.

In the comment I passed the faked as a parameter also by mistake. It is best that you leave this method above by returning a boolean to make it readable and use it for other times.

    
30.07.2015 / 03:53