Search code examples
javahibernatejpahibernate-criteria

Hibernate Criteria One to Many issues


I am trying to use Hibernate Criteria api to fetch only the topics based on the USER_ID but have no idea how to do it using the criteria.

My Tables are "topic_users" (below)

enter image description here

and "topics" table (below)

enter image description here

I know how to do it using SQL, this would be something like:

SELECT TOPICNAME 
FROM topic_users INNER JOIN topics on topic_users.TOPICS_TOPICS_ID = topics.TOPICS_ID 
WHERE topic_users.USER_ID = 1

This will return all TOPICNAME of USER_ID 1 which is exactly what I want but how I can do this with Hibernate Criteria. So far I have this in my Repository class (see below) but this will only return a highly nested JSON array. I could loop through the objects, use a DTO and build my response or try the Hibernate createSQLQuery method that will let me call a native SQL statement directly (haven't tried that yet)...but I am trying to learn the Criteria so I hope anyone can answer my query.

@Repository("userTopicsDao")
public class UserTopicsDaoImpl extends AbstractDao<Integer, UserTopics>implements UserTopicsDao {

    @Override
    public List<UserTopics> findMyTopics(int userId) {
        Criteria crit = createEntityCriteria();
        crit.add(Restrictions.eq("userId", userId));
        List<UserTopics> userTopicsList = (List<UserTopics>)crit.list();
        return userTopicsList;
    }

and my TOPIC_USERS Entity where I have mapped the TOPICS

@Entity
@Table(name="TOPIC_USERS")
public class UserTopics {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name="TOPICUSER_ID")
    private Integer id;

    @Column(name="USER_ID")
    private Integer userId;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "TOPICS_ID")
    private Set<Topics> topicsUser;

   //getter and setters

Solution

  • Ok starting from the ground up.. you entity classes should look like this:

    @Entity
    @Table(name="TOPIC_USERS")
    public class UserTopics {
    
        @Id
        @GeneratedValue(strategy= GenerationType.IDENTITY)
        @Column(name="TOPICUSER_ID")
        private Integer id;
    
        @Column(name="USER_ID")
        private Integer userId;
    
        @ManyToOne(fetch = FetchType.EAGER)
        @JoinColumn(name = "TOPICS_TOPICS_ID")
        private Topics topics;
    

    Your Topics class should look like this:

    @Entity
    @Table(name="TOPICS")
    public class Topic {
    
        @Id
        @GeneratedValue(strategy= GenerationType.IDENTITY)
        @Column(name="TOPICUS_ID")
        private Integer id;
    
        @Column(name="TOPICNAME")
        private Integer topicName;
    
        @OneToMany(mappedBy = "topics")
        private Set<UserTopics> userTopics;
    

    Finally the Criteria:

    Version 1) You get entire entity:

    Criteria c = session.createCriteria(Topics.class, "topics");
    c.createAlias("topics.userTopics", "userTopics");
    c.add(Restrictions.eq("userTopics.userId", userId));
    return c.list(); // here you return List<Topics>
    

    Version 2) You project only the topicname:

    Criteria c = session.createCriteria(Topics.class, "topics");
    c.createAlias("topics.userTopics", "userTopics");
    c.add(Restrictions.eq("userTopics.userId", userId));
    c.setProjection(Projections.property("topics.topicName"));
    List<Object[]> results =  (List<Object[]>)c.list(); 
    
    // Here you have to manually get the topicname from Object[] table.
    

    }