Java N+;ID为字符串时为1(JpaRepository)

Java N+;ID为字符串时为1(JpaRepository),java,hibernate,jpa,spring-data,spring-data-jpa,Java,Hibernate,Jpa,Spring Data,Spring Data Jpa,我有一个字符串id为的实体: @Table @Entity public class Stock { @Id @Column(nullable = false, length = 64) private String index; @Column(nullable = false) private Integer price; } 和JPA对它的假设: public interface StockRepository extends JpaRepo

我有一个字符串id为的实体:

@Table
@Entity
public class Stock {

    @Id
    @Column(nullable = false, length = 64)
    private String index;

    @Column(nullable = false)
    private Integer price;

}
和JPA对它的假设:

public interface StockRepository extends JpaRepository<Stock, String> {
}
在日志中,我有
N
插入项。
当id为数字时,我没有看到类似的行为。
另外,在我的情况下,将id的类型设置为数字并不是最好的解决方案

更新1:
我忘了提到还有一个
Trade
类与
Stock
有多对多关系:

@Table
@Entity
public class Trade {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column
    @Enumerated(EnumType.STRING)
    private TradeType type;

    @Column
    @Enumerated(EnumType.STRING)
    private TradeState state;

    @MapKey(name = "index")
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "trade_stock",
        joinColumns = { @JoinColumn(name = "id", referencedColumnName = "id") },
        inverseJoinColumns = { @JoinColumn(name = "stock_index", referencedColumnName = "index") })
    private Map<String, Stock> stocks = new HashMap<>();

}
但现在它留下了joins交易(但它们是懒惰的)和所有交易的集合(它们也是懒惰的)。但是,生成的
Stock::toString
方法抛出
LazyInitializationException
异常。

相关答案:

您基本上需要设置@Fetch(FetchMode.JOIN),因为Fetch=FetchType.EAGER只是指定将加载关系,而不是如何加载

另外,可能有助于解决您的问题的是 @BatchSize注释,指定在请求第一个惰性集合时将加载多少个惰性集合。例如,如果内存中有100笔交易(股票未初始化)@BatchSize(size=50)将确保只使用2个查询。有效地将n+1改变为(n+1)/50。

关于插入,您可能需要设置 hibernate.jdbc.batch\u size属性,并将订单插入和订单更新设置为true。

但是,生成的Stock::toString方法抛出 LazyInitializationException异常

好的,由此我假设您已经基于类的所有字段使用Lombok或IDE生成器生成了
toString()
(很可能是
equals()
hashcode()
方法)

在JPA环境中,不要以这种方式重写
equals()
hashcode()
toString()
,因为它有可能(a)触发您看到的异常,如果toString()访问事务外部的延迟加载集合,以及(b)在事务中使用时触发加载超大容量的数据。编写一个不涉及关联的合理to字符串,并使用(a)一些业务键(如果有的话)实现equals()和hashcode(),(b)ID(如果这种方法可能出现问题,请注意),或者(c)根本不重写它们


因此,首先,删除这些生成的方法,看看这是否会有所改进。

关于插入,我确实注意到JPA中经常忽略的一件事。我不知道您使用的是什么数据库,但您必须小心使用

@GeneratedValue(strategy = GenerationType.AUTO)
对于MySQL,我认为所有JPA实现都映射到一个自动递增的字段,一旦您知道JPA是如何工作的,这就意味着两个方面

  • 每个insert将由两个查询组成。首先是insert,然后是select查询(MySQL的LAST\u insert\u ID)以获取生成的主键
  • 它还可以防止任何批处理查询优化,因为每个查询都需要在自己的insert中完成
如果插入大量对象,并且希望获得良好的性能,我建议使用表生成序列,让JPA在大块中预先分配ID,这也允许SQL驱动程序批量插入(…)值(…)优化


另一个建议(不是每个人都同意我的建议).就我个人而言,我从不使用多个,我总是将它分解为一个多个多个多个,并将联接表作为一个真实的实体。我喜欢它提供的对级联和获取的附加控制,并且可以避免一些存在于双向关系中的多个陷阱。

请发布整个Stock类。SQL表示选择s.name,s.price fromstock s属性名在哪里?很抱歉,它是索引。我已经编辑了这个问题。你所说的“日志”是什么意思?我删除了
Hibernate:
前缀,例如
作为索引1\u 9\u 0
部分。所以n+1来自@manytomy关系
@ManyToMany(cascade = CascadeType.ALL, mappedBy = "stocks") //lazy by default
Set<Trade> trades = new HashSet<>();
@GeneratedValue(strategy = GenerationType.AUTO)