javagwtrequestfactory

RequestFactory Entity's parameter: List<OfOtherEntity> is null on client. On server is ok


I am learning RequestFactory. I have simple example working. Now I would like to implement for RF those Entities from below:


Server package

@Entity
public class Pizza implements Identifiable, Versionable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Version
    private Long version;
    private String name;
    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private List<Ingredient> ingredients;
    /* Getters and Setters */
}

@Entity
public class Ingredient implements Identifiable, Versionable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Version
    private Long version;
    private String name;
    private boolean vegan;
    /* Getters and Setters */
}

Here is DAO class for Pizza Entity:

@Override
public List<Pizza> get() {

    CriteriaBuilder cb = JPA.em().getCriteriaBuilder();
    CriteriaQuery<Pizza> q = cb.createQuery(Pizza.class);
    Root<Pizza> c = q.from(Pizza.class);
    q.select(c);

    TypedQuery<Pizza> query = JPA.em().createQuery(q);
    List<Pizza> results = query.getResultList();

    for(Pizza p: results) {
        for(Ingredient i: p.getIngredients()) {
            logger.info(i.getName());
        }
    }
    return results;
}

Shared package

I wrote Proxies for those classes:

@ProxyFor(value = Pizza.class, locator = PizzaLocator.class)
public interface PizzaProxy extends EntityProxy {
  public Long getId();
  public String getName();
  public void setName( String name );
  public Long getVersion();
  public List<IngredientProxy> getIngredients();
  public void setIngredients( List<IngredientProxy> ingredients )
}

@ProxyFor(value = Ingredient.class)
public interface IngredientProxy extends EntityProxy {
    public void setId(Long id);
    public Long getId();
    public Long getVersion();
    public void setVersion(Long version);
    public String getName();
    public void setName(String name);
    public boolean isVegan();
    public void setVegan(boolean vegan);
}

RF related interfaces:

public interface RequestFactoryServices extends RequestFactory {
      PizzaRequest pizzaRequest();
}

@Service(value = PizzaDao.class, locator = DaoLocator.class)
public interface PizzaRequest extends RequestContext {
    Request<PizzaProxy> findById( Long id );
    Request<Void> save( PizzaProxy pizza );
    Request<List<PizzaProxy>> get();
}

Client package

And here is the way I get data from server:

List<PizzaProxy> pizzas = new LinkedList<PizzaProxy>();
PizzaRequest context = createFactory().pizzaRequest();
context.get().to(new Receiver<List<PizzaProxy>>() {
        @Override
        public void onSuccess(List<PizzaProxy> response) {
            for(PizzaProxy p: response) {
                RootPanel.get().add(new Label(
                    p.getId() + " " + 
                    p.getName() + ", " + 
                    p.getVersion() + ", " + 
                    p.getIngredients()
                 ));
            }
        }
    }).fire();

As You can see in DAO class in get() method I am printing to logger info about ingredients. On the server side it all works.

The problem is that when I call p.getIngredients() I am getting null, instead of list of IngredientsProxies.

Is it happening cause I don't have Dao and Locator classes for Ingredients Entity?

Please help.

Answer: Entity Relationships

Changes to related entities can be persisted in a single request. For example, this code from the DynatableRF sample app in GWT trunk creates a new Person and Address at the same time:

PersonRequest context = requestFactory.personRequest(); AddressProxy address = context.create(AddressProxy.class); PersonProxy person = context.create(PersonProxy.class); person.setAddress(address); context.persist().using(person).fire(...); RequestFactory automatically sends the whole object graph in a single request. In this case, the implementation of Person.persist() on the server is responsible for persisting the related Address also, which may or may not happen automatically, depending on the ORM framework and how the relationship is defined. Note that RequestFactory does not currently support embedded objects (@Embedded in various ORM frameworks) because it expects every entity to exist independently with its own ID.

When querying the server, RequestFactory does not automatically populate relations in the object graph. To do this, use the with() method on a request and specify the related property name as a String:

Request findReq = requestFactory.personRequest().find(personId).with("address"); It is also necessary to use the with() method to retrieve any properties with types extending ValueProxy. The with() method takes multiple String arguments, so you can specify multiple property names at once. To specify nested properties, use dot notation. Putting it all together, you might have

Request findReq = find(personId).with("phone","address.city","address.zip")


Solution

  • By default gwt does not attach collection entities when fetching the object. You need to use the .with("ingredients") on your rf request. Make sure you have a getIngredients method in your pizza class. The rf request context will use this to fetch the ingredients. You may also need to make sure you have a transaction open if you are fetching this from something like hibernate. This will make sure the rf context can use an attached entity when retrieving the ingredients for your pizza.

    Note that you don't want to use the with("getIngredients") the rf context will call the get method correctly.

    context.get().with("ingredients").fire(new Receiver<>)