【问题标题】:JPA Join on specific fieldJPA加入特定领域
【发布时间】:2015-09-29 13:11:22
【问题描述】:

我有这种情况:

User及其相关的UserRole实体类,如下:

@Entity
@Table(name="USER")
public class User implements Serializable {

   @Id
   @GeneratedValue(strategy=GenerationType.AUTO)
   @Column(name="ID", unique=true, nullable=false)
   private int id;

   @Column(name="USERNAME", unique=true, nullable=false, length=255)
   private String username;

   @OneToMany(mappedBy="user")
   private List<UserRole> userRoles;
}

@Entity
@Table(name="user_roles")
public class UserRole implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="user_role_id", unique=true, nullable=false)
    private int userRoleId;

    @Column(nullable=false, length=45)
    private String role;

    @ManyToOne
    @JoinColumn(name="username", nullable=false)
    private User user;
}

现在,我需要查询所有具有特定角色的用户。我正在尝试加入 JPA 规范,就像这样:

Join<User, UserRole> join = root.join(User_.userRoles);
Expression<String> match = join.get(UserRole_.role);                    
Predicate predicate = builder.equal(match, "ROLE_USER");

问题是生成的联接将在 User.id 和 UserRole.username 之间,查询显然没有结果。

select count(user0_.ID) as col_0_0_ from USER user0_ inner join
user_roles userroles1_ on user0_.ID=userroles1_.username where 
userroles1_.role='ROLE_USER'

我需要在 username 字段上都有 on 子句:

... from USER user0_ inner join
    user_roles userroles1_ on user0_.USERNAME=userroles1_.username ...

我注意到 Join 类有 .on 方法:

根据指定的ON修改join限制结果 健康)状况。替换之前的 ON 条件(如果有)。返回加入 对象

这是正确的方法吗?如果是这样,我该如何实现?

Join<User, UserRole> join = root.join(User_.userRoles).on(????);

提前谢谢你。

更新UserRole_元模型类

@StaticMetamodel(UserRole.class)
public class UserRole_ {
    public static volatile SingularAttribute<UserRole, Integer> userRoleId;
    public static volatile SingularAttribute<UserRole, String> role;
    public static volatile SingularAttribute<UserRole, User> user;
}

User_元模型类:

@StaticMetamodel(User.class)
public class User_ {
    public static volatile SingularAttribute<User, Integer> id;
    public static volatile SingularAttribute<User, String> username;
    public static volatile ListAttribute<User, UserRole> userRoles;
}

【问题讨论】:

  • 您可以使用 JPA 2.1 ON 子句(EclipseLink 在其最新版本中支持此功能),但您必须使用 JPQL 而不是 Criteria。只要我担心,我不明白为什么您的 Critera 请求不起作用,因为表 user_roles 的列 username 将包含用户 ID 而不是其名称(因为用户 ID 是@IdUser)
  • 谢谢@clapsus 你能发一个例子吗?
  • @clapsus user_roles 的列username 包含用户的用户名,因此on 子句应该在两个表的username 列之间
  • 如何填满你的桌子?使用 JPA,还是手动(sql 脚本...等)?
  • 使用 JPA,它是一种用户配置应用程序@clapsus

标签: java hibernate jpa join specifications


【解决方案1】:

你需要使用referencedColumnName:

@ManyToOne
@JoinColumn(name="username", referencedColumnName="username", nullable=false)
private User user;

只有@JoinColumn(name="username") 你告诉Hibernate user_roles 中的连接列被命名为username - 但它仍然希望它包含用户的@Id 属性的值。如果您为您的架构创建 DDL,您将看到 Hibernate 为 user_roles.username 生成一个数字列。

【讨论】:

    【解决方案2】:

    再一次,你应该检查一下user_roles.username列包含什么,因为默认情况下,它包含引用实体的@Id列,这里引用的实体是User,它的@Id列是id(在您的架构中:ID)而不是 username(在您的架构中:USERNAME)。

    不过,这里有一个示例,说明如何编写您在此处描述的内容:

    问题是生成的连接将在 User.id 和 UserRole.username 和查询显然不会有结果。我需要 而是在用户名字段上都有 on 子句:

    在 JPQL 中(使用 JPA 2.1)

    // get entity manager as 'em'
    TypedQuery<User> q = em.createQuery("SELECT u FROM User u INNER JOIN UserRole ur ON ur.user = u.username WHERE ur.role = 'ROLE_USER'", User.class);
    List<User> results = q.getResultList();
    

    【讨论】:

    • user_roles.username 包含用户的用户名。这是 spring security 将查询权威.authoritiesByUsernameQuery("select username, role from user_roles where username=?") 的表。除非没有必要,否则我不想更改表定义,如果可能的话,需要使用 JPA 规范生成Predicate。真的没有办法吗?
    • 我用一种可能的解决方案更新了我的答案,你可以试试这个。
    • 在哪里实现 getUsername()? UserRole_ 是一个 metamodel 类,它的属性是 Attribute&lt;X, T&gt; 的实例。我不明白你的意思。能否请您解释一下?
    • 这是 UserRole_ 元模型类:@StaticMetamodel(UserRole.class) public class UserRole_ { public static volatile SingularAttribute&lt;UserRole, Integer&gt; userRoleId; public static volatile SingularAttribute&lt;UserRole, String&gt; role; public static volatile SingularAttribute&lt;UserRole, User&gt; user; } 是的,我知道泛型是什么意思。无论如何,User 实体类的每个属性都有 getter 和 setter,所以已经有了 getUsername()
    • 错误尝试...生成:org.hibernate.hql.internal.ast.InvalidWithClauseException: with clause can only reference columns in the driving table [select generatedAlias0 from User as generatedAlias0 inner join generatedAlias0.userRoles as generatedAlias1 with generatedAlias1.user=generatedAlias0.username where generatedAlias1.role=:param0]
    猜你喜欢
    • 1970-01-01
    • 2019-01-05
    • 2010-09-06
    • 1970-01-01
    • 1970-01-01
    • 2013-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多