简体   繁体   中英

Hibernate Filter is not applied for FindOne CRUD operation

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?

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) {}

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 eg SimpleJpaRepository.java in Spring uses em.find under the hood . Therefore the request is not filtered. BUT if you override the implementation somehow (eg 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.

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)")

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM