使用 Spring Boot 框架对基于 Spring JPA 的 DAO 进行分层的正确方法

Posted

技术标签:

【中文标题】使用 Spring Boot 框架对基于 Spring JPA 的 DAO 进行分层的正确方法【英文标题】:Proper Way to layer Spring JPA based DAO using Spring Boot Framework 【发布时间】:2015-06-05 12:36:44 【问题描述】:

刚接触 Spring Boot 和 JPA...

假设我有两个实体映射到连接到数据库中的两个表。

学生1-----

另外,假设数据库已经创建并填充。

这描述了一个学生有很多课程......

我的学生实体:

@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 Boot 的 Tutorial Guides 中,它说明了如何扩展 CrudRepository 接口,但是 它没有指定如何设置基于 Spring 的 DAO,其中包含使用 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");
       
 

这是在 Spring DAO 中使用 HQL 的标准方式吗?我已经看到了将 @Transactional 注释添加到 DAO 类的 impl 的示例(例如 CustomerDAOImpl)?

如果这是构建我的 Spring Boot 应用程序的写入方式,或者我应该只扩展/添加到 CrudRepository,请告诉我?

如果有人可以纠正我的示例并将我指向一个使用连接的实体讨论 HQL 的 URL,我将非常感激。

Spring Boot 指南没有描述连接或 DAO - 我只需要学习如何正确创建 finder 方法来模拟返回列表或数据结构的 select 语句。

感谢您花时间阅读本文...

【问题讨论】:

【参考方案1】:

如果我理解你的问题是正确的,你确实有两个问题:

    如何创建DAO和DAOImpl? 将您的交易注释放在哪里?

关于第一个问题,我想指出,这是一个关于 spring-data-jpa 使用 Hibernate 作为 JPA 提供程序的问题,而不是 spring-boot

使用 Spring Data 我通常完全跳过创建 DAO,但直接使用自定义存储库扩展标准存储库,如 CrudRepository。因此,在您的情况下,您甚至不必编写比以下更多的代码:

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

    List<Student> findByStudentName(String studentName);


这已经足够了,如果你使用,Spring Data 会用正确的实现来填充它

@Autowired
StudentRepository studentRepo; 

在您的服务类别中。这也是我通常用@Transactional 注释我的方法的地方,以确保一切都按预期工作。

关于您关于 HQL 的问题,请查看 spring data jpa documentation,它指出对于大多数情况,在接口中坚持使用正确的命名方法或进行命名查询就足够了(第 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);


【讨论】:

哪个更好? JPA 还是 Cruid 存储库?我们可以只使用存储库吗?上次我使用hibernate的时候有spring 2.5和hibernate 3,我不仅忘记了它是怎么回事,我也没有使用这些功能,在youtube上新看到它们之前...... JPARepository 实际上是从CRUDRepository 继承的,所以它是处理JPA Pojos 的特定类型。见docs.spring.io/spring-data/jpa/docs/current/api/org/… 非常感谢,' S saveAndFlush(S entity)' :O 不在 crud 中保存操作,对数据库执行所有保存更改?【参考方案2】:

如果您使用@Transactional 注释您的CourseDaoImpl(假设您已正确定义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>();
    
   

【讨论】:

shazin,感谢您的回复...这是执行 HQL 的标准方法吗?您正在选择学生而不是加入?我很困惑......另外,为什么readOnly = true?正确定义 JpaTransactionManager 是什么意思?你会如何在 Spring Boot 中做到这一点?为什么不使用 CrudRespository?我正在寻求的是做事的标准方式......感谢您的帮助。 这个用户类/对象引用在哪里定义?你是说学生吗?

以上是关于使用 Spring Boot 框架对基于 Spring JPA 的 DAO 进行分层的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot:入门篇

使用 Spring Boot 框架对基于 Spring JPA 的 DAO 进行分层的正确方法

Spring boot 1 : 入门篇

Spring Boot

使用Spring Boot开发Web项目

使用Spring Boot+MyBatis框架做查询操作