Правильный путь к слою Spring DAO на основе JPA с использованием Spring загрузочной платформы

Новое в Spring Boot и JPA...

Скажем, у меня есть два объекта, сопоставленные двум таблицам, которые объединены в базу данных.

Стьюдента-1 ------ < -Course

Кроме того, давайте предположим, что база данных уже создана и заполнена.

Это показывает, что у одного студента много курсов...

Моя студенческая сущность:

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

Мой курс:

@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

}

В Spring Руководствах по учебному пособию по загрузке показано, как расширить интерфейс CrudRepository, но он не указывает, как настроить DAO Spring, который содержит настраиваемые методы поиска, которые используют внутри него HQL и EntityManager.

Правильны ли следующие DAO и DaoImpl?

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();
    }
}   

А затем в клиентском коде, например, в основном классе:

 public class Application {

    @Autowired
    CustomerDao dao;

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

Является ли это стандартным способом использования HQL внутри Spring DAOs? Я привел примеры аннотации @Transactional, которые были добавлены к имплантату класса DAO (например, CustomerDAOImpl)?

Пожалуйста, дайте мне знать, является ли это способом записи моего приложения Spring Boot, или я должен только продлить/добавить только CrudRepository?

Если кто-то может исправить мой пример и указать мне URL-адрес, который говорит о HQL, используя Entities, которые были объединены, я был бы очень благодарен.

Руководство по загрузке Spring не отображало объединений или DAO - мне просто нужно узнать, как правильно создавать методы поиска, которые эмулируют оператор выбора, который возвращает списки или структуры данных.

Спасибо, что нашли время, чтобы прочитать это...

Ответ 1

Если я правильно понял ваш вопрос, у вас есть два вопроса:

  • Как создать DAO и DAOImpl?
  • Где разместить аннотации Transaction?

В отношении первого вопроса я хочу указать, что это вопрос в отношении spring-data-jpa использования Hibernate в качестве поставщика JPA, а не spring-boot.

Используя Spring Данные, я обычно пропускаю полностью, чтобы создать DAO, но напрямую использую пользовательский репозиторий, расширяющий стандартный, например CrudRepository. Поэтому в вашем случае вам даже не нужно писать больше кода, чем:

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

    List<Student> findByStudentName(String studentName);

}

Которое будет достаточно, и Spring Данные позаботятся о том, чтобы заполнить его правильной реализацией, если вы используете

@Autowired
StudentRepository studentRepo; 

в вашем классе обслуживания. Здесь я также обычно комментирую свои методы с помощью @Transactional, чтобы убедиться, что все работает как ожидалось.

В отношении вашего вопроса о HQL загляните в документацию spring данных jpa, в которой указывается, что для большинства случаев этого достаточно, чтобы придерживаться к соответствующим именованным методам в интерфейсе или перейти к именованным запросам (раздел 3.3.3) или использовать аннотацию @Query (раздел 3.3.4), чтобы вручную определить запрос, например должен работать (не пробовал):

@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

Если вы аннотируете свой CourseDaoImpl с помощью @Transactional (предположим, что вы правильно определили JpaTransactionManager), вы можете просто получить ученика с соответствующим именем и вызвать метод getCourses() для ленивой загрузки Курсов, прикрепленных к этому ученику, Поскольку findCoursesByStudentName будет работать внутри транзакции, он будет загружать курсы просто отлично.

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