当前位置: 首页 > 知识库问答 >
问题:

JpaRepository与不同表上的本机@查询

须新
2023-03-14

我只是在Spring数据中偶然发现了一些意想不到的行为。为了演示我设置了一些Spring Boot应用程序,其中添加了JPA、Web、H2https://start.spring.io/。

该应用程序包含两个表和一些数据:

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

示例DAO:

@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个答案

苏雅珺
2023-03-14

您遇到了跨持久实体的身份冲突问题。您可以看到,第一个查询中有四条记录,第二个查询中有两条记录与数据库中的记录相匹配。但是当entitymanager处理第二组记录时,它看到已经有ID为1和2的持久实体,因此它不会创建新的实体。

如果你想这样做,你需要指出你有一个由id和名称组成的复合键。这也将与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 **
*****************************************
 类似资料:
  • 我是JPA新手。目前,我正在编写一个带有注释的本机查询。我有一个类似下面的类 我正在编写两个查询,其中一个查询在选择投影中包含字段2,而另一个不包含字段2。如何对这两个查询使用相同的类?我尝试了瞬态注释。但它使两个查询的值都为null。

  • 我想知道这是否可能。如果我们遇到这样的情况: 和 我们有两个完全相同的where子句,有没有办法创建一些东西来重用它?

  • 我正在使用JPA构建我的第一个Spring Boot应用程序,并像这样设置了我的数据存储库和服务: 然后是粗制滥造的服务 服务实现的抽象类 最后,一个扩展上述服务类的实际服务类示例: 我写了一些服务层逻辑和控制器,一旦我对第一次迭代感到满意,我就做了一些邮递员测试,这些测试工作顺利。

  • 我在ClientRespository中实现此方法时遇到问题: Ex0019:启动Application ationContext时出错。要显示条件报告,请在启用调试的情况下重新运行您的应用程序。2018-05-23 10:33:32.606ERROR 15048 --- [ restartedMain]o.s.boot.SpringApplication:应用程序运行失败 org.springf

  • 我有稍微不同的选择查询从同一个表中读取数据。 和正在更改。 在Laravel中,我最终为每种类型使用了1个查询,但是有10种类型,这意味着10 db连接——这不好。 将这些查询合并到一个查询中,以便建立一个数据库连接以获取所有数据的好方法是什么? (我正在寻找Laravel查询生成器实现这一点的方法)

  • 我有这样的查询语句: 如果我在jpa中与querydsl(com.querydsl)一起使用它(这是scala,不重要): 当我调试测试时,它总是抛出 对方法public abstract Purchase PurchaseRepository.GetByTrackingNo(java.lang.String)使用命名参数,但在注释查询“从T_EC_PURCHASE t中选择t.pt_note,t