JPA Join on specific field

10,719

You need to use referencedColumnName:

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

With only @JoinColumn(name="username") you tell Hibernate that the join column in user_roles is named username - but it still expects that it contains the values of the @Id property of User. If you create the DDL for your schema you will see that Hibernate generates a number column for user_roles.username.

Share:
10,719
s1moner3d
Author by

s1moner3d

Freelancer Senior Software Architect.

Updated on August 21, 2022

Comments

  • s1moner3d
    s1moner3d over 1 year

    I have this scenario:

    User and his related UserRole entity classes, as below:

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

    and

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

    Now, I need to query all users that have a specific role. I'm trying making join with JPA Specifications, like that:

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

    The problem is that the generated join will be between User.id and UserRole.username and the query will obviously have no results.

    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'
    

    I need instead to have the on clause both on username fields:

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

    I noticed that there is the .on method of Join class who:

    Modify the join to restrict the result according to the specified ON condition. Replaces the previous ON condition, if any. Return the join object

    Is this the correct approach? If so, how could I implement that?

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

    Thank you in advance.

    UPDATE: UserRole_ metamodel class

    @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_ metamodel class:

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