Using @Filter with @ManyToOne relation

2019-09-08 08:47发布

I'm using Hibernate 3.6.7-Final and Spring 3.0.5.

I have entity like this

@Entity
public class Foo {
    @ManyToOne
    private Bar bar;
}

@Entity
public class Bar {
    @Column
    String group;
}

How can I use @Filter in Foo where I want to get all Foo's that have Bar with group = :group? This is supposed to be a security constraint.

Tryied just setting @Filter(name = "groupFilter", condition = "group = :group") at the attribute bar fromFoo but it didn't work. Does hibernate have support for this or Filter only works at entity/collection level? Will I have to change all my HQLs to add this security constraint?

2条回答
我命由我不由天
2楼-- · 2019-09-08 09:18

The problem with using filters with @ManyToOne join comes from the following. I'm referring to Hibernate 4.3.10 as this is what I have to look at.

The relevant SQL fragment is generated by class org.hibernate.engine.internal.JoinSequence, method toJoinFragment:

/**
 * Generate a JoinFragment
 *
 * @param enabledFilters The filters associated with the originating session to properly define join conditions
 * @param includeAllSubclassJoins Should all subclass joins be added to the rendered JoinFragment?
 * @param withClauseFragment The with clause (which represents additional join restrictions) fragment
 * @param withClauseJoinAlias The
 *
 * @return The JoinFragment
 *
 * @throws MappingException Indicates a problem access the provided metadata, or incorrect metadata
 */
public JoinFragment toJoinFragment(
        Map enabledFilters,
        boolean includeAllSubclassJoins,
        String withClauseFragment,
        String withClauseJoinAlias) throws MappingException {

        ...

        final String on = join.getAssociationType().getOnCondition( join.getAlias(), factory, enabledFilters, treatAsDeclarations );

Depending on the defined relationship join.getAssociationType() returns CollectionType or EntityType.

The former stands for declaration like:

@OneToMany
private List<MyEntity> myEntities;

The latter stands for this one:

@ManyToOne
private MyEntity myEntity;

In the first case this method is used:

@Override
public String getOnCondition(
        String alias,
        SessionFactoryImplementor factory,
        Map enabledFilters,
        Set<String> treatAsDeclarations) {
    return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters, treatAsDeclarations );
}

while in the second case the method looks like:

@Override
public String getOnCondition(
        String alias,
        SessionFactoryImplementor factory,
        Map enabledFilters,
        Set<String> treatAsDeclarations) {
    if ( isReferenceToPrimaryKey() && ( treatAsDeclarations == null || treatAsDeclarations.isEmpty() ) ) {
        return "";
    }
    else {
        return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters, treatAsDeclarations );
    }
}

This means that:

  • when we have a list of referenced sub-entities in definition of an entity, filter is unconditionally applied.
  • when we have a single referenced sub-entity, filter is applied when certain conditions are (not) met.

Based on the code, I think that filters can be applied in case of @ManyToOne relationship when:

  • in referenced entity we use a field which is not a primary key, or
  • TREAT operator is used (see, for example, here or here)
查看更多
We Are One
3楼-- · 2019-09-08 09:24

First, you have to create the @FilterDef somewhere (this defines the available parameters, and the default condition), then define the @Filter on a particular class.

Finally, have to enable the filter on the Session object, and set any parameters it requires. Filters are not enabled in hibernate sessions by default. You have to enable the specific ones you want once the session is opened.

See section 19.1 for an example: http://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/filters.html

@FilterDef(name="groupFilter", parameters={@ParamDef( name="group", type="string" )})
@Filters(
  { @Filter(name="groupFilter", condition="group = :group") }
)
public class ... {
}

Then somewhere in your dao code:

session.enableFilter("groupFilter").setParameter("group", group);

You should not have to touch any hql. Whenn you enable the filter, all classes which have an actual @Filter defined for it will automatically apply the condition.

There are additional ways to do things for collections, and i suggest you read the documentation referenced above for that. But in general, you can provide the @Filter annotation classes and on collection properties.

查看更多
登录 后发表回答