Правильный путь к слою 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>();
}
}