Spring Data JPA: повторяющиеся записи после сортировки по свойству в объединенной таблице - PullRequest
0 голосов
/ 22 февраля 2020

Я столкнулся с одной проблемой в своем проекте, поэтому приведу пример использования демо-проекта. Представьте, что есть две сущности: учитель и ученик.

@Entity
@Table(name = "STUDENTS")
public class Student {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private String surname;

    @ManyToOne
    private Teacher teacher;

    // getters and setters
}
@Entity
@Table(name = "TEACHERS")
public class Teacher  {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private String surname;

    @OneToMany(mappedBy = "teacher")
    List<Student> students = new ArrayList<>();

    // getters and setters

}

Есть хранилище и класс обслуживания:

public interface TeacherRepository extends PagingAndSortingRepository<Teacher, Long>, JpaSpecificationExecutor<Teacher> {
}
@Service
public class TeacherService {

    @Autowired
    private TeacherRepository teacherRepository;

    @Autowired
    private TeacherToDTOMapper teacherToDTOMapper;

    public Page<TeacherDTO> findAll(Pageable pageable) {
        Specification<Teacher> specification = (root, criteriaQuery, criteriaBuilder) -> {

            return criteriaBuilder.lessThanOrEqualTo(root.get("id"), 2);
        };
        return teacherRepository.findAll(specification, pageable).map(teacherToDTOMapper::mapEntityToDTO);
    }
}

И, наконец, класс контроллера :

@RestController
public class TeacherController {

    @Autowired
    private TeacherService teacherService;

    @GetMapping("/teachers")
    @ResponseStatus(HttpStatus.OK)
    public PagedModel<EntityModel<TeacherDTO>> findAll(PagedResourcesAssembler<TeacherDTO> resourcesAssembler, Pageable pageable) {
        Page<TeacherDTO> page = teacherService.findAll(pageable);
        return resourcesAssembler.toModel(page);
    }
}

В пользовательском интерфейсе имеется таблица учителей .

Пользователь отфильтровывает учителей с идентификатором> 2 и сортирует их по именам своих учеников. Ожидаемый результат должен содержать Aliyah Wiggins и Macy Reyes именно в этом порядке (потому что ученица Амина Паркс должна быть первой после сортировки). http://localhost: 8080 / учителя? Страница = 0 & размер = 3 & сортировка = Students.name, как c

Однако фактический результат:

{
  "_embedded": {
    "teacherDTOList": [
      {
        "id": 1,
        "name": "Aliyah",
        "surname": "Wiggins",
        "students": [
          {
            "id": 1,
            "name": "Amina",
            "surname": "Parks"
          },
          {
            "id": 2,
            "name": "Caleb",
            "surname": "Baker"
          },
          {
            "id": 3,
            "name": "Valentino",
            "surname": "Kent"
          },
          {
            "id": 4,
            "name": "Jaron",
            "surname": "Luna"
          },
          {
            "id": 5,
            "name": "Cayden",
            "surname": "Hoover"
          }
        ]
      },
      {
        "id": 1,
        "name": "Aliyah",
        "surname": "Wiggins",
        "students": [
          {
            "id": 1,
            "name": "Amina",
            "surname": "Parks"
          },
          {
            "id": 2,
            "name": "Caleb",
            "surname": "Baker"
          },
          {
            "id": 3,
            "name": "Valentino",
            "surname": "Kent"
          },
          {
            "id": 4,
            "name": "Jaron",
            "surname": "Luna"
          },
          {
            "id": 5,
            "name": "Cayden",
            "surname": "Hoover"
          }
        ]
      },
      {
        "id": 2,
        "name": "Macy",
        "surname": "Reyes",
        "students": [
          {
            "id": 6,
            "name": "Jasmin",
            "surname": "Friedman"
          },
          {
            "id": 7,
            "name": "Randall",
            "surname": "Archer"
          },
          {
            "id": 8,
            "name": "Caroline",
            "surname": "Davila"
          },
          {
            "id": 9,
            "name": "Sarah",
            "surname": "Pace"
          }
        ]
      }
    ]
  },
  "_links": {
    "self": {
      "href": "http://localhost:8080/teachers?page=0&size=3&sort=students.name,asc"
    }
  },
  "page": {
    "size": 3,
    "totalElements": 3,
    "totalPages": 1,
    "number": 0
  }
}

Там являются дубликатами записей для Алии Виггинс. Я не нашел подходящего способа получения ожидаемого результата, поэтому сейчас я думаю, что поиск объектов, связанных с одним объектом, с сортировкой по свойству в другом объекте не является правильным. Демо-проект доступен здесь .

Ответы [ 3 ]

0 голосов
/ 24 февраля 2020

Вы можете использовать именованный граф сущностей, чтобы избежать дублирования записей.

@NamedEntityGraph(name = "Teacher.students" ,attributeNodes = @NamedAttributeNode("students"))
@Entity
@Table(name = "TEACHERS")
public class Teacher  {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private String surname;

    @OneToMany(mappedBy = "teacher")
    List<Student> students = new ArrayList<>();

    // getters and setters

}

И переопределить метод findAll как

@EntityGraph(value = "Teacher.students" , type = EntityGraphType.LOAD)
Page<Teacher> findAll(Specification<Teacher> spec, Pageable pageable);
0 голосов
/ 12 апреля 2020

Я пришел к выводу, что пытаться сортировать учителей (например, «Один» в отношениях «один ко многим») по именам студентов (то есть «много») неправильно. После попытки сортировки по именам учащихся мы получим следующий результат:

ROW    TEACHER_NAME     TEACHER_SURNAME     STUDENT_FULL_NAME
1      Evan             Fuentes             Allyson Noble
2      Aliyah           Wiggins             Amina Parks
3      Evan             Fuentes             Ashlyn Berg
4      Aliyah           Wiggins             Caleb Baker
...

Учитель может быть первым, третьим и последним одновременно. Следовательно, этот король сортировки не имеет смысла. Более того, «многие ко многим» лучше подходит для примера, но все же не меняет факт.

0 голосов
/ 23 февраля 2020

Вы можете настроить запрос так, чтобы он возвращал различные значения в спецификации:

criteriaQuery.distinct(true);

Это обычно устанавливается в значение true данными Spring для методов findAll, но это не подразумевается, когда вы используете свой собственный Спецификация. Теперь вы видите результаты запроса на присоединение к базе данных (учитель будет повторяться один раз для каждого учащегося).

Я предлагаю добавить свойство флага в модель вместо пропуска по идентификатору. Введение чего-то вроде hidden / deleted / private упростит код и сделает его более читабельным. Затем вы можете использовать метод интерфейса пустого хранилища: findByActiveTrue();

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