Hibernate Filter is not applied for FindOne CRUD operation
Asked Answered
U

4

5

I do have this hibernate filter in my repository:

@Entity
@Audited
@DiscriminatorValue(value = "GROUP")
@FilterDef(name = "groupACL", parameters = @ParamDef(name = "userId", type = "long"))
@Filters({
    @Filter(name = "groupACL", condition = "app_group_id IN (SELECT DISTINCT APP_GROUP_ID FROM APP_GROUP START WITH APP_GROUP_ID IN (SELECT UG.APP_GROUP_ID FROM USER_GROUP UG JOIN APP_USER AU ON AU.APP_USER_ID = UG.APP_USER_ID WHERE USER_ID=:userId) CONNECT BY PARENT_APP_GROUP_ID = PRIOR APP_GROUP_ID)", deduceAliasInjectionPoints = false) })
public class Group extends AbstractGroup {

It is triggered using Spring AOP with the following class:

@Component
@Aspect
public class ACLFilterAspect {
private static final String GROUP_ACL = "groupACL";

@Autowired
private EntityManager em;

@Before("execution(* com.trendshift.kyn.pug.data.GroupRepository.*(..))")
public void forceFilter() {
    Session hibernateSession = em.unwrap(Session.class);
    ....
    hibernateSession.enableFilter(GROUP_ACL).setParameter("userId", userId);
    }
}
}

I finally have the following service:

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class GroupServiceImpl implements GroupService {

  @Autowired
  GroupRepository groupRepository;

  @Override
  public Group findGroupById(long id) {
    Group group = groupRepository.findById(id);
    return group;
  }
}

and these repositories:

@RepositoryRestResource(exported = false)
public interface AbstractGroupRepository<T extends AbstractGroup>
    extends JpaRepository<T, Long>, QueryDslPredicateExecutor<T> {

List<T> findByNameIgnoreCase(String name);

List<T> findByNameAndTypeOrderByNameAsc(String name, String type);

List<T> findByIdOrderByNameAsc(Long id);

AbstractGroup findById(Long id);

}

public interface GroupRepository
    extends AbstractGroupRepository<Group>, GroupRepositoryExtras {

List<Group> findByNameAndCustomerId(String name, Long customerId);

Iterable<Group> findByCustomerIdAndTypeIn(Long id, List<String> types);

Group findById(long id);

}

The issue is that when I use groupRepository.findById(id) the filter is correctly applied.

If I use a CRUD core query groupRepository.findOne(id) the filter is not applied even after processing the Aspect hibernateSession.enableFilter(GROUP_ACL).setParameter("userId", userId); Even if Java enables the filter, the log file doesn't show any trace of the filter in the hibernate query.

The problem seem to be only with the .findOne. findAll is working fine.

Is there something in the Hibernate doc that says that you cannot applied a filter to findOne methods?

Underhung answered 18/7, 2017 at 14:32 Comment(1)
Hi @Underhung did you find any solution..Ecclesiastical
C
2

I used filters to restrict user access to some information based on entity attributes. This was why I wanted even the findOne to respect the filters.

This was the prettiest way that I found to solve this "problem".

  ClassPool pool = ClassPool.getDefault();
  try {
      CtClass cl = pool.get("org.hibernate.loader.plan.exec.internal.EntityLoadQueryDetails");
      CtMethod me = cl.getDeclaredMethod("applyRootReturnFilterRestrictions");
      String s = "{final org.hibernate.persister.entity.Queryable rootQueryable = (org.hibernate.persister.entity.Queryable) getRootEntityReturn().getEntityPersister();" + 
              "$1.appendRestrictions(" +
              "rootQueryable.filterFragment(" +
              "entityReferenceAliases.getTableAlias()," +
              "getQueryBuildingParameters().getQueryInfluencers().getEnabledFilters()" +
              "));}";
      me.setBody(s);
      cl.toClass();
  } catch (Exception e) {}
Clothesline answered 6/7, 2018 at 11:39 Comment(0)
I
2

To answer the actual Question:

Is there something in the Hibernate doc that says that you cannot applied a filter to findOne methods?

Yes, there is. From the Hibernate docs

Filters apply to entity queries, but not to direct fetching. Therefore, in the following example, the filter is not taken into consideration when fetching an entity from the Persistence Context.

entityManager
.unwrap( Session.class )
.enableFilter( "activeAccount" )
.setParameter( "active", true);

Account account = entityManager.find( Account.class, 2L );

assertFalse( account.isActive() );

The implementation of for e.g SimpleJpaRepository.java in Spring uses em.find under the hood. Therefore the request is not filtered. BUT if you override the implementation somehow (e.g. by using a projection or by writing a custom query), so that a query is generated, the request will be filtered. This behaviour can be pretty confusing.

Impedance answered 3/5, 2019 at 13:38 Comment(0)
U
1

I ended up listening for any access to the CRUDRepository class. Not sure if that's the best way but that solves my issue.

@Component
 @Aspect
public class ACLFilterAspect {
    private static final String GROUP_ACL = "groupACL";

    @Autowired
    private EntityManager em;

    @Before("||execution(* *(..)) && this(com.trendshift.kyn.pug.data.GroupRepository)"
            + "||execution(* *(..)) && this(org.springframework.data.repository.CrudRepository)")
Underhung answered 21/8, 2017 at 12:40 Comment(1)
Are you sure above code added the filter to findOne calls, I added the following code @Before("execution(* org.springframework.data.repository.CrudRepository.*(..))") still not working. Do you have any other suggestions.Ecclesiastical
H
1

Just override findById and use getById instead

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer>, SupportedRepositoryOperation<Customer> {

    default Optional<Customer> findById(Long aLong) {
        throw new OperationFindByIdNotAllowedException();
    }

    Optional<Customer> getById(Long id);
}
Homologue answered 19/6, 2020 at 18:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.