我希望这个问题不是基于意见的,但我们走了。 我正在开发一个带有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:问题,课程等永远不会改变所有者,这意味着只有创建的教授才能使用它。
答案 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等,以便为我创建的每个类包含Professor
和enabled
。
所以,我自己的问题的答案:在我看来,在所有类中添加与教授的关联是有效的(在像我这样的情况下,使用JWT身份验证)。我能够减少很多代码,它是一个100%的Spring解决方案。
PS:尚未使用findAll
和Sort
测试Pageable
。