NPE in Taggable add on

Hi, I am having a problem with the Taggable add on. A great feature that is easy to implement. But I sometimes get a Null Pointer Exception when clicking on a tag link. I think what is happening is that some of my entities are set to hard delete. When an entity with a tag is hard deleted, it does not cascade to the Tagging entity. So the Tagging entry is there with a null Taggable property.

Is it possible to update the add on to cascade delete? If not, is there a work around?

Thanks.

Hi,

In this case - right. The addon will not behave as expected, because it stores a String reference to the entity instance. If the instance is not there anymore - it will not work right now.

For now what you can do is that you create an entity listener of your entity that you want to remove. In this entity listener you can search within the Tagging entity for entries where the taggable attribute is your entity (something like this):

private List<Tagging> getTaggings(Entity entity) {
    dataManager.load(Tagging.class)
      .query("select e from ddct$Tagging e where e.taggable = :taggable")
      .parameter("taggable", entity)
      .list()
}

Having those entities at hand, you can delete all of them once again through dataManager.

That should do the trick. Perhaps I will include a functionality into the component itself so that it gets a little bit easier to achieve.

Bye
Mario

Ah, right, a String reference. OK, so I can add a listener to any of my entities that are taggable.

But ironically, the way I encountered this is by installing your Attachable add on. (BTW, thank you for creating these wonderful add ons!). I added an attachment and tagged it. Then I removed the attachment, which led to the situation above. Even though they are soft deleted, the taggable reference is lost… I don’t think I am able to add a listener to an entity that is not “mine”, right?

My next idea is to create a scheduled task to “clean up” the Tagging entities every minute. Does that sound best?

Thanks

thanks :slight_smile:

In fact it is possible to add entity listeners to entities from addons - take a look here for an example: Running Code on Startup - CUBA Platform. Developer’s Manual

You ca give it a try. You are right with the attachable thing. I will think on how to solve that…

Thanks. That is very cool.

My listener is working but I’m having trouble with the select. If I run it as you suggested, I get:
IllegalArgumentException: You have attempted to set a value of type class java.util.UUID for parameter taggable with expected type of interface com.haulmont.cuba.core.entity.Entity from query string select e from ddct$Tagging e where e.taggable = :taggable.

I tried running a slightly different query:
where e.taggable.id = :taggable
But I got:

JPQLException: 
Exception Description: Problem compiling [select e from ddct$Tagging e where e.taggable.id = :taggable]. 
[35, 48] The state field path 'e.taggable.id' cannot be resolved to a valid type.

The custom datatype / soft reference has got me confused. I even tried converting the entity to a String using EntitySoftReferenceConverter and then setting that as my parameter value, but I got:

IllegalArgumentException: You have attempted to set a value of type class java.lang.String for parameter taggable with expected type of interface com.haulmont.cuba.core.entity.Entity from query string select e from ddct$Tagging e where e.taggable = :taggable.

Sorry to be a bother.

hi,

yes - you are right. I mixed it up. There is a gotcha because of implicit type conversion of entity instances.

You can take a look at the TaggingService (cuba-component-taggable/TaggingServiceBean.groovy at master · mariodavid/cuba-component-taggable · GitHub) - there I did it also:

private List<Tagging> getTaggingsForEntity(Entity entity) {
        LoadContext.Query query = LoadContext.createQuery('select e from ddct$Tagging e where e.taggable = :taggable')
        query.setParameter('taggable', entity, false)
        LoadContext<Tagging> loadContext = LoadContext.create(Tagging)
                .setQuery(query).setView(TAGGING_VIEW_NAME)
        return dataManager.loadList(loadContext)
    }

The third parameter false deactivates the implicit type conversion. This is the way it should work :slight_smile:

Yep, that worked. Thanks!