【问题标题】:hibernate - filter relation at runtimehibernate - 在运行时过滤关系
【发布时间】:2014-11-08 01:36:21
【问题描述】:

我有以下 POJO:

@Entity(name = "member")
@Table(name = "member")
public class Member {
    @Column(name = "identifier")
    @Id
    @GeneratedValue
    private int mIdentifier;

    @Column(name = "name", columnDefinition = "text")
    private String mName;

    @ManyToMany(mappedBy = "mMembers")
    private Set<Project> mProjects = new HashSet<Project>();

    public int getIdentifier() {
        return mIdentifier;
    }

    public String getName() {
        return mName;
    }

    public void setName(final String pName) {
        mName = pName;
    }

    public Set<Project> getProjects() {
        return mProjects;
    }

    public Member() {
    }
}

@Entity(name = "project")
@Table(name = "project")
public class Project {
    @Column(name = "identifier")
    @Id
    @GeneratedValue
    private int mIdentifier;

    @ManyToMany(cascade = CascadeType.ALL)
    private Set<Member> mMembers = new HashSet<Member>();

    public int getIdentifier() {
        return mIdentifier;
    }

    public Set<Member> getMembers() {
        return mMembers;
    }

    public Project() {
    }
}

以下代码获取某个项目,并返回所有成员:

Project project = (Project) db.load(Project.class, 1);

System.out.println(project);
System.out.println(project.getMembers());

如何过滤项目的成员?例如,假设我只想要名称以“a”开头的成员。我当然可以做一些客户端过滤(例如使用 Guava 和 Predicate),但让 Hibernate 更改 SQL 查询更有意义。

我知道Hibernate filters,但我认为它们不是为此而设计的。我认为它们对于全局过滤更有用,而不是对特定对象的临时关系过滤。

非常欢迎指向文档、术语等。我对 Hibernate 还很陌生,很难获得这个问题的文档。

编辑

我尝试了标准。我有两个成员,“John Snow”和“Snow”。以下仍然返回两个成员。

final Criteria criteria = db.createCriteria(Project.class)
        .createCriteria("mMembers")
        .add(Restrictions.eq("mName", "John Snow"));

for (final Project project : (List<Project>) criteria.list()) {
    System.out.println(project.getIdentifier());

    for (final Member member : project.getMembers()) {
        System.out.println(member.getName());
    }
}

有什么想法吗?

编辑(2)

似乎执行了第二个查询,以获取所有成员。我们如何才能防止这种情况发生,并且只依赖基于标准的数据?

Hibernate: 
    select
        this_.identifier as identifi1_1_1_,
        mmembers3_.mProjects_identifier as mProject1_1_,
        mmembers1_.identifier as mMembers2_2_,
        mmembers1_.identifier as identifi1_0_0_,
        mmembers1_.name as name2_0_0_ 
    from
        project this_ 
    inner join
        project_member mmembers3_ 
            on this_.identifier=mmembers3_.mProjects_identifier 
    inner join
        member mmembers1_ 
            on mmembers3_.mMembers_identifier=mmembers1_.identifier 
    where
        mmembers1_.name=?
1
Hibernate: 
    select
        mmembers0_.mProjects_identifier as mProject1_1_1_,
        mmembers0_.mMembers_identifier as mMembers2_2_1_,
        member1_.identifier as identifi1_0_0_,
        member1_.name as name2_0_0_ 
    from
        project_member mmembers0_ 
    inner join
        member member1_ 
            on mmembers0_.mMembers_identifier=member1_.identifier 
    where
        mmembers0_.mProjects_identifier=?
Snow
John Snow

编辑 (3)

看上面的第一个查询,这并不是我想要的。本质上,我想要两个查询:

  1. 第一个查询获取某个项目。此查询从表 project 中选择。
  2. 第二个查询获取所有成员,匹配特定条件。当调用getMembers() 时,该查询会延迟执行。此查询从表 member 中选择并加入 project_member

我不想将其合并到一个查询中并从表 project_member 中进行选择,因为这会导致大量的解析开销。我不想以笛卡尔积结束。

这可能吗?

【问题讨论】:

标签: java hibernate runtime filtering relation


【解决方案1】:

正如我在评论中提到的,我建议在 hibernate 中学习标准和/或 hql 文档。举个简单的例子,使用标准你可以执行以下操作(我假设 Spring 的会话工厂,但其他地方的 api 相同):

Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Project.class);

List filteredProjects = criteria.createAlias("member", "member") //this will join members entity
        .add( Restrictions.ilike("member.mName", "a", MatchMode.ANYWHERE) ) //this will make like, with ignore case and at start and end
        .list(); //and finally, list your entities

如果您想要项目的不同结果,也很简单,只需再添加一种方法:

List filteredProjects = criteria.createAlias("member", "member")
        .add( Restrictions.ilike("member.mName", "a", MatchMode.ANYWHERE) )
        .setProjection(Projections.distinct(Projections.property("mIdentifier"))) //this will make distinct on your id property
        .list();

使用 HQL,第二个查询如下所示:

List projects = session.createQuery("select distinct p from Project p join p.mMembers as m where m.mName like :param")
       .setParameter("param", "%a%")
       .list();

编辑:你想要的功能看起来像这样:

@Entity(name = "project")
@Table(name = "project")
public class Project {
    @Column(name = "identifier")
    @Id
    @GeneratedValue
    private int mIdentifier;

    @ManyToMany(cascade = CascadeType.ALL)
    private Set<Member> mMembers = new HashSet<Member>();

    public int getIdentifier() {
        return mIdentifier;
    }

    public Set<Member> getMembers() {
        return mMembers;
    }

    public Project() {
    }

    @Formula("<YOUR SQL TO FILTER RESULT>")
    @Basic(fetch=FetchType.LAZY) 
    public Set<Member> getFilteredMembers() {
        return mMembers
    }
}

但是根据这个:Is it possible to make @Formula annotation lazily loaded? 你需要一些额外的工作才能让它懒惰地工作。

【讨论】:

  • 感谢您的回答。请参阅我的编辑。我尝试了与标准类似的东西(我没有包括别名),但没有成功。你有什么想法吗?
  • 双createCriteria生成什么SQL?我不确定这是否是正确的加入方式,请尝试使用别名
  • 见编辑(2)!我也尝试了别名,但第二个查询总是发生。
  • 好吧,我刚刚意识到,您的 getter/setter 不遵循 java 约定。尝试使其正确并重试
  • 能否详细说明?
【解决方案2】:

新的 JPA 2.1 指定了在 JOIN 中使用 ON 条件的可能性:

select p from Project p left join p.members m ON m.name like 'a%'

【讨论】:

    猜你喜欢
    • 2010-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-04
    • 2012-01-09
    • 2020-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多