Binding to list of pojo's

I am trying to bind a table or grid component in the UI to a list of pojo’s returned from a web service call. I don’t know quite how to go about it since it seems those controls are especially designed to bind to entities from datasources. Is there a datasource implementation that takes a manually provisioned list of plain old java objects and allows me to handle the creat/modify/commit of them through the controller or datasource lifecycle events or something? If I’m off-base, how should I approach this?

Hi,

You can mark your POJO classes with @JmixEntity, they will be registered as DTO entities in Jmix, and will be able to be bound to UI components through data containers:
https://docs.jmix.io/jmix/1.0/data-model/entities.html#dto

After that you will need to override the standard behavior of the UI components in the screens where DTO entities are used, because built-in logic is implemented as CRUD operations with JPA entities.

  1. For data loaders - specify a loadDelegate in the screen controller to specify logic of loading the data:
    https://docs.jmix.io/jmix/1.0/backoffice-ui/data/data-loaders.html

  2. To specify custom save / modify entity logic for the Editor screen, use the commitDelegate in the DataContext section:
    image

3 Likes

Ok, that basically worked. The main issue we ran into is that JMIX seems to assume the @JmixEntity is a JPA Entity as soon as I set annotatedPropertiesOnly = false, and the build fails due to a unsatisfied dependency having to do with eclipselink.
The special scenario I have is that I am using a Category object out of a dependency library that is also under our control. Since it’s an external depdendency class (referenced in build.gradle), I can’t annotate it with the @JmixEntity annotation. So I created a delegate class that wraps it. I have 2 options, I can add @JmixProperty to each getter in the delegate class, which is clunky, or I can simply set annotatedPropertiesOnly = false and they all become properties…except that doesn’t work. Even though the @JmixProperty annotation has a target of both METHOD and PROPERTY, when I put it on a method, it doesn’t process it correctly.
In the end, I had to re-declare the properties in the delegate class (clunky), and set the values of the CategoryDTO based on the passed-in Category object. Then when I set a property of the DTO, set it in both places, the local property, and the contained Category object. Again, super clunky.

The way these annotations are implemented, I cannot create a DTOEntity out of an external class, which I would think would be the most common scenario. I have to build a very strange delegate hybrid.

If there were a properties file version of the annotations JmixEntity and JmixProperty, it might be feasable. There’s nothing in the documentation about that, though.

Thanks for your help!

You cannot use POJO classes from an external dependency because Jmix needs to enhance the bytecode of entities at the application build time. The bytecode modification adds property listeners, id handling, equals and hashCode implementations.

That’s why we require @JmixEntity annotation on DTO entities instead of just some configuration properties - we don’t want to give you a false impression that you can use classes that are not in your source code.

Regarding your case with consuming an external API, why not just create a DTO entity replicating the structure of your external Category class, and map the values between separate objects? I think it’s simpler than building “very strange delegate hybrids”, especially with the help of a mapping library like http://modelmapper.org

I’ve created a small demo project consuming the https://jsonplaceholder.typicode.com/todos API. It has two classes with identical structure: plain ExternalTodo as a demonstration of an external class, and TodoEntity annotated with @JmixEntity. The mapping from ExternalTodo to TodoEntity boils down to the following:

@Autowired
private ExternalTodoService externalTodoService;
@Autowired
private Metadata metadata;
@Autowired
private ModelMapper modelMapper; // this bean is defined in main app class

public List<TodoEntity> loadTodos() {
    return externalTodoService.loadTodos().stream()
            .map(todo -> {
                TodoEntity todoEntity = metadata.create(TodoEntity.class);
                modelMapper.map(todo, todoEntity);
                return todoEntity;
            })
            .collect(Collectors.toList());
}

Could you explain in more detail the situation where you faced this unsatisfied dependency? We have put a lot of effort into decoupling everything from EclipseLink.