9
votes

Am new to Spring Boot & JPA...

Let's say I have two entities mapped to two tables which are joined in a database.

Student-1------<-Course

Also, lets presume that the database is already created and populated.

This depicts that one student has many courses...

My Student Entity:

@Entity
public class Student {

    @OneToMany(mappedBy="student")
    private List<Courses> courses;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "Student_Id")
    private long studentId;

    @Column(name = "Student_Name")
    private String studentName;

    protected Student() { }

    // Getters & Setters
}

My Course Entity:

@Entity
public class Course {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "Course_Id")
    private long courseId;

    @Id
    @Column(name = "Student_Id")
    private long studentId;

    @ManyToOne
    @PrimaryKeyJoinColumn(name="Student_Id", referencedColumnName="Student_Id")
    private Student student;

    @Column(name = "Course_Name")
    private String courseName;

    // Getters & Setters

}

In Spring Boot's Tutorial Guides, it illustrates how to extend a CrudRepository interface, but it doesn't specify how to setup a Spring based DAO which contains custom finder methods which use HQL and EntityManager inside it.

Is the following DAO and DaoImpl correct?

public interface CourseDao {
    List<Course> findCoursesByStudentName(String studentName);
}

@Repository
public class CourseDaoImpl implements CourseDao {

    @PersistenceContext
    EntityManager em;

    public List<Course> findCoursesByStudentName(String studentName) {
        String sql = "select c.courseName" +
                     "from Course c, Student s " +
                     "where c.course_id = s.student_id " +
                     "and s.studentName = :studentName ";

        Query query = em.createQuery(sql);
        query.setParameter("studentName", studentName);
        return query.getResultList();
    }
}   

And then in the client code, for example, in the main class:

 public class Application {

    @Autowired
    CustomerDao dao;

    public static void main (String args []) {
        List<Course> courses = dao.findCoursesByStudentName("John");
    }   
 }

Is this the standard way to use HQL inside Spring DAOs ? I've seend examples of the @Transactional annotation being prepended to the DAO class's impl (e.g. CustomerDAOImpl) ?

Please let me know if this is the write way to structure my Spring Boot app or am I supposed to extend / add to the CrudRepository only?

If someone could correct my example and point me to a URL which talks about HQL using Entities that are joined, I would be very grateful.

The Spring Boot guides didn't depict joins or DAOs - I just need to learn how to properly create finder methods which emulate select statement which return lists or data structures.

Thanks for taking the time to read this...

2

2 Answers

13
votes

If I understood your question correct you do have two questions:

  1. How to create a DAO and DAOImpl?
  2. Where to put your Transaction annotations?

In regards to the first question I want to point out that this is a question in regards to spring-data-jpa using Hibernate as a JPA provider, not spring-boot.

Using Spring Data I usually skip completely to create a DAO but directly use a Custom Repository extending a standard one like CrudRepository. So in your case you don't even have to write more code than:

@Repository
public interface StudentRepository extends CrudRepository<Student, Long> {

    List<Student> findByStudentName(String studentName);

}

Which will be sufficient and Spring Data will take care of filling it with the correct implementation if you use

@Autowired
StudentRepository studentRepo; 

in your service class. This is where I also usually annotate my methods with @Transactional to make sure that everything is working as expected.

In regards to your question about HQL please look into the spring data jpa documentation, which points out that for most of the cases it should be sufficient to stick to proper named methods in the interface or go for named queries (section 3.3.3) or use the @Query annotation (section 3.3.4) to manually define the query, e.g. should work (didn't tried):

@Repository
public interface @CourseRepository extends CrudRepository<Course, Long> {

    @Query("select c.courseName from Course c, Student s where c.course_id = s.student_id and s.studentName = :studentName")
    public List<Course> findCoursesByStudentName(String studentName);

}
2
votes

If you annotate your CourseDaoImpl with @Transactional (Assuming your have defined JpaTransactionManager correctly) You can just retrieve the Student with the matching name and call the getCourses() method to lazy load the Courses attached to that student. Since findCoursesByStudentName will run within a Transaction it will load the courses just fine.

@Repository
@Transactional(readOnly=true)
public class CourseDaoImpl implements CourseDao {

    @PersistenceContext
    EntityManager em;

    public List<Course> findCoursesByStudentName(String studentName) {
        String sql = "select s " +
                     "from Student s " +
                     "where s.studentName = :studentName ";

        Query query = em.createQuery(sql);
        query.setParameter("studentName", studentName);
        User user = query.getSingleResult();
        if(user != null) {
            return user.getCourses();
        }

        return new ArrayList<Course>();
    }
}