更改数据库设计以改进查询

时间:2017-11-10 14:52:19

标签: java spring spring-boot spring-data-jpa

我希望这个问题不是基于意见的,但我们走了。 我正在开发一个带有spring boot,spring data jpa和JWT Token作为身份验证系统的小API。 API非常简单,是一个考试生成器,您可以在其中注册教授,课程,问题,选择等。

例如: [13/11/2017 - 编辑以包含AbstractEntity]

@MappedSuperclass
public class AbstractEntity implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected Long id;
    protected boolean enabled = true;
    //equals, hashcode...
}

public class Professor extends AbstractEntity{ 
    private String email;
}

public class Course extends AbstractEntity {
    @ManyToOne(optional = false)
    private Professor professor;  
}

public class Question extends AbstractEntity {
    @ManyToOne(optional = false)
    private Course course;
}

因为身份验证系统是所有查询的JWT令牌,所以我必须包含教授的信息。

例如

public interface CourseRepository extends PagingAndSortingRepository<Course, Long> {
    @Query("select c from Course c where c.id = ?1 and c.professor = ?#{principal.professor} and c.enabled = true")
    Course findOne(Long id);
}

问题在于,对于每个crud查询,我都必须编写自己的自定义查询,因为教授的信息以及启用的字段。

我知道有一种方法可以扩展PagingAndSortingRepository并编写自己的自定义查询,如下所示:

public interface CustomPagingAndSortRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
    @Override
    @Query("select e from #{#entityName} e where e.id = ?1 and e.professor = ?#{principal.professor} and e.enabled = true")
    T findOne(ID id);
    //More fields overrided here
}

如果我只是担心,这个CustomPagingAndSortRepository可以完美地适用于所有类。这门课程对于课程非常有用,因为e.professor = ?#{principal.professor}会在那里找到教授。但是它对问题不起作用,例如,因为我需要加入课程班才能得到教授,比如e.course.professor = ?#{principal.professor}

关键是我在教授的所有课程中都与教授建立了联系,我可以使通用代码工作,节省大量代码但牺牲了数据库设计的规范化。 问题是:还有另外一种方法吗?或者它可能有效吗?

PS:问题,课程等永远不会改变所有者,这意味着只有创建的教授才能使用它。

2 个答案:

答案 0 :(得分:2)

为什么不创建一个注释@Service的类,使您的CourseRepository自动装配,然后将id, principal.professor, enabled传递给它,然后您的存储库将使用方法Course findByIdAndProfessorAndEnabled(Long id, String professor, Boolean enabled)。 代码段:

public interface CourseRepository extends PagingAndSortingRepository<Course, Long> {
    Course findByIdAndProfessorAndEnabled(Long id, Professor professor, Boolean enabled);
}

@Service
public class CourseService{
    @Autowired
    CourseRepository courseRepository;

    //pass in principal.professor from your controller or from wherever this is called
    public Course findByIdAndProfessorAndEnabled(Long id, Professor  professor, Boolean enabled){
        return courseRepository.findByIdAndProfessor(id, professor, enabled);
    }
}

编辑:适用于Hibernate模型

如果您始终希望enabled在查询中设置为true,则可以在模型上使用@Where注释

@MappedSuperclass
@Where(clause="is_enabled=true")
public class AbstractEntity implements Serializable {
.
.
.
@Column(name="is_enabled")
protected boolean enabled = true;
...

我不知道这种方法是否适用于您的校长,但您可以尝试一下

答案 1 :(得分:0)

好的,我会回答我自己的问题。所以,问题是:

  

关键是如果我在所有类中都与教授建立联系,我可以使通用代码工作,节省大量代码但牺牲数据库设计的规范化。问题是:还有另外一种方法吗?或者它可能有效吗?

我实际上是根据this answer做的,它减少了很多代码。

现在我有这样的模型类(在所有其他类中也包含Professor):

@MappedSuperclass
public class AbstractEntity implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected Long id;
    @Column(columnDefinition = "boolean default true", nullable = false)
    private boolean enabled = true;
// ...
}

@Entity
public class Course extends AbstractEntity {
    private String name;
    @ManyToOne(optional = false)
    private Professor professor;
    //...
}

@Entity
public class Question extends AbstractEntity {
    private String title;
    @ManyToOne(optional = false)
    private Course course;
    @ManyToOne(optional = false)
    private Professor professor;
    // ...
}

像这样的CustomPagingAndSortRepository

@NoRepositoryBean
public interface CustomPagingAndSortRepository<T extends AbstractEntity, ID extends Long>
        extends PagingAndSortingRepository<T,ID> {
    @Override
    @Query("select e from #{#entityName} e where e.professor = ?#{principal.professor} and e.enabled = true")
    Iterable<T> findAll(Sort sort);

    @Override
    @Query("select e from #{#entityName} e where e.professor = ?#{principal.professor} and e.enabled = true")
    Page<T> findAll(Pageable pageable);

    @Override
    @Query("select e from #{#entityName} e where e.id =?1 and e.professor = ?#{principal.professor} and e.enabled = true")
    T findOne(Long id);

    @Override
    default boolean exists(Long id){
        return findOne(id) != null;
    }

    @Override
    @Query("select e from #{#entityName} e where e.professor = ?#{principal.professor} and e.enabled = true")
    Iterable<T> findAll();

    @Override
    @Query("select e from #{#entityName} e where e.professor = ?#{principal.professor} and e.enabled = true")
    Iterable<T> findAll(Iterable<ID> iterable);

    @Override
    @Query("select count(e) from #{#entityName} e where e.professor = ?#{principal.professor} and e.enabled = true")
    long count();

    @Override
    @Transactional
    @Modifying
    @Query("update #{#entityName} e set e.enabled=false where e.id=?1 and e.professor = ?#{principal.professor}")
    void delete(Long id);

    @Override
    @Transactional
    @Modifying
    default void delete(T t){
        delete(t.getId());
    }

    @Override
    @Transactional
    @Modifying
    default void delete(Iterable<? extends T> iterable){
        iterable.forEach(entity -> delete(entity.getId()));
    }

    @Override
    @Transactional
    @Modifying
    @Query("update #{#entityName} e set e.enabled=false where e.professor = ?#{principal.professor}")
    void deleteAll();
}

执行此操作后,我的所有基本查询都将自动包含实体名称,教授以及是否已启用。例如,我的QuestionRepository是这样的:

public interface QuestionRepository extends CustomPagingAndSortRepository<Question, Long> {
    @Query("select q from Question q where q.course.id = ?1 and q.title like %?2% and q.professor = ?#{principal.professor} and q.enabled = true")
    List<Question> listQuestionsByCourseAndTitle(long courseId, String title);
}

现在我不必担心创建自定义删除,自定义findALL或deleteAll等,以便为我创建的每个类包含Professorenabled

所以,我自己的问题的答案:在我看来,在所有类中添加与教授的关联是有效的(在像我这样的情况下,使用JWT身份验证)。我能够减少很多代码,它是一个100%的Spring解决方案。

PS:尚未使用findAllSort测试Pageable