Bypassing views in datamanager and working like Hibernate

Hi,
I am working with dataManager because I need always to impose security.
Data manager force you to use Views when fetching related.
Please excuse me for being so brutal, but Views are a nightmare.
You lose type-safe programming, its like PHP. you refactor some field and then you need to run search and replace. or you add a field and then you need to go through all the views and add it. (same for XML)

Moreover, let’s say that I fetched an entity with a view, and some middlewares are using it, now I need to check on every middleware if its loaded or not in the entire application, and if not I need to reload it with a view.
This code is really redundant and dangerous.

Why dataManager doesn’t act like entityManager when it comes to fetching data.
If you pass a view, the view will define the fetching behavior.
If you dont pass a view, dataManager will fetch all the locals and use the fetch type for defining the related behavior.

Views are really returning me to PHP… why loosing the compiler checks…
I think this should be rethinked.

Thanks

Hi,
couldn’t agree more that that null checks tend to deluge code. We have found a solution in using MANAGED entities when it comes to execute business methods, since in that case persistence loads fields on-the-fly. On the other hand this poses some risks because a managed entity could be accidentally modified. Entities state checking could leverages an aspect to make methods more concise.

Regards,
Ilya.

Hi Avi,

Thank you for the idea, a discussion in this field is certainly useful.

Let me explain our position.
DataManager returns detached entities, only this condition allows it to provide a lot of additional functionality including security checks. For managed entities and lazily loaded attributes it’s close to impossible.
Also, DataManager API is simpler - you define what to load and load it. Period. Same for saving. Same behavior for client and middle tiers. And we are going to further promote working with DataManager, detached entities, EntityChangedEvent in favour of EntityManager and entity listeners.

I agree that working with views is currently not very safe. However, Studio automatically refactors views when you change entity attributes in the entity designer. And we are going to improve the support from tooling in the future.
Another direction is to introduce a static model of interfaces or classes that reflect the graphs of views. These classes will be compatible with entities, so you will be able to use them everywhere, so for example instead of Order entity loaded with order-with-customer view you will have a OrderWithCustomer class with customer attribute always loaded, so no checks will be needed and refactorings will be supported by any IDE. We have some early prototype of this approach.

Regards,
Konstantin

3 Likes

Sounds great!

Can’t wait to see it.

Hi @knstvk . Thank you for clearing this one out.
So, maybe there should be a better/different API for that. The fact that I get an exception about related, makes the code more cmplicated and less safe.

When I ask for related (Car car = user.getCar()) , why dont you return a car with just id and _minimal, or just id, and let me easily quering it.
This way I can go:

Car car = user.getCar();
if(!car.isLoaded()){
    dataManager.load(Car.class).id(car.getId()).one();
}

or if we take it furthur. we can go

Car car = user.getCar().loadIfNeeded();

Ithink you understand the general idea.

Don’t you think that this code can handle security since its a different query and it is simplified?
I understand that the UI is better off be handled by the framework and let it work with views.
For business logic (which is where the headake), we need a better API.
What do you think?
Thanks

1 Like

Yes, please! I tend to have the same feelings as Avi when working with views in middleware code.

When the amount of code grows, and time passes, it’s nearly impossible to keep track of what attributes are needed in views for certain code paths.
It is certainly possible to test views by having proper test cases for services and so on (and that should be, in theory, always the case), but having compile time checks is a plus, and it’s the main reason to stick with strict typed languages…

But how do you model this?
In theory the “synthetic” interfaces like OrderWithCustomer should be higher in the inheritance tree, that is the actual entity class like Order should implement every view related interface. Then the interfaces should contain only the accessor methods for the attributes included in the target view, and maintained in sync with every change in the view definition.
Doing it in this way you achieve the highest degree of polymorphism (Order will always be a subtype of view-interfaces, and will have accessors for all the attributes, while the interfaces will contain a subset of them).

What do you think?

P.

1 Like

Agree. Your thoughts gave me even more radical idea - why not implement lazy loading via DataManager? At a first glance it looks feasible: we need to always load reference ids, store them in ValueHolder fields in the entity, and instead of throwing exceptions load references via DataManager by id. For unfetched local attributes even simpler: reload the same entity and get value from there.
So views could be just a performance optimization hint.
Created issue.

2 Likes

You just made me so happy…:sunglasses: this is exactly why I opened this thread, I think its the same as your thoughts:

Hi all,

First of all, thanks to @avi.fatal for touching such an interesting subject. We also were thinking a lot about the problems, which have been highlighted in the topic. @knstvk has mentioned that we have developed a prototype for testing a brand new approach making data manipulations safe.

Hibernate-like approach of “lazy load everywhere” without partial load is a big pain when you develop a big enterprise app, because it causes unpredictable issues with performance and db load, despite the fact that it is very convenient way for developers - no need to think what you are going to use in business logic, just call it. CUBA views have been designed in response to this problem and, generally, it solves the problem by a clear declaration of what when will be loaded.

As it has been mentioned in this thread, using CUBA views causes some problems and the biggest one that there is no convenient way to understand what fields of an entity are loaded in a certain part of the code. The problem grows when there is a long chain of calls passing entity from one method to another.

After some analysis, we came up with an approach to define views as an interface, like:

public interface SampleWithUserView extends BaseEntityView<SampleEntity> {
    String getName();
    UserMinimalView getUser();
    interface UserMinimalView extends BaseEntityView<User>{
        String getName();
        String getLogin();
    }
}

BaseEntityView is not just a marker interface, it provides a couple of very useful methods:

  • transform(targetView) - returns the same entity in another View wrapper (works lazy)
  • getOrigin() - returns the entity it wraps.

In short, the advantages are:

  • No unfetched attribute exceptions - working with this views you will not be able to even call a field that is not loaded
  • Views can be used as an API, e.g. your service consumes SampleWithUserView, so you can be sure that all the fields you call are loaded
  • You can mix views, as your view interface can implement multiple interfaces (like multiple inheritance)
  • You can introduce a @MetaProperty in views interfaces by implementing default methods, e.g. for calculating subtotals.
  • You can define true read-only properties on the view level, just declare only a getter without a setter.

You are welcome to see the PoC of this approach here. Its readme.md provides quite detailed information. It is developed as an application component, so you can even try it with minimum efforts. To see ViewInterfaces in action, have a look at the implemented tests.

Regards,
Aleksey

P.S. As @pfurini question, at first we also wanted to organize inheritance model this way, but it didn’t work. One reason is a number of views per entity. It’s normal to have 20-30 views for the same entity, so it means that you will need to implement them all… or how? Another reason is dealing with references to other entities. In a JPA entity you should operate with JPA entity in getters and setters, however, it will ruin the concept of safety. In our current concept you always operate with views in the absolutely safe way.

1 Like

The advantages you just mentioned are interesting.

  1. When will this be production ready?
  2. Will this be supported by the studio?
  3. What about konstantin’s comment? Your suggestion is different.

Thanks.

Would be nice if you had a refactoring option in the Studio plugin that could convert an existing xml view file into a new interface-based one.

  1. Hard to say, there is still quite a lot of things to test before we finally decide to adopt it.
  2. Sure.
  3. Which one? :slight_smile: