JpaRepository с Native @Query на разных таблицах - PullRequest
0 голосов
/ 06 ноября 2018

Я только что наткнулся на неожиданное поведение весенних данных. Для демонстрации я настроил приложение для весенней загрузки с Initializr (https://start.spring.io/) с добавлением JPA, Web, H2.

Приложение содержит две таблицы и некоторые данные:

data.sql:

create table table1 (
  id int,
  name varchar(50)
);

create table table2 (
  id int,
  name varchar(50)
);

insert into table1 (id, name) values (1, 'First row from table 1');
insert into table1 (id, name) values (2, 'Second row from table 1');
insert into table1 (id, name) values (3, 'Third row from table 1');
insert into table1 (id, name) values (4, 'Fourth row from table 1');

insert into table2 (id, name) values (1, '** TABLE 2: 1st ROW UPPERCASE **');
insert into table2 (id, name) values (2, '** TABLE 2: 2nd ROW UPPERCASE **');

Существует только одна модель для этой структуры таблицы, поскольку структура одинакова для обеих таблиц. Я создал JpaRepository для этой таблицы

SampleDAO:

@Repository
public interface SampleDAO extends JpaRepository<SampleModel, Integer> {

  @Query(value = "select id, name from table1", nativeQuery = true)
  List<SampleModel> findAllFromTable1();

  @Query(value = "select id, name from table2", nativeQuery = true)
  List<SampleModel> findAllFromTable2();
}

Наконец-то я добавил контроллер (TestController):

@RestController
public class TestController {

  @Autowired
  private final SampleDAO sampleDAO;

  public TestController(SampleDAO sampleDAO) {
    this.sampleDAO = sampleDAO;
  }

  @GetMapping(path = "/")
  @ResponseBody
  public String testNativeQuery() {
    List<SampleModel> list1 = this.sampleDAO.findAllFromTable1();
    List<SampleModel> list2 = this.sampleDAO.findAllFromTable2();

    SampleModel m1 = list1.get(0);
    SampleModel m2 = list2.get(0);
    System.out.println("*****************************************");
    System.out.println("Data from findAllFromTable1():");
    list1.forEach(l -> {
      System.out.println(l.getName());
    });
    System.out.println("*****************************************");
    System.out.println("Data from findAllFromTable2():");
    list2.forEach(l -> {
      System.out.println(l.getName());
    });
    System.out.println("*****************************************");

    return "Done";
  }
}

Я ожидал, что list1 и list2 будут содержать данные двух моих таблиц, но, к удивлению, будет получен результат только первой таблицы:

*****************************************
Data from findAllFromTable1():
First row from table 1
Second row from table 1
Third row from table 1
Fourth row from table 1
*****************************************
Data from findAllFromTable2():
First row from table 1
Second row from table 1
*****************************************

Пример проекта можно найти здесь: https://github.com/steinmann321/nativequerydemo

Это ожидаемое поведение или я что-то не так делаю?

(Обратите внимание: это только пример проекта, реальные запросы гораздо сложнее, но результат тот же)

1 Ответ

0 голосов
/ 06 ноября 2018

У вас возникла проблема с конфликтом идентичности между постоянными сущностями. Вы можете видеть, что вы получаете четыре записи из первого запроса и две записи из второго, что соответствует записям в базе данных. Но поскольку диспетчер сущностей обрабатывает записи из второго набора, он видит, что уже существуют постоянные сущности с идентификаторами 1 и 2, поэтому он не собирается создавать новые сущности.

Если вы хотите, чтобы это работало, вам нужно указать, что у вас есть составной ключ, который состоит из идентификатора и имени. Это также будет соответствовать бизнес-логике в вашем методе equals.

Создание встраиваемого ключа JPA:

@Embeddable
public class MyKey implements Serializable {

    private static final long serialVersionUID = 1L;

    @Column(name = "id", nullable = false)
    private String id;

    @Column(name = "name", nullable = false)
    private String name;

    public String getId() {
      return id;
    }

    public void setId(String id) {
      this.id = id;
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) {
        return true;
      }
      if (o == null || getClass() != o.getClass()) {
        return false;
      }
      MyKey that = (MyKey) o;
      return Objects.equals(id, that.getId()) && Objects.equals(name, that.getName());
    }

    @Override
    public int hashCode() {
      return Objects.hash(id, name);
    }
}   

Затем используйте встроенный ключ в вашей сущности SampleModel:

@Entity
public class SampleModel {

  @EmbeddedId
  private MyKey myKey;

  public MyKey getMyKey() {
    return myKey;
  }

  public void setMyKey(MyKey myKey) {
    this.myKey = myKey;
  }
}

Ваш контроллер изменится немного, чтобы получить значения из ключа.

System.out.println("*****************************************");
System.out.println("Data from findAllFromTable1():");
list1.forEach(l -> {
  System.out.println(l.getMyKey().getName());
});
System.out.println("*****************************************");
System.out.println("Data from findAllFromTable2():");
list2.forEach(l -> {
  System.out.println(l.getMyKey().getName());
});
System.out.println("*****************************************");

Тогда попробуйте свой TestController. Я попробовал это, и вот результат:

*****************************************
Data from findAllFromTable1():
First row from table 1
Second row from table 1
Third row from table 1
Fourth row from table 1
*****************************************
Data from findAllFromTable2():
** TABLE 2: 1st ROW UPPERCASE **
** TABLE 2: 2nd ROW UPPERCASE **
*****************************************
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...