Java N+;ID为字符串时为1(JpaRepository)
我有一个字符串id为的实体: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
@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中完成
另一个建议(不是每个人都同意我的建议).就我个人而言,我从不使用多个,我总是将它分解为一个多个多个多个,并将联接表作为一个真实的实体。我喜欢它提供的对级联和获取的附加控制,并且可以避免一些存在于双向关系中的多个陷阱。请发布整个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)